diff --git a/docs/api_changes.md b/docs/api_changes.md index 29d6a92..ac68f99 100644 --- a/docs/api_changes.md +++ b/docs/api_changes.md @@ -3,9 +3,9 @@ This document keeps a record of notable changes to Moonraker's Web API. ### July 18th 2023 - Moonraker API Version 1.3.0 -- Added [Spoolman](web_api.md#spoolman-apis) APIs. -- Added [Rollback](web_api.md#rollback-to-the-previous-version) API to - the `update_manager` +- Added [Spoolman](./external_api/integrations.md#spoolman) APIs. +- Added [Rollback](./external_api/update_manager.md#rollback-to-the-previous-version) + API to the `update_manager` - The `update_manager` status response has new fields for items of the `git_repo` and `web` types: - `recovery_url`: Url of the repo a "hard" recovery will fetch from @@ -25,12 +25,13 @@ This document keeps a record of notable changes to Moonraker's Web API. - `GET /server/sensors/sensor` - `GET /server/sensors/measurements` - See [web_api.md](web_api.md) for details on these new endpoints. + See [the documentation](./external_api/devices.md#sensor-endpoints) + for details on these new endpoints. - A `sensors:sensor_update` notification has been added. When at least one monitored sensor is reporting a changed value Moonraker will broadcast this notification. - - See [web_api.md](web_api.md) for details on this new notification. + See [the documentation](./external_api/jsonrpc_notifications.md#sensor-events) + for details on this new notification. ### February 17 2023 - Moonraker API Version 1.2.1 @@ -50,14 +51,14 @@ This document keeps a record of notable changes to Moonraker's Web API. recommended to use `server.connection.identify` method to identify your client. This method returns a `connection_id` which is the websocket's unique id. See - [the documentation](web_api.md#identify-connection) for details. + [the documentation](./external_api/server.md#identify-connection) for details. ### May 8th 2021 - The `file_manager` has been refactored to support system file file events through `inotify`. Only mutable `roots` are monitored, - (currently `gcodes` and `config`). Subfolders within these + (currently `gcodes` and `config`). Sub-folders within these these roots are also monitored, however hidden folders are not. - The following changes API changes have been made to acccommodate + The following changes API changes have been made to accommodate this functionality: - The `notify_filelist_changed` actions have changed. The new actions are as follows: @@ -78,7 +79,7 @@ This document keeps a record of notable changes to Moonraker's Web API. processed. Notifications are synced with requests so that the request should always return before the notification is sent. - Thumbnails are now stored in the `.thumbs` directory to prevent - changes to thumbnails from emitting filelist notications. This + changes to thumbnails from emitting filelist notifications. This change will be reflected in the metadata's `relative_path` field, so clients that use this field should not need to take additional action. Note that existing thumbnails will remain in the `thumbs` @@ -103,7 +104,7 @@ This document keeps a record of notable changes to Moonraker's Web API. ### January 31st 2021 - The `GET /server/temperature_store` endpoint now only returns fields - that each sensor reports. For example, if a particuarly temperature + that each sensor reports. For example, if a particularly temperature sensor does not report "target" or "power", then the corresponding fields will not be reported for that sensor in response to the `temperature_store` request. @@ -153,7 +154,8 @@ This document keeps a record of notable changes to Moonraker's Web API. core API: - `POST /machine/services/restart` -See [web_api.md](web_api.md) for details on these new endpoints. +See [the documentation](./external_api/update_manager.md) for details +on these new endpoints. ### November 23rd 2020 - Moonraker now serves Klipper's "docs" directory. This can be access @@ -187,7 +189,7 @@ See [web_api.md](web_api.md) for details on these new endpoints. "params":{"dev_name":null}}`\ Toggles device off. Returns the current status of the device. - The `notify_power_changed` notification now includes an object - containing device info, matching that which would be recieved + containing device info, matching that which would be received from a single item in `/machine/power/devices`. ### November 12th 2020 @@ -207,13 +209,13 @@ See [web_api.md](web_api.md) for details on these new endpoints. - All HTTP API request may now include arguments in either the query string or in the request's body. - Subscriptions are now managed on a per connection basis. Each - connection will only recieve updates for objects in which they + connection will only receive updates for objects in which they are currently subscribed. If an "empty" request is sent, the subscription will be cancelled. - The `POST /printer/object/subscribe` now requires a `connection_id` argument. This is used to identify which connection's associated subscription should be updated. - Currenlty subscriptions are only supported over the a + Currently subscriptions are only supported over the a websocket connection, one may use the id received from `server.websocket.id`. - The `notify_klippy_ready` websocket notification has been @@ -298,8 +300,8 @@ See [web_api.md](web_api.md) for details on these new endpoints. - `POST /machine/gpio_power/off` : `machine.gpio_power.off` ### September 1st 2020 -- A new notification has been added: `notify_metdata_update`. This - notification is sent when Moonraker parses metdata from a new upload. +- A new notification has been added: `notify_metadata_update`. This + notification is sent when Moonraker parses metadata from a new upload. Note that the upload must be made via the API, files manually (using SAMBA, SCP, etc) do not trigger a notification. The notification is sent in the following format: @@ -399,7 +401,7 @@ See [web_api.md](web_api.md) for details on these new endpoints. state for all currently subscribed objects (in the same format as a `query`). This data can be used to initialize all local state after the request completes. -- Subscriptions are now pushed as "diffs". Clients will only recieve updates +- Subscriptions are now pushed as "diffs". Clients will only receive updates for subscribed items when that data changes. This requires that clients initialize their local state with the data returned from the subscription request. diff --git a/docs/changelog.md b/docs/changelog.md index 3db212c..0d7536f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -174,7 +174,8 @@ The format is based on [Keep a Changelog]. - **update_manager**: The `install_script` option for the `git_repo` has been deprecated, new configurations should use the `system_dependencies` option. - **update_manager**: APIs that return status report additional fields. - See the [API Documentation](./web_api.md#get-update-status) for details. + See the [API Documentation](./external_api/update_manager.md#get-update-status) + for details. - **proc_stats**: Improved performance of Raspberry Pi CPU throttle detection. - **power**: Bound services are now processed during initialization when `initial_state` is configured. @@ -193,7 +194,7 @@ The format is based on [Keep a Changelog]. !!! Note This is the first tagged release since a changelog was introduced. The list - below contains notable changes introduced beginning in Feburary 2023. Prior + below contains notable changes introduced beginning in February 2023. Prior notable changes were kept in [user_changes.md] and [api_changes.md]. ### Added @@ -202,10 +203,10 @@ The format is based on [Keep a Changelog]. - Added pyproject.toml with support for builds through [pdm](https://pdm.fming.dev/latest/). - **sensor**: New component for generic sensor configuration. - [Configuration Docs](configuration.md#sensor) - - [API Docs](web_api.md#sensor-apis) - - [Websocket Notification Docs](web_api.md#sensor-events) -- **file_manager**: Added new [scan metadata](web_api.md#scan-gcode-metadata) endpoint. -- **file_manager**: Added new [thumbnails](web_api.md#get-gcode-thumbnails) endpoint. + - [API Docs](./external_api/devices.md#sensor-endpoints) + - [Websocket Notification Docs](./external_api/jsonrpc_notifications.md#sensor-events) +- **file_manager**: Added new [scan metadata](./external_api/file_manager.md#scan-gcode-metadata) endpoint. +- **file_manager**: Added new [thumbnails](./external_api/file_manager.md#get-gcode-thumbnail-details) endpoint. - **file_manager**: Added [file_system_observer](configuration.md#file_manager) configuration option. - **file_manager**: Added [enable_observer_warnings](configuration.md#file_manager) @@ -217,9 +218,9 @@ The format is based on [Keep a Changelog]. - **machine**: Added service detection to the `supervisord_cli` provider. - **machine**: Added `octoeverywhere` to the list of default allowed service. - **power**: Added support for "Hue" device groups. -- **websockets**: Added support for [direct bridge](web_api.md#bridge-websocket) +- **websockets**: Added support for [direct bridge](./external_api/introduction.md#bridge-websocket) connections. -- **update_manager**: Added new [refresh](web_api.md#refresh-update-status) endpoint. +- **update_manager**: Added new [refresh](./external_api/update_manager.md#refresh-update-status) endpoint. - **update_manager**: Added support for pinned pip upgrades. - **websockets**: Added support for post connection authentication over the websocket. - **scripts**: Added database backup and restore scripts. diff --git a/docs/components.md b/docs/components.md index 771eec7..f3c0fa6 100644 --- a/docs/components.md +++ b/docs/components.md @@ -47,7 +47,7 @@ class Example: return await klippy_apis.query_objects({'print_stats': None}) async def _handle_example_request(self, web_request): - web_request.get_int("required_reqest_param") + web_request.get_int("required_request_param") web_request.get_float("optional_request_param", None) state = await self.request_some_klippy_state() return {"example_return_value": state} @@ -126,7 +126,7 @@ object. #### *ConfigHelper.get_prefix_sections(prefix)* Returns a list section names in the configuration that start with `prefix`. -These strings can be used to retreve ConfigHelpers via +These strings can be used to retrieve ConfigHelpers via [get_section()](#confighelpergetsectionsection_name). ### The Server Object @@ -219,15 +219,15 @@ callback. Event names should be in the form of #### *Server.register_notification(event_name, notify_name=None)* Registers a websocket notification to be pushed when `event_name` -is emitted. By default JSON-RPC notifcation sent will be in the form of +is emitted. By default JSON-RPC notification sent will be in the form of `notify_{event_description}`. For example, when the server sends the `server:klippy_connected` event, the JSON_RPC notification will be `notify_klippy_connected`. If a `notify_name` is provided it will override the `{event_description}` -extracted from the `event_name`. For example, if the `notify_name="kconnect` -were specfied when registering the `server:klippy_connected` event, the -websocket would emit a `notify_kconnect` notification. +extracted from the `event_name`. For example, if the `notify_name="k_connected"` +were specified when registering the `server:klippy_connected` event, the +websocket would emit a `notify_k_connected` notification. #### *Server.get_host_info()* @@ -242,7 +242,7 @@ Klippy. If Klippy has never connected this will be an empty dict. ### The WebRequest Object All callbacks registered with the -[register_endpoint()](#serverregister_endpointuri-request_methods-callback-protocolhttp-websocket-wrap_resulttrue) +[register_endpoint()](#serverregister_endpointuri-request_methods-callback-transportshttp-websocket-mqtt-wrap_resulttrue) method are passed a WebRequest object when they are executed. This object contains information about the request including its endpoint name and arguments parsed from the request. @@ -253,7 +253,7 @@ Returns the URI registered with this request, ie: `/server/example`. #### *WebRequest.get_action()* -Returns the request action, which is synonomous with its HTTP request +Returns the request action, which is synonymous with its HTTP request method. Will be either `GET`, `POST`, or `DELETE`. This is useful if your endpoint was registered with multiple request methods and needs to handle each differently. @@ -326,7 +326,7 @@ established. Attempts to publish a topic to the Broker. The `payload` may be a bool, int, float, string, or json encodable (Dict or List). If omitted then an empty -payload is sent. The `qos` may be an integer from 0 to 2. If not specifed +payload is sent. The `qos` may be an integer from 0 to 2. If not specified then the QOS level will use the configured default. If `retain` is set to `True` then the retain flag for the payload will be set. @@ -340,7 +340,7 @@ Publishes the supplied `topic` with the arguments specified by `payload`, `qos`, and `retain`, then subscribes to the `response_topic`. The payload delivered by the response topic is returned. Note that this method is a coroutine, it must always be awaited. The call will block until the -entire process has completed unless a `timeout` (in seconds) is specifed. +entire process has completed unless a `timeout` (in seconds) is specified. The `timeout` is applied to both the attempt to publish and the pending response, so the maximum waiting time would be approximately 2*timeout. @@ -351,7 +351,7 @@ response, so the maximum waiting time would be approximately 2*timeout. #### *MQTTClient.subscribe_topic(topic, callback, qos=None)* -Subscibes to the supplied `topic` with the specified `qos`. If `qos` is not +Subscribes to the supplied `topic` with the specified `qos`. If `qos` is not supplied the configured default will be used. The `callback` should be a callable that accepts a `payload` argument of a `bytes` type. The callable may be a coroutine. The callback will be run each time the subscribed topic @@ -359,7 +359,7 @@ is published by another client. Returns a `SubscriptionHandle` that may be used to unsubscribe the topic. -#### *MQTTClinet.unsubscribe(hdl)* +#### *MQTTClient.unsubscribe(hdl)* Unsubscribes the callback associated with `hdl`. If no outstanding callbacks exist for the topic then the topic is unsubscribed from the broker. diff --git a/docs/configuration.md b/docs/configuration.md index 96105a3..6196cbd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -271,7 +271,7 @@ gcode: !!! Note This section no long has configuration options. Previously the - `database_path` option was used to determine the locatation of + `database_path` option was used to determine the location of the database folder, it is now determined by the `data path` configured on the command line. @@ -559,7 +559,7 @@ user_filter: (&(objectClass=user)(cn=USERNAME)) ### `[octoprint_compat]` Enables partial support of OctoPrint API is implemented with the purpose of allowing uploading of sliced prints to a moonraker instance. -Currently we support Slic3r derivatives and Cura with Cura-OctoPrint. +Currently PrusaSlicer derivatives and Cura with Cura-OctoPrint. ```ini # moonraker.conf @@ -718,7 +718,7 @@ bound_services: # the Moonraker service can not be bound to a power device. Note that # service names are case sensitive. # -# When the "initial_state" option is explcitly configured bound services +# When the "initial_state" option is explicitly configured bound services # will be synced with the current state. For example, if the initial_state # is "off", all bound services will be stopped after device initialization. # @@ -1386,7 +1386,7 @@ port: ##### Example ```ini -# moonraker.confg +# moonraker.config # Example for controlling a device connected to a Raspberry Pi 3B+. # Location 1-1 Port 2 controls power for all 4 exposed ports. @@ -1432,7 +1432,7 @@ a Jinja2 context with the following fields: - `async_sleep`: An alias for the `asyncio.sleep` method. This may be used to add delays if necessary. - `log_debug`: An alias for `logging.debug`. This can be used to log messages - and data to `moonraker.log` to aid in debugging an implmentation. Note that + and data to `moonraker.log` to aid in debugging an implementation. Note that verbose logging must be [enabled](installation.md#debug-options-for-developers) for these messages to appear in the log. @@ -1531,7 +1531,7 @@ __`http_response.final_url`__ > A property that returns "effective" url of the request after all redirects. -__`http_reponse.headers`__ +__`http_response.headers`__ > A property that returns the response headers as a python `Dict`. @@ -1688,7 +1688,7 @@ gcode: # Override SDCARD_PRINT_FILE [gcode_macro SDCARD_PRINT_FILE] -rename_existing: SDCPF +rename_existing: DO_PRINT gcode: # Step 1: Call the remote method to turn on the power device POWER_ON_HEATERS @@ -1696,7 +1696,7 @@ gcode: # pauses for 4 seconds. It may be necessary to tweak this value. G4 P4000 # Step 3: Call the renamed command to start the print - SDCPF {rawparams} + DO_PRINT {rawparams} ``` @@ -1712,7 +1712,7 @@ gcode: Another exotic use case is the addition of a "conditional" peripheral, such as an MMU device. The user may not wish to power on this device for every print, and instead power it on from within the "Start G-GCode" -conditionally. Additionaly we do not want this device to be turned on/off +conditionally. Additionally we do not want this device to be turned on/off unintentionally during a print. The `set_device_power` remote method takes an optional `force` argument that can be used to accommodate this scenario. @@ -2064,7 +2064,7 @@ info_tags: pinned_commit: # A git commit hash to "pin" updates to. When specified Moonraker will not # update the repo beyond the pinned commit. If the repo is already beyond -# the specified commit, or if the commit is not in the repo, futher updates +# the specified commit, or if the commit is not in the repo, further updates # are disabled until the pinned_commit is changed. It is recommended to # specify the complete hash, however abbreviated hashes with a minimum of # 8 characters are accepted. The "pinned_commit" overrides the update @@ -2078,7 +2078,7 @@ pinned_commit: services Moonraker is allowed to manage and how to add additional services. Also not that systemd services are case sensitive. The `extension_name` - in the section header and the value provided in the `managed_servies` + in the section header and the value provided in the `managed_services` option must match the case of the systemd unit file. #### Zip Application Configuration @@ -2147,12 +2147,12 @@ refresh_interval: # This overrides the refresh_interval set in the primary [update_manager] # section. virtualenv: -# Path to the virtual enviromnent containing the python application. +# Path to the virtual environment containing the python application. project_name: # Name of the python project as listed in the python package index. If # the packaged is sourced from GitHub, this will be the name of the project # specified in the build. Optional "extras" may be added, see the tip -# follwing this example for details. The default is the name specified by the +# following this example for details. The default is the name specified by the # configuration section. primary_branch: # For packages sourced from GitHub, this option may be used to specify the @@ -2242,7 +2242,7 @@ When a `zip` or `git_repo` application depends on OS packages it is possible to specify them in a file that Moonraker can refer to. During an update Moonraker will use this file to install new dependencies if they are detected. -Below is an example of Moonraker's system dependcies file, located at +Below is an example of Moonraker's system dependencies file, located at in the repository at [scripts/system-dependencies.json](https://github.com/Arksine/moonraker/blob/master/scripts/system-dependencies.json): @@ -2278,8 +2278,8 @@ Pi OS, Ubuntu, and likely other Debian derived distributions. Enables an MQTT Client. When configured most of Moonraker's APIs are available by publishing JSON-RPC requests to `{instance_name}/moonraker/api/request`. Responses will be published to `{instance_name}/moonraker/api/response`. See -the [API Documentation](web_api.md#json-rpc-api-overview) for details on -on JSON-RPC. +the [API Documentation](./external_api/introduction.md#json-rpc-api-overview) +for details on on JSON-RPC. It is also possible for other components within Moonraker to use MQTT to publish and subscribe to topics. @@ -2294,7 +2294,7 @@ address: port: # Port the Broker is listening on. Default is 1883. client_id: -# A string client identifer sent by the client to the broker after +# A string client identifier sent by the client to the broker after # connecting. The default is a randomly assigned client id. enable_tls: False # Enables SSL/TLS connections when set to true. Note that if a user intends @@ -2450,7 +2450,7 @@ serial: # 0.13 Build 2108250 or later. # Required when type: serial initial_preset: -# Initial preset ID (favourite) to use. If not specified initial_colors +# Initial preset ID (favorite) to use. If not specified initial_colors # will be used instead. initial_red: initial_green: @@ -2555,7 +2555,7 @@ detect and use Moonraker instances. [zeroconf] mdns_hostname: -# The hostname used when registering the multicast DNS serivce. +# The hostname used when registering the multicast DNS service. # The instance will be available at: # http://{mdns_hostname}.local:{port}/ # The default is the operating system's configured hostname. @@ -2598,7 +2598,7 @@ debounce_period: .05 minimum_event_time: 0 # The minimum event duration (in seconds) required to trigger a response. # This can be used as a secondary debounce procedure. The default is 0 -# seconds (no minumum duration). +# seconds (no minimum duration). on_press: on_release: # Jinja2 templates to be executed when a button event is detected. At least one @@ -2613,7 +2613,8 @@ with two methods that may be called in addition to Jinja2's default filters adn methods: - `call_method`: Calls an internal API method. See the - [API documentation](web_api.md#jinja2-template-api-calls) for details. + [API documentation](./external_api/introduction.md#jinja2-template-api-calls) + for details. - `send_notification`: Emits a websocket notification. This is useful if you wish to use buttons to notify attached clients of some action. This method takes an optional argument that may contain any JSON object. @@ -2826,7 +2827,7 @@ body: "Your printer status has changed to {event_name}" # event_message: An additional message passed to the notification when # triggered. This is commonly used when the notification # is received from Klippy using a gcode_macro. -# The default is a body containining the "name" of the notification as entered +# The default is a body containing the "name" of the notification as entered # in the section header. body_format: # The formatting to use for the body, can be `text`, `html` and `markdown`. @@ -2838,8 +2839,8 @@ title: attach: # One or more items to attach to the notification. This may be a path to a # local file or a url (such as a webcam snapshot). Multiple attachments must be -# separated by a newline. This option accepts Jinja2 templates, the tempalte -# will recieve the same context as the "body" and "title" options. The default +# separated by a newline. This option accepts Jinja2 templates, the template +# will receive the same context as the "body" and "title" options. The default # is no attachment will be sent with the notification. # # Note: Attachments are not available for all notification services, you can @@ -2948,7 +2949,7 @@ ambient_sensor: - Klipper's version, connection state, and date pulled - Moonraker's version - - Currenly connected front-end and version + - Currently connected front-end and version - Current python version - Linux distribution and version - Network connection type (wifi or ethernet) @@ -3003,7 +3004,7 @@ parameter_{parameter_name}: # be a valid measurement reported by the sensor. The value should be # a newline separated list of key-value pairs describing the # the measurement. Currently the only key used is "units". For -# example, the configuration for a parameter may look like the follwing: +# example, the configuration for a parameter may look like the following: # # parameter_energy: # units=kWh @@ -3061,7 +3062,7 @@ history_field_{field_name}: - `basic`: This strategy should be used if the value should be stored in history directly as it is received. Simply put, the last value - received before a job completes wiill the the value stored in the job + received before a job completes will be the value stored in the job history. - `accumulate`: When a job starts, the tracked value initialized to 0 or the last received measurement. New measurements will be added to the @@ -3181,7 +3182,7 @@ Tasmota Example: !!! Note It may be necessary to set Tasmota's Telemetry Period to a low value - to acheive a decent response. This can be done in the with the + to achieve a decent response. This can be done in the with the `TelePeriod` command via the console. For example, the command to set the telemetry period to 10 seconds is: @@ -3223,7 +3224,7 @@ history_field_average_current: units=A report_total=false report_maximum=true -# Mulitple history fields may track the same sensor parameter: +# Multiple history fields may track the same sensor parameter: history_field_max_current: parameter=current desc=Maximum current draw diff --git a/docs/contributing.md b/docs/contributing.md index 8bece89..68b7b33 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -78,7 +78,7 @@ Signed-off-by: Eric Callahan ``` By signing off on commits, you acknowledge that you agree to the -[developer certificate of origin](../developer-certificate-of-origin) +[developer certificate of origin](https://developercertificate.org/) shown below. As mentioned above, your signature must contain your real name and a current email address. diff --git a/docs/dev_changelog.md b/docs/dev_changelog.md index 24586e7..be70488 100644 --- a/docs/dev_changelog.md +++ b/docs/dev_changelog.md @@ -70,7 +70,7 @@ - Added thumbnail extraction from SuperSlicer and PrusaSlicer gcode files - For status requests, `virtual_sdcard.current_file` has been renamed to `virtual_sdcard.filename` -- Clients should not send `M112` via gcode to execute an emegency shutdown. +- Clients should not send `M112` via gcode to execute an emergency shutdown. They should instead use the new API which exposes this functionality. - New APIs: - `POST /printer/emergency_stop` - `post_printer_emergency_stop` @@ -108,7 +108,7 @@ remote_api.py module that handles server configuration. - webhooks.py has been changed to handle communications with the server - klippy.py has been changed to pass itself to webhooks - - file_manager.py has been changed to specifiy the correct status code + - file_manager.py has been changed to specify the correct status code when an error is generated attempting to upload or delete a file - The nginx configuration will need the following additional section: ``` @@ -149,8 +149,8 @@ This allows the client limited access to Klippy in the event of a startup error, assuming the config file was successfully parsed and the `remote_api` configuration section is valid. Note that when the server is - initally launched not all endpoints will be available. The following - endponts are guaranteed when the server is launched: + initially launched not all endpoints will be available. The following + endpoints are guaranteed when the server is launched: - `/websocket` - `/printer/info` - `/printer/restart` @@ -159,7 +159,7 @@ - `/printer/gcode` - `/access/api_key` - `/access/oneshot_token` - The following startup sequence is recommened for clients which make use of + The following startup sequence is recommended for clients which make use of the websocket: - Attempt to connect to `/websocket` until successful - Once connected, query `/printer/info` for the ready status. If not ready @@ -199,7 +199,7 @@ - Fix bug in CORS where DELETE requests raised an exception - Disable the server when running Klippy in batch mode - The the `/printer/cancel`, `/printer/pause` and `/printer/resume` gcodes - are now registed by the pause_resume module. This results in the following + are now registered by the pause_resume module. This results in the following changes: - The `cancel_gcode`, `pause_gcode`, and `resume_gcode` options have been removed from the [web_server] section. @@ -275,17 +275,17 @@ websocket data from a Blob into a String. - The endpoint for querying endstops has changed from `GET /printer/extras/endstops` to `GET /printer/endstops` -- Serveral API changes have been made to accomodate the addition of webhooks: +- Several API changes have been made to accommodate the addition of webhooks: - `GET /printer/klippy_info` is now `GET /printer/info`. This endpoint no longer returns host information, as that can be retrieved direct via the `location` object in javascript. Instead it returns CPU information. - - `GET /printer/objects` is no longer used to accomodate multiple request + - `GET /printer/objects` is no longer used to accommodate multiple request types by modifying the "Accept" headers. Each request has been broken down in their their own endpoints: - `GET /printer/objects` returns all available printer objects that may be queried - `GET /printer/status?gcode=gcode_position,speed&toolhead` returns the - status of the printer objects and attribtues + status of the printer objects and attributes - `GET /printer/subscriptions` returns all printer objects that are current being subscribed to along with their poll times - `POST /printer/subscriptions?gcode&toolhead` requests that the printer @@ -297,7 +297,7 @@ - `POST /printer/gcode/` is now `POST /printer/gcode?script=` - `POST printer/print/start/` is now `POST /printer/print/start?filename=` - - The websocket API also required changes to accomodate dynamically registered + - The websocket API also required changes to accommodate dynamically registered endpoints. Each method name is now generated from its comparable HTTP request. The new method names are listed below: | new method | old method | @@ -317,7 +317,7 @@ | post_printer_firmware_restart | firmware_restart | | get_printer_endstops | get_endstops | - As with the http API, a change was necessary to the way arguments are send - along with the request. Webocket requests should now send "keyword + along with the request. Websocket requests should now send "keyword arguments" rather than "variable arguments". The test client has been updated to reflect these changes, see main.js and json-rpc.js, specifically the new method `call_method_with_kwargs`. For status requests this simply diff --git a/docs/dictionaries/moonraker.txt b/docs/dictionaries/moonraker.txt new file mode 100644 index 0000000..c2d0e27 --- /dev/null +++ b/docs/dictionaries/moonraker.txt @@ -0,0 +1,168 @@ +adxl +apikey +apirequest +apiresponse +argone +argtwo +arksine +armv +asvc +asyncio +atmega +Benchy +bigtreetech +binutils +calicat +camerastreamer +canbus +cansocket +cmnd +comms +compat +configfile +configheler +confighelper +configurator +Connor +crowsnest +Cura +datapath +datasheet +dbus +devel +distro +distros +Domoticz +Donkie +eeprom +endstop +endstops +EPCOS +eventtime +extr +feedrate +filelist +fileobj +firemon +Fluidd +fourcc +fromjson +frontends +ftdi +gcode +gcodes +getboolean +getfloat +getfqdn +getint +getitem +getsection +gpio +gpiochip +hexeditor +highspeed +hlsstream +homeassistant +homeseer +httpx +inotify +ipstream +jmuxer +journalctl +Kasa +katapult +klipper +klippy +klippysocket +kwargs +libcamera +libgpiod +libnacl +lmdb +logind +Loxone +loxonev +mediamtx +metascan +microsteps +mjpeg +mjpegstreamer +MJPG +Mobileraker +moko +moonagent +moontest +msgspec +Muxer +mypassword +mypy +neopixel +neopixels +Obico +octoeverywhere +octoprint +oneshot +packagekit +packagename +paneldue +piezo +PKGLIST +poweroff +Prusa +pstats +pullup +pyapp +Pycurl +pyproject +pyserial +raspberrypi +ratos +rawparams +readwrite +sdcard +sdcardinfo +sdist +Semitec +setitem +sgbrg +simplyprint +smartplug +smartthings +spoolman +SSDP +stallguard +subdir +supervisord +sysfs +sysinfo +tasmota +telegam +testcam +testdir +testuser +TESTZ +tojson +tplink +trapq +uart +ububctl +uhubctl +unicam +unixsocket +userpass +uvcvideo +uvloop +vcgencmd +venv +virt +virtualenv +volted +webcamd +webrtc +websockets +wlan +WLED +worktrees +yuyv +zeroconf +Zigbee diff --git a/docs/doc-requirements.txt b/docs/doc-requirements.txt index b653d97..c5f70dc 100644 --- a/docs/doc-requirements.txt +++ b/docs/doc-requirements.txt @@ -1,3 +1,4 @@ -mkdocs-material==9.5.4 -compact_tables@git+https://github.com/Arksine/markdown-compact-tables@v1.0.0 -./pymdown-extras \ No newline at end of file +mkdocs-material==9.5.50 +pymdown-extensions~=10.14.1 +compact_tables@git+https://github.com/Arksine/markdown-compact-tables@v1.1.0 +./docs/pymdown-extras \ No newline at end of file diff --git a/docs/example-home-assistant-extended.yaml b/docs/example-home-assistant-extended.yaml index 3769c01..3adf6d9 100644 --- a/docs/example-home-assistant-extended.yaml +++ b/docs/example-home-assistant-extended.yaml @@ -25,7 +25,7 @@ sensor: # and if Home Assistant is not a trusted client headers: x-api-key: 123456789abcdefghijklmno - + - platform: template sensors: swx1_state: diff --git a/docs/example-home-assistant.yaml b/docs/example-home-assistant.yaml index 90e334a..2c11cef 100644 --- a/docs/example-home-assistant.yaml +++ b/docs/example-home-assistant.yaml @@ -14,12 +14,12 @@ sensor: - display_status - virtual_sdcard value_template: >- - {{ 'OK' if ('result' in value_json) else None }} + {{ 'OK' if ('result' in value_json) else None }} # Adding an API key is only necessary while using the [authorization] component # and if Home Assistant is not a trusted client headers: x-api-key: 123456789abcdefghijklmno - + - platform: template sensors: diff --git a/docs/external_api/announcements.md b/docs/external_api/announcements.md new file mode 100644 index 0000000..dd24732 --- /dev/null +++ b/docs/external_api/announcements.md @@ -0,0 +1,692 @@ +# Announcements + +The following endpoints are available to manage announcements. +Moonraker announcements are effectively push notifications that +can be used to notify users of important information related the +development and status of software in the Klipper ecosystem. +See [the appendix](#appendix) for details on how announcements +work and recommendations for your implementation. + +## List announcements + +Retrieves a list of current announcements. + +```{.http .apirequest title="HTTP Request"} +GET /server/announcements/list?include_dismissed=false +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.announcements.list", + "params": { + "include_dismissed": false + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------------------- | :--: | ------- | ----------------------------------- | +| `include_dismissed` | bool | true | When set to false dismissed entries | +| | | | will be excluded from the returned |^ +| | | | list of current announcements. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "entries": [ + { + "entry_id": "arksine/moonlight/issue/3", + "url": "https://github.com/Arksine/moonlight/issues/3", + "title": "Test announcement 3", + "description": "Test Description [with a link](https://moonraker.readthedocs.io).", + "priority": "normal", + "date": 1647459219, + "dismissed": false, + "date_dismissed": null, + "dismiss_wake": null, + "source": "moonlight", + "feed": "moonlight" + }, + { + "entry_id": "arksine/moonlight/issue/2", + "url": "https://github.com/Arksine/moonlight/issues/2", + "title": "Announcement Test Two", + "description": "This is a high priority announcement. This line is included in the description.", + "priority": "high", + "date": 1646855579, + "dismissed": false, + "date_dismissed": null, + "dismiss_wake": null, + "source": "moonlight", + "feed": "moonlight" + }, + { + "entry_id": "arksine/moonlight/issue/1", + "url": "https://github.com/Arksine/moonlight/issues/1", + "title": "Announcement Test One", + "description": "This is the description. Anything here should appear in the announcement, up to 512 characters.", + "priority": "normal", + "date": 1646854678, + "dismissed": false, + "date_dismissed": null, + "dismiss_wake": null, + "source": "moonlight", + "feed": "moonlight" + }, + { + "entry_id": "arksine/moonraker/issue/349", + "url": "https://github.com/Arksine/moonraker/issues/349", + "title": "PolicyKit warnings; unable to manage services, restart system, or update packages", + "description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.", + "priority": "normal", + "date": 1643392406, + "dismissed": false, + "dismiss_wake": null, + "source": "moonlight", + "feed": "Moonraker" + } + ], + "feeds": [ + "moonraker", + "klipper", + "moonlight" + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------- | :------: | ---------------------------------------------------------- | +| `entries` | [object] | An array of [announcement entry](#announcement-entry-spec) | +| | | objects. The array is sorted by date in descending order |^ +| | | (newest to oldest). |^ +| `feeds` | [string] | An array of RSS announcement feeds Moonraker is | +| | | currently subscribed to. |^ +{ #list-announcements-spec } + +| Field | Type | Description | +| ---------------- | :-----------: | ------------------------------------------------------------ | +| `entry_id` | string | A unique identifier for the announcement entry. | +| `url` | string | A url associated with the announcement. This will link to | +| | | a GitHub issue for announcements sourced from `moonlight`. |^ +| `title` | string | The title of the announcement. | +| `description` | string | A brief description of the announcement. For announcement's | +| | | sourced from `moonlight` this will be the first paragraph |^ +| | | of the associated GitHub issue. Moonlight will truncate |^ +| | | truncate descriptions over 512 characters. |^ +| `priority` | string | The [priority](#announcement-priority-desc) of the | +| | | announcement. |^ +| `date` | int \| float | The announcement creation date in unix time. | +| `dismissed` | bool | Set to `true` if the announcement has been dismissed. | +| `date_dismissed` | float \| null | The date, in unix time, the announcement was last dismissed. | +| | | Will be `null` if the announcement has not been dismissed. |^ +| `dismiss_wake` | float \| null | The amount of time remaining, in seconds, before the entry's | +| | | `dismissed` flag reverts to `true`. Will be `null` if the |^ +| | | announcement has not been dismissed or if the announcement |^ +| | | was dismissed indefinitely. |^ +| `source` | string | The [source](#announcement-source-desc) of the announcement. | +| `feed` | string | The registered RSS feed the announcement belongs to. For | +| | | announcements sourced internally this will typically be |^ +| | | the name of the component that generated the announcement. |^ +{ #announcement-entry-spec } Announcement Entry + +| Priority | Description | +| -------- | ----------------------------------------------------------------- | +| `normal` | Standard priority. Front-end devs should use their own discretion | +| | on how to present announcements with normal priority to users. |^ +| `high` | High priority. It is recommended that front-ends alert the user | +| | when a high priority announcement is received. |^ +{ #announcement-priority-desc } Announcement Priority + +| Source | Description | +| ----------- | -------------------------------------------------------------------- | +| `moonlight` | The announcement was received from the | +| | [moonlight](https://github.com/Arksine/moonlight) GitHub repo. |^ +| | Announcements received from local XML files when the `announcements` |^ +| | module is configured in `dev_mode` will also report the source as |^ +| | `moonlight`. |^ +| `internal` | The announcement was generated by Moonraker itself. This could | +| | be a component, such as `simplyprint`. |^ +{ #announcement-source-desc } Announcement Source + +/// + +## Update announcements +Requests that Moonraker check for announcement updates. This is generally +not required in production, as Moonraker will automatically check for +updates every 30 minutes. However, during development this endpoint is +useful to force an update when it is necessary to perform integration +tests. + +```{.http .apirequest title="HTTP Request"} +POST /server/announcements/update +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.announcements.update", + "id": 4654 +} +``` + +Returns: + +The current list of announcements, in descending order (newest to oldest) +sorted by `date`, and a `modified` field that contains a boolean value +indicating if the update resulted in a change: + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "entries": [ + { + "entry_id": "arksine/moonraker/issue/349", + "url": "https://github.com/Arksine/moonraker/issues/349", + "title": "PolicyKit warnings; unable to manage services, restart system, or update packages", + "description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.", + "priority": "normal", + "date": 1643392406, + "dismissed": false, + "source": "moonlight", + "feed": "Moonraker" + }, + { + "entry_id": "arksine/moonlight/issue/1", + "url": "https://github.com/Arksine/moonlight/issues/1", + "title": "Announcement Test One", + "description": "This is the description. Anything here should appear in the announcement, up to 512 characters.", + "priority": "normal", + "date": 1646854678, + "dismissed": true, + "source": "moonlight", + "feed": "Moonlight" + }, + { + "entry_id": "arksine/moonlight/issue/2", + "url": "https://github.com/Arksine/moonlight/issues/2", + "title": "Announcement Test Two", + "description": "This is a high priority announcement. This line is included in the description.", + "priority": "high", + "date": 1646855579, + "dismissed": false, + "source": "moonlight", + "feed": "Moonlight" + }, + { + "entry_id": "arksine/moonlight/issue/3", + "url": "https://github.com/Arksine/moonlight/issues/3", + "title": "Test announcement 3", + "description": "Test Description [with a link](https://moonraker.readthedocs.io).", + "priority": "normal", + "date": 1647459219, + "dismissed": false, + "source": "moonlight", + "feed": "Moonlight" + } + ], + "modified": false +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :------: | ---------------------------------------------------------- | +| `entries` | [object] | An array of [Announcement Entry](#announcement-entry-spec) | +| | | objects. |^ +| `modified` | bool | A value of `true` indicates that announcement entries | +| | | were changed after the update operation. |^ + +/// + +## Dismiss an announcement +Sets the dismiss flag of an announcement to `true`. + + + +```{.http .apirequest title="HTTP Request"} +POST /server/announcements/dismiss +Content-Type: application/json + +{ + "entry_id": "arksine/moonlight/issue/1", + "wake_time": 600 +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.announcements.dismiss", + "params": { + "entry_id": "arksine/moonlight/issue/1", + "wake_time": 600 + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------- | :-----------: | ------------ | -------------------------------------------- | +| `entry_id` | string | **REQUIRED** | The entry ID of the announcement to dismiss. | +| `wake_time` | float \| null | null | A time, in seconds, after which the entry's | +| | | | `dismiss` flag will revert to `true`. When |^ +| | | | set to `null` the flag will remain `false` |^ +| | | | indefinitely. |^ + +//// tip +The `entry_id` typically contains forward slashes. Remember to escape this value +if including it in the query string of an HTTP request. +//// + +/// + +```{.json .apiresponse title="Example Response"} +{ + "entry_id": "arksine/moonlight/issue/1" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :----: | ------------------------------------------------- | +| `entry_id` | string | The entry ID of the dismissed announcement entry. | + +/// + +## List announcement feeds + +```{.http .apirequest title="HTTP Request"} +GET /server/announcements/feeds +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.announcements.feeds", + "id": 4654 +} +``` + +```{.json .apiresponse title="Example Response"} +{ + "feeds": [ + "moonraker", + "klipper" + ] +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :------: | ----------------------------------------------------- | +| `feeds` | [string] | An array of announcement feeds Moonraker is currently | +| | | subscribed to. |^ + +/// + +## Subscribe to an announcement feed + +Subscribes Moonraker to the announcement feed specified in the request. + +```{.http .apirequest title="HTTP Request"} +POST /server/announcements/feed +Content-Type: application/json + +{ + "name": "my_feed" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.announcements.post_feed", + "params": { + "name": "my_feed" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------------ | -------------------------------------------------- | +| `name` | string | **REQUIRED** | The name of the announcement feed to subscribe to. | + +/// + +```{.json .apiresponse title="Example Response"} +{ + "feed": "my_feed", + "action": "added" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | -------------------------------------------------------- | +| `feed` | string | The name of the announcement feed subscribed to. | +| `action` | string | The [subscription action](#feed-subscribed-action-desc ) | +| | | taken by Moonraker after the request has been processed. |^ + +| Action | Description | +| --------- | ------------------------------------------------------- | +| `added` | The requested announcement feed has been subscribed to. | +| `skipped` | Moonraker was already subscribed to the requested feed. | +{ #feed-subscribed-action-desc } Subscription Action + +/// + +## Remove an announcement feed + +Removes a subscribed feed. Only feeds previously subscribed to using +the [subscribe feed](#subscribe-to-an-announcement-feed) endpoint may be +removed. Feeds configured in `moonraker.conf` may not be removed. + +```{.http .apirequest title="HTTP Request"} +DELETE /server/announcements/feed?name=my_feed +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.announcements.delete_feed", + "params": { + "name": "my_feed" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------------ | -------------------------------------------- | +| `name` | string | **REQUIRED** | The name of the announcement feed to remove. | + +/// + + +Parameters: + +- `name`: The name of the new feed to remove. This parameter is required. + +Returns: + +The name of the new feed and the action taken. The `action` will be +`removed` if the operation was successful. + +```{.json .apiresponse title="Example Response"} +{ + "feed": "my_feed", + "action": "removed" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ------------------------------------------------------ | +| `feed` | string | The name of the announcement feed removed. | +| `action` | string | The action taken after the request. Will be `removed` | +| | | upon successful removal. |^ + +//// tip +Unlike the [feed subscription request](#subscribe-to-an-announcement-feed) an +error will be returned if either the feed does not exist or the feed is +configured in `moonraker.conf`. +//// +/// + +## Appendix + +This section will provide an overview of how the announcement system +in Moonraker works, how to set up a dev environment, and provide +recommendations on front-end implementation. + +### How announcements work + +Moonraker announcements are GitHub issues tagged with the `announcement` +label. GitHub repos may registered with +[moonlight](https://github.com/arksine/moonlight), which is responsible +for generating RSS feeds from GitHub issues using GitHub's REST API. These +RSS feeds are hosted on GitHub Pages, for example Moonraker's feed may be found +[here](https://arksine.github.io/moonlight/assets/moonraker.xml). By +centralizing GitHub API queries in `moonlight` we are able to poll multiple +repos without running into API rate limit issues. Moonlight has has a workflow +that checks all registered repos for new announcements every 30 minutes. In +theory it would be able to check for announcements in up to 500 repos before +exceeding GitHub's API rate limit. + +Moonraker's `[announcements]` component will always check the `klipper` and +`moonraker` RSS feeds. It is possible to configure additional RSS feeds by +adding them to the `subscriptions` option. The component will poll configured +feeds every 30 minutes, resulting in maximum of 1 hour for new announcements +to reach all users. + +When new issues are tagged with `announcement` these entries will be parsed +and added to the RSS feeds. When the issue is closed they will be removed from +the corresponding feed. Moonlight will fetch up to 20 announcements for each +feed, if a repo goes over this limit older announcements will be removed. + +/// Note +It is also possible for Moonraker to generate announcements itself. For +example, if a Moonraker component needs user feedback it may generate an +announcement and notify all connected clients. From a front-end's +perspective there is no need to treat these announcements differently than +any other announcement. +/// + +### Setting up the dev environment + +Moonraker provides configuration to parse announcements from a local folder +so that it is possible to manually add and remove entries, allowing front-end +developers to perform integration tests: + +```ini +# moonraker.conf + +[announcements] +dev_mode: True +``` + +With `dev_mode` enabled, Moonraker will look for`moonraker.xml` and +`klipper.xml` in the following folder: +```shell +~/moonraker/.devel/announcement_xml +``` + +If moonraker is not installed in the home folder then substitute `~` +for the parent folder location. This folder is in a hardcoded location +to so as not to expose users to vulnerabilities associated with parsing XML. + +It is possible to configure Moonraker to search for your own feeds: + +```ini +# moonraker.conf + +[announcements] +subscription: + my_project +dev_mode: True +``` + +The above configuration would look for `my_project.xml` in addition to +`klipper.xml` and `moonraker.xml`. The developer may manually create +the xml feeds or they may clone `moonlight` and leverage its script +to generate a feed from issues created on their test repo. When local +feeds have been modified one may call the [update announcements API](#update-announcements) +to have Moonraker fetch the updates and add/remove entries. + +### RSS file structure + +Moonlight generates RSS feeds in XML format. Below is an example generated +from moonlight's own issue tracker: + +```xml + + + + arksine/moonlight + https://github.com/Arksine/moonlight + RSS Announcements for Moonraker + Tue, 22 Mar 2022 23:19:04 GMT + f2912192bf0d09cf18d8b8af22b2d3501627043e5afa3ebff0e45e4794937901 + + Test announcement 3 + https://github.com/Arksine/moonlight/issues/3 + Test Description [with a link](https://moonraker.readthedocs.io). + Wed, 16 Mar 2022 19:33:39 GMT + normal + arksine/moonlight/issue/3 + + + Announcement Test Two + https://github.com/Arksine/moonlight/issues/2 + This is a high priority announcement. This line is included in the description. + Wed, 09 Mar 2022 19:52:59 GMT + high + arksine/moonlight/issue/2 + + + Announcement Test One + https://github.com/Arksine/moonlight/issues/1 + This is the description. Anything here should appear in the announcement, up to 512 characters. + Wed, 09 Mar 2022 19:37:58 GMT + normal + arksine/moonlight/issue/1 + + + +``` + +Each xml file may contain only one `` element, and each `` element +may contain only one channel. All items must be present aside from +`moonlight:configHash`, which is used by the workflow to detect changes to +moonlight's configuration. Most elements are self explanatory, developers will +be most interested in adding and removing `` elements, as these are +the basis for entries in Moonraker's announcement database. + +### Generating announcements from your own repo + +As mentioned previously, its possible to clone moonlight and use its rss +script to generate announcements from issues in your repo: + +```shell +cd ~ +git clone https://github.com/arksine/moonlight +cd moonlight +virtualenv -p /usr/bin/python3 .venv +source .venv/bin/activate +pip install httpx[http2] +deactivate +``` + +To add your repo edit `~/moonlight/src/config.json`: +```json +{ + "moonraker": { + "repo_owner": "Arksine", + "repo_name": "moonraker", + "description": "API Host For Klipper", + "authorized_creators": ["Arksine"] + }, + "klipper": { + "repo_owner": "Klipper3d", + "repo_name": "klipper", + "description": "A 3D Printer Firmware", + "authorized_creators": ["KevinOConnor"] + }, + // Add your test repo info here. It should contain + // fields matching those in "moonraker" and "klipper" + // shown above. +} +``` + +Once your repo is added, create one or more issues on your GitHub +repo tagged with the `announcement` label. Add the `critical` label to +one if you wish to test high priority announcements. You may need to +create these labels in your repo before they can be added. + +Now we can use moonlight to generate the xml files: +```shell +cd ~/moonlight +source .venv/bin/activate +src/update_rss.py +deactivate +``` + +After the script has run it will generate the configured RSS feeds +and store them in `~/moonlight/assets`. If using this method it may +be useful to create a symbolic link to it in Moonraker's devel folder: + +```shell +cd ~/moonraker +mkdir .devel +cd .devel +ln -s ~/moonlight/assets announcement_xml +``` + +If you haven't done so, configure Moonraker to subscribe to your feed +and restart the Moonraker service. Otherwise you may call the +[announcement update](#update-announcements) API to have Moonraker +parse the announcements from your test feed. + + +### Implementation details and recommendations + +When a front-end first connects to Moonraker it is recommended that the +[list announcements](#list-announcements) API is called to retrieve +the current list of [announcement entries](#announcement-entry-spec). +If the front-end is connected via websocket it may watch for the +[announcement update](./jsonrpc_notifications.md#announcement-update-event) and +[announcement dismissed](./jsonrpc_notifications.md#announcement-dismissed-event) +JSON-RPC notifications and update its UI accordingly. + +Front-end devs should decide how they want to present announcements to users. +They could be treated as any other notification, for example a front-end +may have a notification icon that shows the current number of unread +announcements. Front-ends can mark an announcement as `read` by calling +the [dismiss announcement](#dismiss-an-announcement) endpoint. Any +announcement entry with `dismissed == true` should be considered read. + +When a `high priority` announcement is detected it is recommended that +clients present the announcement in a format that is immediately visible +to the user. That said, it may be wise to allow users to opt out of +this behavior via configuration. + +/// note +If an announcement is dismissed, closed on GitHub, then reopened, +the `dismissed` flag will reset to false. This is expected behavior +as announcements are pruned from the database when they are no +longer present in feeds. It isn't valid for repo maintainers +to re-open a closed announcement. That said, its fine to close +and re-open issues during development and testing using repos +that are not yet registered with moonlight. +/// diff --git a/docs/external_api/authorization.md b/docs/external_api/authorization.md new file mode 100644 index 0000000..60ad2fc --- /dev/null +++ b/docs/external_api/authorization.md @@ -0,0 +1,635 @@ + +# Authorization and Authentication + +The Authorization endpoints provide access to the various methods +used to authorize connections to Moonraker. This includes user +authentication, API Key authentication, Temporary access via +"oneshot tokens", and IP and/or domain based authentication +("ie: trusted clients). + +Untrusted clients must use either a JSON Web Token or an API key to access +Moonraker's HTTP APIs. JWTs should be included in the `Authorization` +header as a `Bearer` type for each HTTP request. If using an API Key it +should be included in the `X-Api-Key` header for each HTTP Request. + +Websocket authentication can be achieved via the request itself or +post connection. Unlike HTTP requests it is not necessary to pass a +token and/or API Key to each request. The +[identify connection](./server.md#identify-connection) endpoint takes optional +`access_token` and `api_key` parameters that may be used to authenticate +a user already logged in, otherwise the `login` API may be used for +authentication. Websocket connections will stay authenticated until +the connection is closed or the user logs out. + +User authentication can be performed using a choice sources. Moonraker +currently supports the following authentication sources: + +| Source Name | Description | +| ----------- | -------------------------------------------------------------- | +| `moonraker` | Authentication is performed using credentials stored in | +| | Moonraker's database. |^ +| `ldap` | Authentication is performed through a connected LDAP provider. | +| | Requires a valid `[LDAP]` configuration. |^ +{ #auth-source-desc } Authentication Source + +/// note +ECMAScript imposes limitations on certain requests that prohibit the +developer from modifying the HTTP headers (ie: Requests to open a +websocket, "download" requests that open a user dialog). In these cases +it is recommended for the developer to request a `oneshot_token`, then +send the result via the `token` query string argument in the desired +request. +/// + +/// warning +It is strongly recommended that arguments for the below APIs are +passed in the request's body. +/// + +## Login User +```{.http .apirequest title="HTTP Request"} +POST /access/login +Content-Type: application/json + +{ + "username": "my_user", + "password": "my_password", + "source": "moonraker" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.login", + "params": { + "username": "my_user", + "password": "my_password", + "source": "moonraker" + }, + "id": 1323 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | -------------------- | --------------------------------------------------- | +| `username` | string | **REQUIRED** | The user login name. | +| `password` | string | **REQUIRED** | The user password. | +| `source` | string | Set by configuration | A valid [authentication source](#auth-source-desc). | + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY4MDAuNDgxNjU1LCAiZXhwIjogMTYxODg4MDQwMC40ODE2NTUsICJ1c2VybmFtZSI6ICJteV91c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.QdieeEskrU0FrH7rXKuPDSZxscM54kV_vH60uJqdU9g", + "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY4MDAuNDgxNzUxNCwgImV4cCI6IDE2MjY2NTI4MDAuNDgxNzUxNCwgInVzZXJuYW1lIjogIm15X3VzZXIiLCAidG9rZW5fdHlwZSI6ICJyZWZyZXNoIn0.btJF0LJfymInhGJQ2xvPwkp2dFUqwgcw4OA_wE-EcCM", + "action": "user_logged_in", + "source": "moonraker" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------------- | :----: | -------------------------------------------------------------------- | +| `username` | string | The name of the logged in user. | +| `token` | string | A JSON Web Token (JWT) used to authenticate requests, also commonly | +| | | referred to as an `access token`. HTTP requests should include this |^ +| | | token in the `Authorization` header as a `Bearer` type. This token |^ +| | | expires after 1 hour. |^ +| `refresh_token` | string | A JWT that should be used to generate new access tokens after they | +| | | expire. See the [refresh token section](#refresh-json-web-token) |^ +| | | for details. |^ +| `action` | string | The action taken by the auth manager. Will always be | +| | | "user_logged_in". |^ +| `source` | string | The [authentication source](#auth-source-desc) used. | + +/// + +/// note +This endpoint may be accessed without prior authentication. A 401 will +only be returned if the authentication fails. +/// + +## Logout Current User + +```{.http .apirequest title="HTTP Request"} +POST /access/logout +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.logout", + "id": 1323 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "action": "user_logged_out" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :----: | ----------------------------------------------------- | +| `username` | string | The name of the logged out user. | +| `action` | string | The action taken by the auth manager. Will always be | +| | | "user_logged_out". |^ + +/// + +## Get Current User + +```{.http .apirequest title="HTTP Request"} +GET /access/user +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.get_user", + "id": 1323 +} +``` + +Returns: An object containing the currently logged in user name, the source and +the date on which the user was created (in unix time). +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "source": "moonraker", + "created_on": 1618876783.8896716 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------ | :----: | ---------------------------------------------------- | +| `username` | string | The name of the logged in user. | +| `source` | string | The [source](#auth-source-desc) used to authenticate | +| | | the user. |^ +| `created_on` | float | The date, in unix time, the user entry was created. | + +/// + +## Create User + +Creates a new local user and logs them in. + +```{.http .apirequest title="HTTP Request"} +POST /access/user +Content-Type: application/json + +{ + "username": "my_user", + "password": "my_password" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.post_user", + "params": { + "username": "my_user", + "password": "my_password" + }, + "id": 1323 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | -------------------- | +| `username` | string | **REQUIRED** | The user login name. | +| `password` | string | **REQUIRED** | The user password. | + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY3ODMuODkxNjE5LCAiZXhwIjogMTYxODg4MDM4My44OTE2MTksICJ1c2VybmFtZSI6ICJteV91c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.oH0IShTL7mdlVs4kcx3BIs_-1j0Oe-qXezJKjo-9Xgo", + "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY3ODMuODkxNzAyNCwgImV4cCI6IDE2MjY2NTI3ODMuODkxNzAyNCwgInVzZXJuYW1lIjogIm15X3VzZXIiLCAidG9rZW5fdHlwZSI6ICJyZWZyZXNoIn0.a6ZeRjk8RQQJDDH0JV-qGY_d_HIgfI3XpsqUlUaFT7c", + "source": "moonraker", + "action": "user_created" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------------- | :----: | --------------------------------------------------------------------- | +| `username` | string | The name of the created user. | +| `token` | string | A JSON Web Token (JWT) used to authenticate requests, also commonly | +| | | referred to as an `access token`. HTTP requests should include this |^ +| | | token in the `Authorization` header as a `Bearer` type. This token |^ +| | | expires after 1 hour. |^ +| `refresh_token` | string | A JWT that should be used to generate new access tokens after they | +| | | expire. See the [refresh token section](#refresh-json-web-token) |^ +| | | for details. |^ +| `action` | string | The action taken by the auth manager. Will always be "user_created". | +| `source` | string | The [authentication source](#auth-source-desc) used. | + +/// + +/// note +Unlike `/access/login`, `/access/user` is a protected endpoint. To +create a new user a client must either be trusted, use the API Key, +or be logged in as another user. +/// + +## Delete User + +/// note +A request to delete a user MUST come from an authorized login +other than the account to be deleted. This can be a "trusted user", +the "api key user", or any other user account. +/// + +```{.http .apirequest title="HTTP Request"} +DELETE /access/user +Content-Type: application/json + +{ + "username": "my_username" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.delete_user", + "params": { + "username": "my_username" + }, + "id": 1323 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :--: | ------------ | ------------------------------------ | +| `username` | str | **REQUIRED** | The username of the entry to delete. | + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "action": "user_deleted" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :----: | ------------------------------------------------- | +| `username` | string | The username of the deleted entry. | +| `action` | string | The action taken by the auth manager. Will always | +| | | be "user_deleted". |^ + +/// + +## List Available Users + +```{.http .apirequest title="HTTP Request"} +GET /access/users/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.users.list", + "id": 1323 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "users": [ + { + "username": "testuser", + "source": "moonraker", + "created_on": 1618771331.1685035 + }, + { + "username": "testuser2", + "source": "ldap", + "created_on": 1620943153.0191233 + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :------: | -------------------------------- | +| `users` | [object] | An array of `User Info` objects. | +| | | #user-info-spec |+ + +| Field | Type | Description | +| ------------ | :----: | ---------------------------------------------------- | +| `username` | string | The username of the entry. | +| `source` | string | The [source](#auth-source-desc) that must be used to | +| | | authenticate the user. |^ +| `created_on` | float | The date, in unix time, the user entry was created. | +{ #user-info-spec } User Info + +/// + +## Reset User Password + +```{.http .apirequest title="HTTP Request"} +POST /access/user/password +Content-Type: application/json + +{ + "password": "my_current_password", + "new_password": "my_new_pass" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.user.password", + "params": { + "password": "my_current_password", + "new_password": "my_new_pass" + }, + "id": 1323 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------------- | :----: | ------------ | ---------------------------- | +| `password` | string | **REQUIRED** | The user's current password. | +| `new_password` | string | **REQUIRED** | The user's new password. | + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "action": "user_password_reset" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :----: | --------------------------------------------------- | +| `username` | string | The username of the entry whose password was reset. | +| `action` | string | Action taken by the Auth manager. Will always be | +| | | "user_password_reset". |^ + +/// + +## Refresh JSON Web Token +This endpoint can be used to refresh an expired access token. If this +request returns an error then the refresh token is no longer valid and +the user must login with their credentials. + +```{.http .apirequest title="HTTP Request"} +POST /access/refresh_jwt +Content-Type: application/json + +{ + "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4Nzc0ODUuNzcyMjg5OCwgImV4cCI6IDE2MjY2NTM0ODUuNzcyMjg5OCwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAicmVmcmVzaCJ9.Y5YxGuYSzwJN2WlunxlR7XNa2Y3GWK-2kt-MzHvLbP8" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.refresh_jwt", + "params": { + "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4Nzc0ODUuNzcyMjg5OCwgImV4cCI6IDE2MjY2NTM0ODUuNzcyMjg5OCwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAicmVmcmVzaCJ9.Y5YxGuYSzwJN2WlunxlR7XNa2Y3GWK-2kt-MzHvLbP8" + }, + "id": 1323 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| --------------- | :----: | ------------ | ------------------------------------- | +| `refresh_token` | string | **REQUIRED** | A valid `refresh_token` for the user. | + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "username": "my_user", + "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzgyNDMuNTE2Nzc5MiwgImV4cCI6IDE2MTg4ODE4NDMuNTE2Nzc5MiwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.Ia_X_pf20RR4RAEXcxalZIOzOBOs2OwearWHfRnTSGU", + "source": "moonraker", + "action": "user_jwt_refresh" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :----: | -------------------------------------------------------------------- | +| `username` | string | The username of the entry whose access token ws refreshed. | +| `token` | string | A JSON Web Token (JWT) used to authenticate requests, also commonly | +| | | referred to as an `access token`. HTTP requests should include this |^ +| | | token in the `Authorization` header as a `Bearer` type. This token |^ +| | | expires after 1 hour. |^ +| `source` | string | The [authentication source](#auth-source-desc) of the user entry. | +| `action` | string | The action taken by the Auth Manager. Will always be | +| | | "user_jwt_refresh". |^ + +/// + +/// note +This endpoint may be accessed by unauthorized clients. A 401 will +only be returned if the refresh token is invalid. +/// + +## Generate a Oneshot Token + +Javascript is not capable of modifying the headers for some HTTP requests +(for example, the `websocket`), which is a requirement to apply JWT or API Key +authorization. To work around this clients may request a Oneshot Token and +pass it via the query string for these requests. Tokens expire in 5 seconds +and may only be used once, making them relatively safe for inclusion in the +query string. + +```{.http .apirequest title="HTTP Request"} +GET /access/oneshot_token +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.oneshot_token", + "id": 1323 +} +``` + +```{.json .apiresponse title="Example Response"} +"APDBEGHUTBUD6SOAYBPF3KE5BRMO7YSL" +``` + +/// api-response-spec + open: True + +The response is a string value containing the oneshot token. It may +added to a request's query string for access to any API endpoint. The query +string should be added in the form of: + +``` +?token={base32_random_token} +``` + +/// + +## Get authorization module info + +```{.http .apirequest title="HTTP Request"} +GET /access/info +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.info", + "id": 1323 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "default_source": "moonraker", + "available_sources": [ + "moonraker", + "ldap" + ], + "login_required": false, + "trusted": true +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------------- | :------: | ---------------------------------------------------- | +| `default_source` | string | The configured default | +| | | [authentication source](#auth-source-desc). |^ +| `available_sources` | [string] | An array of available authentication sources. | +| `login_required` | bool | Set to `true` when `force_logins` is enabled via the | +| | | configuration at least one user has been created. |^ +| `trusted` | bool | Set to `true` when the connection making the info | +| | | request is a trusted connection. |^ + +/// + +/// note +This endpoint may be accessed by unauthorized clients. +/// + +## Get the Current API Key + +```{.http .apirequest title="HTTP Request"} +GET /access/api_key +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.get_api_key", + "id": 1323 +} +``` + +```{.json .apiresponse title="Example Response"} +e514851f37b94c779d955212b6906f95 +``` + +/// api-response-spec + open: True + +The response string value containing the current API key. + +/// + +## Generate a New API Key +```{.http .apirequest title="HTTP Request"} +POST /access/api_key +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "access.post_api_key", + "id": 1323 +} +``` + +```{.json .apiresponse title="Example Response"} +e514851f37b94c779d955212b6906f95 +``` + +/// api-response-spec + open: True + +The response string value containing the new API key. + +/// + +/// note +After this request executes the API key change is applied immediately. +All subsequent HTTP requests from untrusted clients must use the new key. +Changing the API Key will not affect open websockets authenticated using +the previous API Key. +/// diff --git a/docs/external_api/database.md b/docs/external_api/database.md new file mode 100644 index 0000000..a36274b --- /dev/null +++ b/docs/external_api/database.md @@ -0,0 +1,773 @@ +# Database Management + +The following endpoints provide access to Moonraker's internal sqlite database. +The primary table exposed to clients is divided into `namespaces`. Each client +may define its own namespace to store information. From the client's point of +view, a namespace is an `object`. Items in the database are accessed by providing +a namespace and a key. A key may be specified as string, where a "." is a +delimiter to access nested fields. Alternatively the key may be specified +as an array of strings, where each string references a nested field. +This is useful for scenarios where a namespace contains fields that include +a "." character, such as a file name. + +/// note +Moonraker reserves several namespaces for internal use. Clients may read from +these namespaces but they may not modify them. +/// + +For example, assume the following object is stored in the "superclient" +namespace: + +```json +{ + "settings": { + "console": { + "enable_autocomplete": true + } + }, + "theme": { + "background_color": "black" + } +} +``` +One may access the `enable_autocomplete` field by supplying `superclient` as +the `namespace` argument and `settings.console.enable_autocomplete` or +`["settings", "console", "enable_autocomplete"]` as the `key` argument for +the request. The entire settings object could be accessed by providing +`settings` or `["settings"]` as the `key` argument. The entire namespace +may be read by omitting the `key` argument, however as explained below it +is not possible to modify a namespace without specifying a key. + +## List Database Info + +Lists all namespaces with read and/or write access. Also lists database +backup files. + +```{.http .apirequest title="HTTP Request"} +GET /server/database/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.list", + "id": 8694 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "namespaces": [ + "gcode_metadata", + "webcams", + "update_manager", + "announcements", + "database", + "moonraker" + ], + "backups": [ + "sqldb-backup-20240513-134542.db", + "testbackup.db", + "testbackup2.db" + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------ | :------: | ---------------------------------------------------- | +| `namespaces` | [string] | An array of namespaces registered with the database | +| | | that may be read by clients. |^ +| `backups` | [string] | An array of database backup filenames that have been | +| | | created. |^ + +/// + +## Get Database Item + +Retrieves an item from a specified namespace. The `key` argument may be +omitted, in which case an object representing the entire namespace will +be returned in the `value` field. If the `key` is provided and does not +exist in the database an error will be returned. + +```{.http .apirequest title="HTTP Request"} +GET /server/database/item?namespace={namespace}&key={key} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.get_item", + "params": { + "namespace": "{namespace}", + "key": "{key}" + }, + "id": 5644 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------- | :----------------: | ------------ | ------------------------------------------ | +| `namespace` | string | **REQUIRED** | The namespace of the item to retrieve. | +| `key` | string \| [string] | null | The key indicating the field or fields | +| | \| null | | within the namespace to retrieve. May |^ +| | | | be a string, where nested fields are |^ +| | | | separated by a ".", or a list of strings. |^ +| | | | If the key is omitted the entire namespace |^ +| | | | will be returned. |^ + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "namespace": "moonraker", + "key": "file_manager.metadata_version", + "value": 2 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :----------------: | ------------------------------------------------- | +| `namespace` | string | The namespace of the returned item. | +| `key` | string \| [string] | The key indicating the requested field(s). | +| | \| null | |^ +| `value` | any | The value of the requested item. This can be any | +| | | valid JSON type. |^ + +/// + +## Add Database Item +Inserts an item into the database. If the `namespace` does not exist +it will be created. If the `key` specifies a nested field, all parents +will be created if they do not exist. If the key exists it will be +overwritten with the provided `value`. The `key` parameter must be provided, +as it is not possible to assign a value directly to a namespace. + +/// note +If the request parameters are placed in the query string and the `value` +is not a string type, then `value` argument must provide a +[type hint](./introduction.md#query-string-type-hints). It is strongly +recommended to put parameters in the body of the request wrapped in a +JSON object. +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/database/item +Content-Type: application/json + +{ + "namespace": "my_client", + "key": "settings.some_count", + "value": 100 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------- | :----------------: | ------------ | ----------------------------------------- | +| `namespace` | string | **REQUIRED** | The namespace where the value | +| | | | should be inserted. |^ +| `key` | string \| [string] | **REQUIRED** | The key indicating the field or fields | +| | | | where the value should be inserted. |^ +| `value` | any | **REQUIRED** | The value to insert in the database. May | +| | | | be any valid JSON type. |^ + +/// + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.post_item", + "params": { + "namespace": "{namespace}", + "key": "{key}", + "value": 100 + }, + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "namespace": "test", + "key": "settings.some_count", + "value": 9001 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :----------------: | ------------------------------------------- | +| `namespace` | string | The namespace where the value was inserted. | +| `key` | string \| [string] | The key indicating the field or fields | +| | | where the value was inserted. |^ +| `value` | any | The value inserted into the database. May | +| | | be any valid JSON type. |^ + +/// + +## Delete Database Item + +Deletes an item from a `namespace` at the specified `key`. If the key does not +exist in the namespace an error will be returned. If the deleted item results +in an empty namespace, the namespace will be removed from the database. + +```{.http .apirequest title="HTTP Request"} +DELETE /server/database/item?namespace={namespace}&key={key} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.delete_item", + "params": { + "namespace": "{namespace}", + "key": "{key}" + }, + "id": 4654 +} +``` +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------- | :----------------: | ------------ | -------------------------------------- | +| `namespace` | string | **REQUIRED** | The namespace where the item should be | +| | | | should be removed. |^ +| `key` | string \| [string] | **REQUIRED** | The key indicating the field or fields | +| | | | where the item should be removed. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "namespace": "test", + "key": "settings.some_count", + "value": 9001 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :----------------: | ------------------------------------------ | +| `namespace` | string | The namespace containing the item removed. | +| `key` | string \| [string] | The key indicating the field or fields | +| | | where the item was removed. |^ +| `value` | any | The of the item at the removed field. May | +| | | be any valid JSON type. |^ + +/// + +## Compact Database + +Compacts and defragments the the sqlite database using the `VACUUM` command. +This endpoint cannot be requested when Klipper is printing. + +```{.http .apirequest title="HTTP Request"} +POST /server/database/compact +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.compact", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "previous_size": 139264, + "new_size": 122880 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------------- | :--: | -------------------------------------------------- | +| `previous_size` | int | Size in bytes of the database prior to compaction. | +| `new_size` | int | Size in bytes of the database after compaction. | + +/// + +## Backup Database + +Creates a backup of the current database. The backup will be +created in the `/backup/database/`. + +This API cannot be requested when Klipper is printing. + +```{.http .apirequest title="HTTP Request"} +POST /server/database/backup +Content-Type: application/json + +{ + "filename": "sql-db-backup.db" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.post_backup", + "params": { + "filename": "sql-db-backup.db" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | -------------------------- | -------------------------- | +| `filename` | string | sqldb-backup-{timespec}.db | The file name of the saved | +| | | | backup file. |^ + +//// note +The `{timespec}` of the default `filename` is in the following format: + +`-` +//// + +/// + +```{.json .apiresponse title="Example Response"} +{ + "backup_path": "/home/test/printer_data/backup/database/sql-db-backup.db" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------------ | +| `backup_path` | string | The complete absolute path where the backup was saved. | + +/// + +## Delete a backup + +Deletes a previously backed up database. + +```{.http .apirequest title="HTTP Request"} +DELETE /server/database/backup?filename=sql-db-backup.db +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.delete_backup", + "params": { + "filename": "sql-db-backup.db" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | ---------------------------------------------- | +| `filename` | string | **REQUIRED** | The name of the backup file to delete. Must be | +| | | | a valid filename reported by the |^ +| | | | [database list endpoint](#list-database-info). |^ + +/// + +```{.json .apiresponse title="Example Response"} +{ + "backup_path": "/home/test/printer_data/backup/database/sql-db-backup.db" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | -------------------------------------------------------- | +| `backup_path` | string | The complete absolute path where the backup was removed. | + +/// + + +## Restore Database + +Restores a previously backed up sqlite database file. The backup +must be located at `/backup/database/`. The +`` must be a valid filename reported in by the +[database list](#list-database-info) API. + +This API cannot be requested when Klipper is printing. + +/// Note +Moonraker will restart immediately after this request is processed. +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/database/restore +Content-Type: application/json + +{ + "filename": "sql-db-backup.db" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.database.restore", + "params": { + "filename": "sql-db-backup.db" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | ----------------------------------------------- | +| `filename` | string | **REQUIRED** | The name of the backup file to restore. Must be | +| | | | a valid filename reported by the |^ +| | | | [database list endpoint](#list-database-info). |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "restored_tables": [ + "table_registry", + "namespace_store", + "authorized_users", + "job_history", + "job_totals" + ], + "restored_namespaces": [ + "database", + "fluidd", + "gcode_metadata", + "mainsail", + "moonraker", + "update_manager", + "webcams" + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------------------- | :------: | ------------------------------------------- | +| `restored_tables` | [string] | An array of table names that were recovered | +| | | after the restore operation. |^ +| `restored_namespaces` | [string] | An array of namespaces that were recovered | +| | | after the restore operation. |^ + +/// + +## Debug endpoints + +Below are a number of debug endpoints available when Moonraker has been +launched with [debug features enabled](../installation.md#command-line-usage). +Front ends should not rely on these endpoints in production releases, however +they may be useful during development. Developers writing extensions and/or +additions to Moonraker may also find these endpoints useful. + +/// Warning +Debug endpoints may expose security vulnerabilities. They should only be +enabled by developers on secured machines. +/// + +### List Database Info (debug) + +Debug version of the [List Database Info](#list-database-info) endpoint. +Returns all namespaces, including those exclusively reserved for Moonraker. +In addition all registered SQL tables are reported. + + +```{.http .apirequest title="HTTP Request"} +GET /debug/database/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "debug.database.list", + "id": 8694 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "namespaces": [ + "gcode_metadata", + "webcams", + "update_manager", + "announcements", + "database", + "moonraker" + ], + "backups": [ + "sqldb-backup-20240513-134542.db", + "testbackup.db", + "testbackup2.db" + ], + "tables": [ + "job_history", + "job_totals", + "namespace_store", + "table_registry", + "authorized_users" + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------ | :------: | -------------------------------------------------------- | +| `namespaces` | [string] | An array of all namespaces registered with the database. | +| `backups` | [string] | An array of database backup filenames that have been | +| | | created. |^ +| `tables` | [string] | An array of tables created within the database. | + +/// + +### Get Database Item (debug) + +Debug version of the [Get Database Item](#get-database-item) endpoint. +Keys within protected and forbidden namespaces may be read. + +```http title="HTTP Request" +GET /debug/database/item?namespace={namespace}&key={key} +``` +```json title="JSON-RPC Request" +{ + "jsonrpc": "2.0", + "method": "debug.database.get_item", + "params": { + "namespace": "{namespace}", + "key": "{key}" + }, + "id": 5644 +} +``` + +See the [Get Database Item](#get-database-item) endpoint for the +`Parameter Specification`, `Example Response`, and `Response Specification`. + +### Add Database Item (debug) + +Debug version of the [Add Database Item](#add-database-item) endpoint. +Keys within protected and forbidden namespaces may be inserted. + +/// Warning +Modifying protected namespaces outside of Moonraker can result in +broken functionality and is not supported for production environments. +Issues opened with reports/queries related to this endpoint will be +redirected to this documentation and closed. +/// + +```{.http .apirequest title="HTTP Request"} +POST /debug/database/item +Content-Type: application/json + +{ + "namespace": "my_client", + "key": "settings.some_count", + "value": 100 +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "debug.database.post_item", + "params": { + "namespace": "{namespace}", + "key": "{key}", + "value": 100 + }, + "id": 4654 +} +``` + +See the [Add Database Item](#add-database-item) endpoint for the +`Parameter Specification`, `Example Response`, and `Response Specification`. + +### Delete Database Item (debug) + +Debug version of [Delete Database Item](#delete-database-item). Keys within +protected and forbidden namespaces may be removed. + +/// Warning +Modifying protected namespaces outside of Moonraker can result in +broken functionality and is not supported for production environments. +Issues opened with reports/queries related to this endpoint will be +redirected to this documentation and closed. +/// + +```{.http .apirequest title="HTTP Request"} +DELETE /debug/database/item?namespace={namespace}&key={key} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "debug.database.delete_item", + "params": { + "namespace": "{namespace}", + "key": "{key}" + }, + "id": 4654 +} +``` + +See the [Delete Database Item](#delete-database-item) endpoint for the +`Parameter Specification`, `Example Response`, and `Response Specification`. + + +### Get Database Table + +Requests all the contents of a specified table. + +```{.http .apirequest title="HTTP Request"} +GET /debug/database/table?table=job_history +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "debug.database.table", + "params": { + "table": "job_history" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------- | :----: | ------------ | --------------------------------- | +| `table` | string | **REQUIRED** | The name of the table to request. | + +/// + + + +Returns: + +An object with the table's name and a list of all rows contained +within the table. The `rowid` will always be included for each +row, however it may be represented by an alias. In the example +below the alias for `rowid` is `job_id`. + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "table_name": "job_history", + "rows": [ + { + "job_id": 1, + "user": "No User", + "filename": "active_test.gcode", + "status": "completed", + "start_time": 1690749153.2661753, + "end_time": 1690749173.076986, + "print_duration": 0.0, + "total_duration": 19.975574419135228, + "filament_used": 0.0, + "metadata": { + "size": 211, + "modified": 1635771217.0, + "uuid": "627371e0-faa5-4ced-8bb4-7017d29226fa", + "slicer": "Unknown", + "gcode_start_byte": 8, + "gcode_end_byte": 211 + }, + "auxiliary_data": [], + "instance_id": "default" + }, + { + "job_id": 2, + "user": "No User", + "filename": "active_test.gcode", + "status": "completed", + "start_time": 1701262034.9242446, + "end_time": 1701262054.7332363, + "print_duration": 0.0, + "total_duration": 19.990913168992847, + "filament_used": 0.0, + "metadata": { + "size": 211, + "modified": 1635771217.0, + "uuid": "627371e0-faa5-4ced-8bb4-7017d29226fa", + "slicer": "Unknown", + "gcode_start_byte": 8, + "gcode_end_byte": 211 + }, + "auxiliary_data": { + "spool_ids": [ + 2 + ] + }, + "instance_id": "default" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------ | :------: | -------------------------------------------------- | +| `table_name` | string | The name of the table requested. | +| `rows` | [object] | An array of row objects. The fields for each | +| | | object are columns defined by the table schema. |^ +| | | The `rowid` will always be included for each row, |^ +| | | however it may be represented by an alias. In the |^ +| | | example above, `job_id` is an alias for `rowid`. |^ + +/// diff --git a/docs/external_api/devices.md b/docs/external_api/devices.md new file mode 100644 index 0000000..fa1a9d8 --- /dev/null +++ b/docs/external_api/devices.md @@ -0,0 +1,1442 @@ +# Switches, Sensors, and Devices + +This document covers the API for managing various devices +through Moonraker. It should be noted that the endpoints +here are only available when such devices are added to +Moonraker's configuration. + +## Power Endpoints + +Moonraker's `power` component enables switch-like device management. +Various device types are supported, including GPIOs and HTTP controlled +devices. + +The endpoints in this section are available when +one or more `[power ]` sections are configured +in `moonraker.conf`. + +### Get Device List + +```{.http .apirequest title="HTTP Request"} +GET /machine/device_power/devices +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.device_power.devices", + "id": 5646 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "devices": [ + { + "device": "green_led", + "status": "off", + "locked_while_printing": true, + "type": "gpio" + }, + { + "device": "printer", + "status": "off", + "locked_while_printing": false, + "type": "tplink_smartplug" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------- | :------: | ------------------------------------------------------------ | +| `devices` | [object] | An array of [Power Device Status](#power-device-status-spec) | +| | | objects. |^ + +| Field | Type | Description | +| ----------------------- | :----: | ------------------------------------------------------ | +| `device` | string | The configured name of the device. | +| `status` | string | The current [state](#power-state-desc) of the device. | +| `locked_while_printing` | bool | When set to `true` the power device status | +| | | may not be changed when Klipper is printing. |^ +| `type` | string | The [Device Type](#power-type-desc) of the | +| | | device. |^ +{ #power-device-status-spec } Power Device Status + +| Device State | Description | +| ------------ | -------------------------------- | +| `on` | The device is powered on. | +| `off` | The device is powered off. | +| `init` | The device is initializing. | +| `error` | The device encountered an error. | +{ #power-state-desc } Power Device State + +| Device Type | Description | +| ------------------ | ---------------------------------------------------------- | +| `gpio` | The device is controlled by a GPIO on the local machine. | +| `klipper_device` | The device is controlled by Klipper. | +| `tplink_smartplug` | The device is a TPLink Smartplug Device (aka Kasa Device.) | +| `tasmota` | The device is a HTTP device running Tasmota firmware. | +| `shelly` | The device is a Shelly branded device (V1 API). | +| `homeseer` | The device is a device managed by HomeSeer. | +| `homeassistant` | The device is a device managed by Home Assistant. | +| `loxonev1` | The device is a Loxone V1 device. | +| `rf` | The device is a RF device with a GPIO interface. | +| `mqtt` | The device is a device available over MQTT. | +| `smartthings` | The device is a Samsung SmartThings device. | +| `hue` | The device is a Phillips Hue device. | +| `http` | The device is a generic HTTP device. | +| `uhubctl` | The device is a USB port with a controller compatible with | +| | `uhubctl`. |^ +{ #power-type-desc } Device Type + +//// Note +It is possible for unofficial 3rd party extensions to register their own +Device Types and implementations. +//// +/// + +### Get Device State +Requests the device state for a single configured device. + +```{.http .apirequest title="HTTP Request"} +GET /machine/device_power/device?device=green_led +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.device_power.get_device", + "params": { + "device": "green_led" + }, + "id": 4564 +} +``` + +```{.json .apiresponse title="Example Response"} +{ + "green_led": "off" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | -------------------------------------- | +| *device_name* | string | The current [state](#power-state-desc) | +| | | of the requested device. |^ + +/// + +### Set Device State +Toggle, turn on, or turn off a specified device. + +```{.http .apirequest title="HTTP Request"} +POST /machine/device_power/device +Content-Type: application/json + +{ + "device": "green_led", + "action": "on" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.device_power.post_device", + "params": { + "device": "green_led", + "action": "on" + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :----: | ------------ | --------------------------------------- | +| `device` | string | **REQUIRED** | The name of the device to manage. | +| `action` | string | **REQUIRED** | The [action](#power-device-action-desc) | +| | | | to perform on the device. |^ + +| Action | Description | +| -------- | --------------------- | +| `on` | Turns the device on. | +| `off` | Turns the device off. | +| `toggle` | Toggles device state. | +{ #power-device-action-desc } Power Device Action + +/// + +```{.json .apiresponse title="Example Response"} +{ + "green_led": "off" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | -------------------------------------- | +| *device_name* | string | The current [state](#power-state-desc) | +| | | of the requested device. |^ + +/// + +### Get Batch Device Status +Get power status for the requested devices. At least one device must be +specified. + +```{.http .apirequest title="HTTP Request"} +GET /machine/device_power/status?dev_one&dev_two +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.device_power.status", + "params": { + "dev_one": null, + "dev_two": null + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :--: | ------- | ------------------------------------------ | +| *device* | null | null | There may be multiple devices specified, | +| | | | where the keys the requested device names. |^ +| | | | Values should always be `null`. |^ + +//// Note +The strangeness of this parameter specification is an artifact +from an early attempt to simplify the query string and maintain +compatibility with JSON parameters. +//// + +/// + + +```{.json .apiresponse title="Example Response"} +{ + "green_led": "off", + "printer": "off" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | -------------------------------------- | +| *device_name* | string | The current [state](#power-state-desc) | +| | | of the requested device. |^ + +/// + +### Batch Power On Devices +Power on the requested devices. At least one device must be +specified. + +```{.http .apirequest title="HTTP Request"} +POST /machine/device_power/on +Content-Type: application/json + +{ + "dev_one": null, + "dev_two": null +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.device_power.on", + "params": { + "dev_one": null, + "dev_two": null + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :--: | ------- | ------------------------------------------ | +| *device* | null | null | There may be multiple devices specified, | +| | | | where the keys the requested device names. |^ +| | | | Values should always be `null`. |^ + +//// Note +The strangeness of this parameter specification is an artifact +from an early attempt to simplify query string parameters and maintain +compatibility with JSON parameters. +//// + +/// + +```{.json .apiresponse title="Example Response"} +{ + "green_led": "on", + "printer": "on" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------ | +| *device_name* | string | The current [state](#power-state-desc) | +| | | of the requested device. The field name |^ +| | | of the response is the device's configured |^ +| | | name. The response may contain multiple devices. |^ + +/// + + + +### Batch Power Off Devices +Power off the requested devices. At least one device must be +specified. + +```{.http .apirequest title="HTTP Request"} +POST /machine/device_power/off?dev_one&dev_two +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.device_power.off", + "params": { + "dev_one": null, + "dev_two": null + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :--: | ------- | ------------------------------------------ | +| *device* | null | null | There may be multiple devices specified, | +| | | | where the keys the requested device names. |^ +| | | | Values should always be `null`. |^ + +//// Note +The strangeness of this parameter specification is an artifact +from an early attempt to simplify query string parameters and maintain +compatibility with JSON parameters. +//// + +/// + +```{.json .apiresponse title="Example Response"} +{ + "green_led": "off", + "printer": "off" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------ | +| *device_name* | string | The current [state](#power-state-desc) | +| | | of the requested device. The field name |^ +| | | of the response is the device's configured |^ +| | | name. The response may contain multiple devices. |^ + +/// + +## WLED Endpoints + +The `wled` component can be used to perform high level management of +devices running WLED firmware. + +The endpoints in this section are available when one or more `[wled ]` +sections are configured in `moonraker.conf`. + +For lower-level control of wled consider using the WLED +[JSON API](https://kno.wled.ge/interfaces/json-api/) directly. + +### Get strips + +```{.http .apirequest title="HTTP Request"} +GET /machine/wled/strips +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.strips", + "id": 7123 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "strips": { + "lights": { + "strip": "lights", + "status": "on", + "chain_count": 79, + "preset": -1, + "brightness": 255, + "intensity": -1, + "speed": -1, + "error": null + }, + "desk": { + "strip": "desk", + "status": "on", + "chain_count": 60, + "preset": 8, + "brightness": -1, + "intensity": -1, + "speed": -1, + "error": null + } + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------------------- | +| `strips` | object | A container of [WLED Strip Status](#wled-strip-status-spec) | +| | | objects. The keys in this object will be the name of strip |^ +| | | with the values containing strip status. |^ + +| Field | Type | Description | +| ------------- | :------------: | ------------------------------------------- | +| `strip` | string | The configured name of the strip. | +| `status` | string | The current state of the WLED strip. Will | +| | | be `on` if the strip is enabled or `off` |^ +| | | if the strip is disabled. |^ +| `chain_count` | int | The number of LEDs configured on the chain. | +| `preset` | int | The numbered preset. Will be -1 if no | +| | | preset is selected. |^ +| `brightness` | int | The brightness value set by Moonraker. Will | +| | | be -1 if Moonraker has not set this value. |^ +| `intensity` | int | The intensity value set by Moonraker. Will | +| | | be -1 if Moonraker has not set this value. |^ +| `speed` | int | The speed value set by Moonraker. Will | +| | | be -1 if Moonraker has not set this value. |^ +| `error` | string \| null | A message describing last error returned | +| | | from an attempted WLED command. Will be |^ +| | | `null` if no error is returned. |^ +{ #wled-strip-status-spec } WLED Strip Status + +/// + +### Get strip status + +```{.http .apirequest title="HTTP Request"} +GET /machine/wled/status?strip1&strip2 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.status", + "params": { + "lights": null, + "desk": null + }, + "id": 7124 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------- | :--: | ------- | ----------------------------------------- | +| *strip* | null | null | There may be multiple strips specified, | +| | | | where the keys the requested strip names. |^ +| | | | Values should always be `null`. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "lights": { + "strip": "lights", + "status": "on", + "chain_count": 79, + "preset": -1, + "brightness": 255, + "intensity": -1, + "speed": -1, + "error": null + }, + "desk": { + "strip": "desk", + "status": "on", + "chain_count": 60, + "preset": 8, + "brightness": -1, + "intensity": -1, + "speed": -1, + "error": null + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | -------------------------------------------- | +| *strip* | object | There may be multiple `strips`, where the | +| | | keys are strip names and the values are |^ +| | | [WLED strip status](#wled-strip-status-spec) |^ +| | | objects. |^ + +/// + +### Turn strip on + +Turns the specified strips on to the initial colors or initial preset. + +```{.http .apirequest title="HTTP Request"} +POST /machine/wled/on +Content-Type: application/json + +{ + "lights": null, + "desk": null +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.on", + "params": { + "lights": null, + "desk": null + }, + "id": 7125 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "lights": { + "strip": "lights", + "status": "on", + "chain_count": 79, + "preset": -1, + "brightness": 255, + "intensity": -1, + "speed": -1, + "error": null + }, + "desk": { + "strip": "desk", + "status": "on", + "chain_count": 60, + "preset": 8, + "brightness": -1, + "intensity": -1, + "speed": -1, + "error": null + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | -------------------------------------------- | +| *strip* | object | There may be multiple `strips`, where the | +| | | keys are strip names and the values are |^ +| | | [WLED strip status](#wled-strip-status-spec) |^ +| | | objects. |^ + +/// + +### Turn strip off + +Turns off all specified strips. + +```{.http .apirequest title="HTTP Request"} +POST /machine/wled/off +Content-Type: application/json + +{ + "lights": null, + "desk": null +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.off", + "params": { + "lights": null, + "desk": null + }, + "id": 7126 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------- | :--: | ------- | ----------------------------------------- | +| *strip* | null | null | There may be multiple strips specified, | +| | | | where the keys the requested strip names. |^ +| | | | Values should always be `null`. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "lights": { + "strip": "lights", + "status": "off", + "chain_count": 79, + "preset": -1, + "brightness": 255, + "intensity": -1, + "speed": -1, + "error": null + }, + "desk": { + "strip": "desk", + "status": "off", + "chain_count": 60, + "preset": 8, + "brightness": -1, + "intensity": -1, + "speed": -1, + "error": null + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | -------------------------------------------- | +| *strip* | object | There may be multiple `strips`, where the | +| | | keys are strip names and the values are |^ +| | | [WLED strip status](#wled-strip-status-spec) |^ +| | | objects. |^ + +/// + +### Toggle strip on/off state + +Toggles the current enabled state for the requested strips. + +```{.http .apirequest title="HTTP Request"} +POST /machine/wled/toggle +Content-Type: application/json + +{ + "lights": null, + "desk": null +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.toggle", + "params": { + "lights": null, + "desk": null + }, + "id": 7127 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------- | :--: | ------- | ----------------------------------------- | +| *strip* | null | null | There may be multiple strips specified, | +| | | | where the keys the requested strip names. |^ +| | | | Values should always be `null`. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "lights": { + "strip": "lights", + "status": "on", + "chain_count": 79, + "preset": -1, + "brightness": 255, + "intensity": -1, + "speed": -1, + "error": null + }, + "desk": { + "strip": "desk", + "status": "off", + "chain_count": 60, + "preset": 8, + "brightness": -1, + "intensity": -1, + "speed": -1, + "error": null + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | -------------------------------------------- | +| *strip* | object | There may be multiple `strips`, where the | +| | | keys are strip names and the values are |^ +| | | [WLED strip status](#wled-strip-status-spec) |^ +| | | objects. |^ + +/// + +### Get individual strip state + +```{.http .apirequest title="HTTP Request"} +GET /machine/wled/strip?strip=lights +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.get_strip", + "params": { + "strip": "lights" + } + "id": 7128 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "lights": { + "strip": "lights", + "status": "on", + "chain_count": 79, + "preset": 1, + "brightness": 50, + "intensity": 255, + "speed": 255, + "error": null + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | -------------------------------------------------- | +| *strip* | object | An object containing the requested strip's current | +| | | status. The key is the strip's name, the value is |^ +| | | an [WLED strip status](#wled-strip-status-spec) |^ +| | | object. |^ + +/// + +### Control individual strip state + +Toggle, turn on, turn off, turn on with preset, turn on with brightness, or +turn on preset will some of brightness, intensity, and speed. Or simply set +some of brightness, intensity, and speed. + +```{.http .apirequest title="HTTP Request"} +POST /machine/wled/strip +Content-Type: application/json + +{ + "strip": "lights", + "action" "on", + "preset": 3, + "brightness": 200, + "intensity": 50, + "speed": 180 +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.wled.post_strip", + "params": { + "strip": "lights", + "action" "on", + "preset": 3, + "brightness": 200, + "intensity": 50, + "speed": 180 + }, + "id": 7128 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------------ | :----: | --------------- | ---------------------------------------------- | +| `strip` | string | **REQUIRED** | The name of the strip to control. | +| `action` | string | **REQUIRED** | The [WLED Action](#wled-action-desc) to | +| | | | execute on the strip. |^ +| `preset` | int | **INITIAL_VAL** | The numbered preset stored on the WLED | +| | | | controller. The `preset` is only applied when |^ +| | | | a strip is enabled, either through the `on` or |^ +| | | | `toggle` actions. |^ +| `brightness` | int | **CURRENT_VAL** | Changes the `brightness` of the LEDs on the | +| | | | strip. The permitted range is 1-255. |^ +| `intensity` | int | **CURRENT_VAL** | Changes the `intensity` value of the current | +| | | | preset. The permitted range is 0-255. This |^ +| | | | setting is ignored if no preset is active. |^ +| `speed` | int | **CURRENT_VAL** | Changes the `speed` value of the current | +| | | | preset. The permitted range is 0-255. This |^ +| | | | setting is ignored if no preset is active. |^ + +| Action | Description | +| --------- | --------------------------------------------------------- | +| `on` | Enable the strip. The `on` action may be accompanied | +| | by one or more of the `preset`, `brightness`, `intensity` |^ +| | or `speed` parameters, which will be applied immediately. |^ +| `off` | Disable the strip. | +| `toggle` | Toggle the strip's enabled state. | +| `control` | Modify `brightness`, `intensity`, and/or `speed` without | +| | changing the current enabled state. At least one of the |^ +| | these parameters must be provided when the action is |^ +| | `control`. |^ +{ #wled-action-desc } WLED Action + +//// Note +When a strip is enabled the `brightness`, `intensity`, and `speed` +values will be reset to the preset's default values. +//// + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "lights": { + "strip": "lights", + "status": "on", + "chain_count": 79, + "preset": 1, + "brightness": 50, + "intensity": 255, + "speed": 255, + "error": null + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | -------------------------------------------------- | +| *strip* | object | An object containing the requested strip's current | +| | | status. The key is the strip's name, the value is |^ +| | | an [WLED strip status](#wled-strip-status-spec) |^ +| | | object. |^ + +/// + +## Sensor endpoints + +The endpoints in this section are available when at least one +`[sensor ]` section has been configured in `moonraker.conf`. + +### Get Sensor List + +```{.http .apirequest title="HTTP Request"} +GET /server/sensors/list?extended=False +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.sensors.list", + "params": { + "extended": false + } + "id": 5646 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :--: | ------- | ---------------------------------------- | +| `extended` | bool | false | When set to `true` the status for each | +| | | | sensor will include `parameter_info` and |^ +| | | | `history_fields` fields. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "sensors": { + "sensor1": { + "id": "sensor1", + "friendly_name": "Sensor 1", + "type": "mqtt", + "values": { + "value1": 0, + "value2": 119.8 + }, + "parameter_info": [ + { + "units": "kWh", + "name": "value1" + }, + { + "units": "V", + "name": "value2" + } + ], + "history_fields": [ + { + "field": "power_consumption", + "provider": "sensor sensor1", + "description": "Printer Power Consumption", + "strategy": "delta", + "units": "kWh", + "init_tracker": true, + "exclude_paused": false, + "report_total": true, + "report_maximum": true, + "precision": 6, + "parameter": "value1" + }, + { + "field": "max_voltage", + "provider": "sensor sensor1", + "description": "Maximum voltage", + "strategy": "maximum", + "units": "V", + "init_tracker": true, + "exclude_paused": false, + "report_total": false, + "report_maximum": false, + "precision": 6, + "parameter": "value2" + } + ] + } + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------- | :----: | -------------------------------------------- | +| `sensors` | object | An object containing the sensor status. | +| | | Each key will be the sensor's ID, each |^ +| | | value will be a |^ +| | | [sensor status](#sensor-status-spec) object. |^ + +| Field | Type | Description | +| ---------------- | :------: | ------------------------------------------- | +| `id` | string | The sensor's configured ID. | +| `friendly_name` | string | The sensor's configured friendly name. | +| `type` | string | The sensor's configured type. Currently | +| | | only `mqtt` types are supported. |^ +| `values` | object | A `Sensor Values` object reporting the | +| | | most recent values measured by the sensor. |^ +| | | #sensor-values-spec |+ +| `parameter_info` | [object] | An array of `Parameter Info` objects. Only | +| | | included with `extended` responses. |^ +| `history_fields` | [object] | An array of `History Field` objects. Only | +| | | reported with `extended` responses. Will |^ +| | | be an empty list if no history fields are |^ +| | | configured for the sensor. |^ +{ #sensor-status-spec } Sensor Status + +| Field | Type | Description | +| ------------ | :--: | ----------------------------------------------- | +| *value_name* | any | The object may contain multiple `values`, where | +| | | each key is the name of a parameter tracked |^ +| | | by the sensor, and the value is the most |^ +| | | recent reported measurement. |^ +{ #sensor-values-spec } Sensor Values + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------------- | +| `name` | string | The name of a parameter measured by the sensor. | +| _custom_ | string | The `parameter_info` object may contain additional | +| | | custom fields provided in the sensor's configuration. |^ +| | | It is common for a sensor to add a `units` field |^ +| | | specifying the type of data measured by the sensor. |^ +{ #sensor-parameter-info-spec } Parameter Info + +| Field | Type | Description | +| ---------------- | :------------: | ---------------------------------------------------- | +| `field` | string | The name of the auxiliary field to be stored in | +| | | the [job history](./history.md). |^ +| `provider` | string | The object providing data for history | +| | | tracking. Will be the sensor's config |^ +| | | section name, ie: `sensor my_sensor`. |^ +| `description` | string | A brief description of the measurement. | +| `strategy` | string | The [strategy](#sensor-history-strategy) used to | +| | | track data stored in the job history. |^ +| `units` | string \| null | The units, if applicable, for the value stored | +| | | in history. |^ +| `init_tracker` | bool | When `true` the first value tracked will be | +| | | initialized to the most recent sensor measurement. |^ +| `exclude_paused` | bool | When `true` tracking will exclude measurements | +| | | taken while a job is paused. |^ +| `report_total` | bool | When `true` the final tracked value will be | +| | | accumulated and included in the `history` |^ +| | | component's job totals. |^ +| `report_maximum` | bool | When `true` the maximum final tracked value during a | +| | | job will be included in the `history` component's |^ +| | | job totals. |^ +| `precision` | int | The precision of the final tracked value, presuming | +| | | it is a float. |^ +| `parameter` | string | The `name` of the sensor parameter to track. | +{ #sensor-history-field-spec } History Fields + +| Strategy | Description | +| ------------ | -------------------------------------------------------- | +| `basic` | Stores the last value measured during a job. | +| `delta` | Stores the difference between the last and first values | +| | measured during a job. |^ +| `accumulate` | Stores the cumulative value of all measurements reported | +| | during the job. |^ +| `average` | Stores an average of all measurements taken during the | +| | job. |^ +| `maximum` | Stores the maximum value measured during the job. | +| `minimum` | Stores the minimum value measured during the job. | +| `collect` | Stores all values measured during the job in an array. | +{ #sensor-history-strategy } History Tracking Strategy + +/// + +### Get Sensor Information +Returns the status for a single configured sensor. + +```{.http .apirequest title="HTTP Request"} +GET /server/sensors/info?sensor=sensor1&extended=false +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.sensors.info", + "params": { + "sensor": "sensor1", + "extended": false + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | ---------------------------------------- | +| `sensor` | string | **REQUIRED** | The ID of the requested sensor. | +| `extended` | bool | false | When set to `true` the status for the | +| | | | sensor will include `parameter_info` and |^ +| | | | `history_fields` fields. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "id": "sensor1", + "friendly_name": "Sensor 1", + "type": "mqtt", + "values": { + "value1": 0.0, + "value2": 120.0 + }, + "parameter_info": [ + { + "units": "kWh", + "name": "value1" + }, + { + "units": "V", + "name": "value2" + } + ], + "history_fields": [ + { + "field": "power_consumption", + "provider": "sensor sensor1", + "description": "Printer Power Consumption", + "strategy": "delta", + "units": "kWh", + "init_tracker": true, + "exclude_paused": false, + "report_total": true, + "report_maximum": true, + "precision": 6, + "parameter": "value1" + }, + { + "field": "max_voltage", + "provider": "sensor sensor1", + "description": "Maximum voltage", + "strategy": "maximum", + "units": "V", + "init_tracker": true, + "exclude_paused": false, + "report_total": false, + "report_maximum": false, + "precision": 6, + "parameter": "value2" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +The response specification is a [Sensor Status](#sensor-status-spec) object. + +/// + +### Get Sensor Measurements +Returns all recorded measurements for a configured sensor. + +```{.http .apirequest title="HTTP Request"} +GET /server/sensors/measurements?sensor=sensor1 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.sensors.measurements", + "params": { + "sensor": "sensor1" + }, + "id": 4564 +} +``` +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | ---------------------------------------- | +| `sensor` | string | **REQUIRED** | The ID of the requested sensor. | + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "sensor1": { + "value1": [ + 3.1, + 3.2, + 3.0 + ], + "value2": [ + 120.0, + 120.0, + 119.9 + ] + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :----: | -------------------------------------------------- | +| *sensor_id* | object | A [Sensor Measurements](#sensor-measurements-spec) | +| | | object. The key for this item will be the sensor |^ +| | | id. |^ + +| Field | Type | Description | +| ------------ | :------------: | -------------------------------------------- | +| *param_name* | [float \| int] | An array of decimal numbers containing all | +| | | stored measurements for the named parameter. |^ +| | | There may be multiple items in this |^ +| | | object, where they keys are parameter names. |^ +{ #sensor-measurements-spec } Sensor Measurements + +/// + +### Get Batch Sensor Measurements +Returns recorded measurements for all sensors. + +```{.http .apirequest title="HTTP Request"} +GET /server/sensors/measurements +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.sensors.measurements", + "id": 4564 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "sensor1": { + "value1": [ + 3.1, + 3.2, + 3.0 + ], + "value2": [ + 120.0, + 120.0, + 119.9 + ] + }, + "sensor2": { + "value_a": [ + 1, + 1, + 0 + ] + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :----: | -------------------------------------------------- | +| *sensor_id* | object | A [Sensor Measurements](#sensor-measurements-spec) | +| | | object. There may be multiple sensor items, where | +| | | the keys are sensor IDs. |^ + +/// + +## MQTT Endpoints + +Moonraker supports `mqtt` connections for communicating with other +devices on the network. In addition to the [power](#power-endpoints) +and [sensor](#sensor-endpoints) implementations Moonraker provides +endpoints for clients to publish and subscribe to topics on the +network. These endpoints are available when `[mqtt]` has been configured +in `moonraker.conf`. + +/// Note +These endpoints are not available over the `mqtt` transport as they +are redundant. MQTT clients can publish and subscribe to +topics directly. +/// + +### Publish a topic + +```{.http .apirequest title="HTTP Request"} +POST /server/mqtt/publish +Content-Type: application/json + +{ + "topic": "home/test/pub", + "payload": "hello", + "qos": 0, + "retain": false, + "timeout": 5 +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.mqtt.publish", + "params":{ + "topic": "home/test/pub", + "payload": "hello", + "qos": 0, + "retain": false, + "timeout": 5 + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| --------- | :----: | ------------------ | ---------------------------------------- | +| `topic` | string | **REQUIRED** | The topic to publish to the network. | +| `payload` | any | null | The payload to send with the topic. | +| | | | May be a boolean, float, integer, |^ +| | | | object, or array. Objects and Arrays |^ +| | | | are JSON encoded. When this parameter |^ +| | | | is omitted an empty payload is sent. |^ +| `qos` | int | **CONFIG_DEFAULT** | The QOS level to use when publishing a | +| | | | topic. Valid range is 0-2. |^ +| `retain` | bool | false | When set to `true` the topic's retain | +| | | | flag is set. |^ +| `timeout` | float | null | A timeout, in seconds, in which | +| | | | Moonraker will wait for acknowledgement |^ +| | | | from the broker. If the timeout is |^ +| | | | exceeded the request will return with a |^ +| | | | 504 error. Only applies to QOS levels 1 |^ +| | | | 2. When omitted the request will wait |^ +| | | | indefinitely. |^ + +//// tip +The `retain` flag tells the broker to save, or "retain", the payload +associated with the topic. Only the most recent payload published +to the topic is retained. Subsequent subscribers to the topic will +immediately receive the retained payload. + +To clear a retained value of a topic, publish the topic with an empty +payload and `retain` set to `true`. +//// + +/// + +```{.json .apiresponse title="Example Response"} +{ + "topic": "home/test/pub" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :----: | ------------------------------------------ | +| `topic` | string | The topic that was successfully published. | + +/// + +### Subscribe to a topic + + +```{.http .apirequest title="HTTP Request"} +POST /server/mqtt/subscribe +Content-Type: application/json + +{ + "topic": "home/test/sub", + "qos": 0, + "timeout": 5 +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.mqtt.subscribe", + "params":{ + "topic": "home/test/sub", + "qos": 0, + "timeout": 5 + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| --------- | :----: | ------------------ | ------------------------------------------ | +| `topic` | string | **REQUIRED** | The topic to subscribe to. Wildcards | +| | | | are **not** allowed. |^ +| `qos` | int | **CONFIG_DEFAULT** | The QOS level to use for the subscription. | +| | | | Valid range is 0-2. |^ +| `timeout` | float | null | A timeout, in seconds, to wait until a | +| | | | response is received. The request will |^ +| | | | return with a 504 error if the timeout |^ +| | | | is exceeded. By default the request will |^ +| | | | wait indefinitely. |^ + +/// + +/// note +If the topic was previously published with a retained payload this request +will return immediately with the retained value. +/// + +```{.json .apiresponse title="Example Response"} +{ + "topic": "home/test/pub", + "payload": "test" +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------- | :-------------------------------: | ------------------------------------ | +| `topic` | string | The name of the topic subscribed to. | +| `payload` | string \| object \| array \| null | The payload received with the topic. | + +//// note +If the `payload` contains a JSON value it will decoded and +set as an object or array before re-encoding the full response back +to JSON. Otherwise it will be a string or null if the +payload is empty. +//// + +/// diff --git a/docs/external_api/extensions.md b/docs/external_api/extensions.md new file mode 100644 index 0000000..edec684 --- /dev/null +++ b/docs/external_api/extensions.md @@ -0,0 +1,277 @@ +# Extensions + +Moonraker has limited support for 3rd party extensions through the +use of its API. Extensions must establish a Websocket or Unix Socket +connection and [identify](./server.md#identify-connection) themselves +as an `agent`. + +The endpoints in this section can be broken down into two categories: + +- Endpoints used by Front Ends and other clients to manage and manipulate + extensions. +- Endpoints specific to agents that provide functional enhancements not + available to other client types. + +## Extension Management + +### List Extensions + +Returns a list of all available extensions. Currently Moonraker can only +be officially extended through connected `agents`. + +```{.http .apirequest title="HTTP Request"} +GET /server/extensions/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.extensions.list", + "id": 4564 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "agents": [ + { + "name": "moonagent", + "version": "0.0.1", + "type": "agent", + "url": "https://github.com/arksine/moontest" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :------: | --------------------------------------------------- | +| `agents` | [object] | An array of [Agent Info](#agent-info-spec) objects. | + +| Field | Type | Description | +| --------- | :----: | -------------------------------------------------- | +| `name` | string | The name provided by the registered agent. | +| `version` | string | The version of the software reported by the agent. | +| `type` | string | The client type. Will always be `agent`. | +| `url` | string | A url to the agent software's webpage. | +{ #agent-info-spec } Agent Info + +/// + +### Call an extension method + +This endpoint may be used to call a method on a connected agent. +The request effectively relays a JSON-RPC request from a front end +or other client to the agent. Agents should document their +available methods so Moonraker client developers can interact +with them. + +```{.http .apirequest title="HTTP Request"} +POST /server/extensions/request +Content-Type: application/json + +{ + "agent": "moonagent", + "method": "moontest.hello_world", + "arguments": {"argone": true, "argtwo": 9000} +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.extensions.request", + "params":{ + "agent": "moonagent", + "method": "moontest.hello_world", + "arguments": {"argone": true, "argtwo": 9000} + }, + "id": 4564 +} +``` + +Parameters: + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------- | :-------------: | ------------ | -------------------------------- | +| `agent` | string | **REQUIRED** | The name of the registered agent | +| | | | hosting the requested method. |^ +| `method` | string | **REQUIRED** | The name of the method to call. | +| `arguments` | array \| object | null | The arguments to send with the | +| | | | method. This may be an array |^ +| | | | containing positional arguments |^ +| | | | or an object containing keyword |^ +| | | | arguments. A value of `null` |^ +| | | | will omit arguments from the |^ +| | | | request. |^ + +/// + +/// api-response-spec + open: True + +The result received from the agent will be returned directly. See +the agent's documentation for response specifications + +/// + +## Agent specific endpoints + +/// Note +These endpoints are only available to connections that have +identified themselves as an `agent` type. +/// + +### Send an agent event + +Sends a [JSON-RPC notification](./jsonrpc_notifications.md#agent-events) +containing the supplied event info to all of Moonraker's persistent +connections. + +```{.http .apirequest title="HTTP Request"} +Not Available +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "connection.send_event", + "params":{ + "event": "my_event", + "data": {"my_arg": "optional data"} + } +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------- | :----: | ------------ | -------------------------------------------- | +| `event` | string | **REQUIRED** | The name of the event. This may be any | +| | | | name other than those reserved by Moonraker. |^ +| `data` | any | undefined | The data to send with the event. This can be | +| | | | any valid JSON decodable value. If omitted |^ +| | | | no data is sent with the event. |^ + +//// Note +The `connected` and `disconnected` events are reserved for use +by Moonraker and may not be sent from agents. +//// + +/// + +```{.apiresponse title="Response when JSON-RPC 'id' present"} +ok +``` + +/// note +An agent may send an event without specifying the JSON-RPC `id` field. +In this case Moonraker will not return a response. +/// + + +### Register a method with Klipper + +Registers a "remote method" with Klipper that can be called +from GCode Macros. + +```{.http .apirequest title="HTTP Request"} +Not Available +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "connection.register_remote_method", + "params": { + "method_name": "firemon_alert_heated" + } +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------------- | :----: | ------------ | --------------------------------------------- | +| `method_name` | string | **REQUIRED** | The name of the remote method to register | +| | | | with Klipper. It is recommended for agents |^ +| | | | to use a unique identifier, such as a prefix, |^ +| | | | to prevent collisions with other remote |^ +| | | | methods registered with Klipper. |^ + +/// + +```{.apiresponse title= Response"} +ok +``` + +/// Note +Methods registered by agents will persist until the agent disconnects. +Upon connection it is only necessary that they register their desired +methods once. +/// + +#### Remote Method Example + +Presume an application named `firemon` has connected to Moonraker's websocket +and identified itself as an `agent`. After identification it registers a +remote method named `firemon_alert_heated` using the JSON-RPC request +example above. + +In addition, the user has following `gcode_macro` configured in `printer.cfg`: + +```ini +# printer.cfg + +[gcode_macro ALERT_HEATED] +gcode: + {% if not params %} + {action_call_remote_method("firemon_alert_heated")} + {% else %} + {% set htr = params.HEATER|default("unknown") %} + {% set tmp = params.TEMP|default(0)|float %} + {action_call_remote_method( + "firemon_alert_heated", heater=htr, temp=tmp)} + {% endif %} + + +``` + +When the `ALERT_HEATED HEATER=extruder TEMP=200` gcode is executed by Klipper, +the agent will receive the following JSON-RPC request from Moonraker: + +```{.json .apiresponse title="Remote Method Call"} +{ + "jsonrpc": "2.0", + "method": "firemon_alert_heated", + "params": { + "heater": "extruder", + "temp": 200 + } +} +``` + +When the `ALERT_HEATED` gcode is executed with no parameters, the agent will +receive the following JSON-RPC request from Moonraker: + +```{.json .apiresponse title="Remote Method Call"} +{ + "jsonrpc": "2.0", + "method": "monitor_alert_heated" +} +``` + +/// Note +Remote methods called from Klipper never contain the JSON-RPC "id" field, +as Klipper does not accept return values to remote methods. +/// diff --git a/docs/external_api/file_manager.md b/docs/external_api/file_manager.md new file mode 100644 index 0000000..5b2df6d --- /dev/null +++ b/docs/external_api/file_manager.md @@ -0,0 +1,1214 @@ +# File Management + +Most file operations are available over both HTTP and JSON-RPC APIs, +however file transfers (upload and download) are exclusive to the HTTP +API. + +Moonraker organizes local directories into "roots". For example, +`gcodes` are located at `http://host/server/files/gcodes/*`, otherwise known +as the "gcodes" root. The following default roots are generally available: + +- gcodes +- config +- logs (read-only) +- config_examples (Klipper Configuration Examples, read-only) +- docs (Klipper Documentation, read-only) + +Write operations (upload, delete, make directory, remove directory) are +only available on the `gcodes` and `config` roots, however it is possible +for users to configure the `config` root to be read-only. + +Many endpoints return permission information on files and/or folders. +Permissions are represented as a string value in the following format: + +| Value | Description | +| ------ | ------------------------------------ | +| `"r"` | Item is read-only. | +| `"rw"` | Item has read and write permissions. | +| `""` | Item is not accessible. | +{ #file-permissions-desc } Permissions + +## List available files + +Walks through a directory and fetches all detected files. +File names include a path relative to the specified `root`. + +/// Tip +In most scenarios it will likely be preferable to request files +by [directory](#get-directory-information) as opposed to listing +the entire root. +/// + +```{.http .apirequest title="HTTP Request"} +GET /server/files/list?root=config +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.list", + "params": { + "root": "config" + }, + "id": 4644 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| ------ | :----: | -------- | --------------------------------------------- | +| `root` | string | `gcodes` | The name of the `root` from which a file list | +| | | | should be returned. |^ + +//// Note +The `gcodes` root will only return files with valid gcode file extensions. +//// +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +[ + { + "path": "3DBenchy_0.15mm_PLA_MK3S_2h6m.gcode", + "modified": 1615077020.2025201, + "size": 4926481, + "permissions": "rw" + }, + { + "path": "Shape-Box_0.2mm_PLA_Ender2_20m.gcode", + "modified": 1614910966.946807, + "size": 324236, + "permissions": "rw" + }, + { + "path": "test_dir/A-Wing.gcode", + "modified": 1605202259, + "size": 1687387, + "permissions": "rw" + }, + { + "path": "test_dir/CE2_CubeTest.gcode", + "modified": 1614644445.4025, + "size": 1467339, + "permissions": "rw" + }, + { + "path": "test_dir/V350_Engine_Block_-_2_-_Scaled.gcode", + "modified": 1615768477.5133543, + "size": 189713016, + "permissions": "rw" + } +] +``` +/// + +/// api-response-spec + open: True + +The result is an array of `File Info` objects: + +| Field | Type | Description | +| ------------- | :----: | ---------------------------------------------------------------- | +| `path` | string | The path of the file, relative to the requested root. | +| `modified` | float | The last modified date in Unix Time (seconds). | +| `size` | int | The size of the file in bytes. | +| `permissions` | string | The available [permissions](#file-permissions-desc) of the file. | +{ #file-info-spec} File Info + +/// + +## List registered roots + +Reports information about "root" directories registered with Moonraker. + +```{.http .apirequest title="HTTP Request"} +GET /server/files/roots +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.roots", + "id": 4644 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +[ + { + "name": "config", + "path": "/home/pi/printer_data/config", + "permissions": "rw" + }, + { + "name": "logs", + "path": "/home/pi/printer_data/logs", + "permissions": "r" + }, + { + "name": "gcodes", + "path": "/home/pi/printer_data/gcodes", + "permissions": "rw" + }, + { + "name": "config_examples", + "path": "/home/pi/klipper/config", + "permissions": "r" + }, + { + "name": "docs", + "path": "/home/pi/klipper/docs", + "permissions": "r" + } +] +``` +/// + +/// api-response-spec + open: True + +The result is an array of `Root Info` objects: + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------------------ | +| `name` | string | The name of the registered root. | +| `path` | string | The absolute path on disk of the registered root. | +| `permissions` | string | [Permissions](#file-permissions-desc) available on the root. | +{ #root-info-spec } Root Info + +/// + +## Get GCode Metadata + +Get metadata for a specified gcode file. + +```{.http .apirequest title="HTTP Request"} +GET /server/files/metadata?filename=tools/drill.gcode +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.metadata", + "params": { + "filename": "tools/drill.gcode" + }, + "id": 3545 +} +``` +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :--: | ------------ | ---------------------------------------------------------- | +| `filename` | str | **REQUIRED** | The path to the gcode file, relative to the `gcodes` root. | + +/// + +/// collapse-code +```{.json #metadata-example-response .apiresponse title="Example Response"} +{ + "size": 1629418, + "modified": 1706359465.4947228, + "uuid": "473a41d2-15f4-434b-aeb4-ab96eb122bbf", + "slicer": "PrusaSlicer", + "slicer_version": "2.7.1+win64", + "gcode_start_byte": 87410, + "gcode_end_byte": 1618468, + "object_height": 8, + "estimated_time": 5947, + "nozzle_diameter": 0.4, + "layer_height": 0.2, + "first_layer_height": 0.2, + "first_layer_extr_temp": 215, + "first_layer_bed_temp": 60, + "chamber_temp": 50, + "filament_name": "Generic PLA Brown", + "filament_type": "PLA", + "filament_total": 9159.55, + "filament_weight_total": 27.32, + "thumbnails": [ + { + "width": 32, + "height": 32, + "size": 1078, + "relative_path": ".thumbs/hook_x4_0.2mm_PLA_MK3S_1h39m-32x32.png" + }, + { + "width": 400, + "height": 300, + "size": 61576, + "relative_path": ".thumbs/hook_x4_0.2mm_PLA_MK3S_1h39m-400x300.png" + } + ], + "print_start_time": 1706359466.722097, + "job_id": "0000BF", + "filename": "hook_x4_0.2mm_PLA_MK3S_1h39m.gcode" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------------------- | :------: | ------------------------------------------------------------ | +| `size` | int | The gcode file size in bytes. | +| `modified` | float | The last modified time in Unix Time (seconds). | +| `uuid` | string | A unique identifier for the metadata object. | +| `slicer` | string | The name of the slicer software used to slice the file. | +| `slicer_version` | string | The version of the slicer software. | +| `gcode_start_byte` | int | The byte offset in the file where the first gcode command | +| | | is detected. |^ +| `gcode_int_byte` | int | The byte offset in the file where the last gcode command | +| | | is detected. |^ +| `object_height` | float | The height (in mm) of the tallest object in the file. | +| `estimated_time` | float | The estimated time to complete the print, in seconds. | +| `nozzle_diameter` | float | The configured nozzle diameter, in mm. | +| `layer_height` | float | The configured layer height, in mm. | +| `first_layer_height` | float | The configured first layer height in mm. | +| `first_layer_extr_temp` | float | The configured first layer extruder temperature, in Celsius. | +| `first_layer_bed_temp` | float | The configured first layer bed temperature, in Celsius. | +| `chamber_temp` | float | The configured chamber temperature, in Celsius. | +| `filament_name` | str | The name of the filament used. | +| `filament_type` | str | The type of filament used, ie: `PLA`. | +| `filament_total` | float | The total length filament used in mm. | +| `filament_weight_total` | float | The total weight of filament used in grams. | +| `thumbnails` | [object] | A list of `Thumbnail Info` objects. | +| | | #thumbnail-info-spec |+ +| `job_id` | string? | The last `history` job ID associated with the gcode. | +| | | Will be `null` if no job has been associated with the file. |^ +| `print_start_time` | float | The most recent start time the gcode file was printed. Will | +| | | be `null` if the file has yet to be printed. |^ +| `filename` | string | Path to the gcode file, relative to the `gcodes` root. | +{ #gcode-metadata-spec } + +| Field | Type | Description | +| --------------- | :----: | --------------------------------------------------------------- | +| `width` | int | The width of the thumbnail in pixels. | +| `height` | int | The height of the thumbnail in pixels. | +| `size` | int | The size of the thumbnail in bytes. | +| `relative_path` | string | The path of the thumbnail, relative to the gcode file's parent. | +{ #thumbnail-info-spec } Thumbnail Info + +//// Note +Metadata field availability depends on the Slicer application and its +configuration. If a field cannot be parsed from the slicer it will +be omitted. +//// + +/// + + +## Scan GCode Metadata + +Initiate a metadata scan for a selected file. If the file has already +been scanned the endpoint will force a re-scan. + +```{.http .apirequest title="HTTP Request"} +POST /server/files/metascan?filename={filename} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.metascan", + "params": { + "filename": "{filename}" + }, + "id": 3545 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :--: | ------------ | ---------------------------------------------------------- | +| `filename` | str | **REQUIRED** | The path to the gcode file, relative to the `gcodes` root. | + +/// + +For an example response refer to the +[Metadata Example Response](#metadata-example-response). + +/// api-response-spec + open: True +The response spec is identical to the +[Metadata Request Specification](#gcode-metadata-spec) +/// + + +## Get GCode Thumbnail Details + +Returns thumbnail information for a supplied gcode file. + +```{.http .apirequest title="HTTP Request"} +GET /server/files/thumbnails?filename=tools/drill.gcode +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.thumbnails", + "params": { + "filename": "{filename}" + }, + "id": 3545 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :--: | ------------ | ---------------------------------------------------------- | +| `filename` | str | **REQUIRED** | The path to the gcode file, relative to the `gcodes` root. | + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +[ + { + "width": 32, + "height": 32, + "size": 1551, + "thumbnail_path": "test/.thumbs/CE2_FanCover-120mm-Mesh-32x32.png" + }, + { + "width": 300, + "height": 300, + "size": 31819, + "thumbnail_path": "test/.thumbs/CE2_FanCover-120mm-Mesh.png" + } +] +``` +/// + +/// api-response-spec + open: True + +The result is an array of `Thumbnail Details` objects. + +| Field | Type | Description | +| ---------------- | :----: | --------------------------------------------------------- | +| `width` | int | The width of the thumbnail in pixels. | +| `height` | int | The height of the thumbnail in pixels. | +| `size` | int | The size of the thumbnail in bytes. | +| `thumbnail_path` | string | The path of the thumbnail, relative to the `gcodes` root. | +{ #thumbnail-details-spec } Thumbnail Details + +//// Note +The `Thumbnails Details` spec is nearly identical to the +[Thumbnail Info](#thumbnail-info-spec) spec reported in +a [metadata request](#get-gcode-metadata), with one exception. +The `thumbnail_path` field in the result above contains a +path relative to the `gcodes` root, whereas the `relative_path` +field reported in the `Thumbnail Info` is relative to the gcode +file's parent folder. +//// + +/// + + +## Get directory information + +Returns a list of files and subdirectories given a supplied path. +Unlike `/server/files/list`, this command does not walk through +subdirectories. This request will return all files in a directory, +including files in the `gcodes` root that do not have a valid gcode +extension. + +```{.http .apirequest title="HTTP Request"} +GET /server/files/directory?path=gcodes/my_subdir&extended=true +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.get_directory", + "params": { + "path": "gcodes/my_subdir", + "extended": true + }, + "id": 5644 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | -------- | --------------------------------------------------- | +| `path` | string | `gcodes` | Path to the directory. The first part must be a | +| | | | registered root. |^ +| `extended` | bool | `false` | When set to `true` metadata will be included in the | +| | | | response for gcode file. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "dirs": [ + { + "modified": 1615768162.0412788, + "size": 4096, + "permissions": "rw", + "dirname": "test" + }, + { + "modified": 1613569827.489749, + "size": 4096, + "permissions": "rw", + "dirname": "Cura" + }, + { + "modified": 1615767459.6265886, + "size": 4096, + "permissions": "rw", + "dirname": "thumbs" + } + ], + "files": [ + { + "modified": 1615578004.9639666, + "size": 7300692, + "permissions": "rw", + "filename": "Funnel_0.2mm_PLA_Ender2_2h4m.gcode" + }, + { + "modified": 1589156863.9726968, + "size": 4214831, + "permissions": "rw", + "filename": "CE2_Pi3_A+_CaseLID.gcode" + }, + { + "modified": 1615030592.7722695, + "size": 2388774, + "permissions": "rw", + "filename": "CE2_calicat.gcode" + } + ], + "disk_usage": { + "total": 7522213888, + "used": 4280369152, + "free": 2903625728 + }, + "root_info": { + "name": "gcodes", + "permissions": "rw" + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------ | :------: | ------------------------------------------------------- | +| `dirs` | [object] | An array of `Directory Info` objects. Will be empty if | +| | | no sub-directories are found. |^ +| | | #directory-info-spec |+ +| `files` | [object] | An array of `File Info` objects. Will be empty if no | +| | | files are found. |^ +| | | #dir-req-file-info-spec |+ +| `disk_usage` | object | A `Disk Usage` object. This provides disk usage details | +| | | about the underlying storage media containing the |^ +| | | requested directory. |^ +| | | #disk-usage-spec |+ +| `root_info` | object | A `Root Info` object. Provides details about the | +| | | directory's root parent. |^ +| | | #root-info-spec |+ + + +| Field | Type | Description | +| ------------- | :----: | --------------------------------------------------------------------- | +| `modified` | float | The last modified date in Unix Time (seconds). | +| `size` | int | The size of the file in bytes. | +| `permissions` | string | The available [permissions](#file-permissions-desc) of the directory. | +| `dirname` | string | The name of the directory. | +{ #directory-info-spec } Directory Info + + +| Field | Type | Description | +| ----------------- | :----: | ---------------------------------------------------------------------- | +| `modified` | float | The last modified date in Unix Time (seconds). | +| `size` | int | The size of the file in bytes. | +| `permissions` | string | The available [permissions](#file-permissions-desc) of the directory. | +| `filename` | string | The name of the file. | +| _metadata-fields_ | _any_ | When the `extended` parameter is set to true all available metadata | +| | | fields are included in the object. See the |^ +| | | [metadata response spec](#gcode-metadata-spec) for details. Note that |^ +| | | the `filename` in the metadata spec, which is a relative path, will | +| | | not overwrite the `filename` above, which is not a path. | +{ #dir-req-file-info-spec } File Info + +| Field | Type | Description | +| ------- | :--: | ---------------------------------------- | +| `free` | int | The amount of free space in bytes. | +| `used` | int | The amount of used data in bytes. | +| `total` | int | The total capacity of the disk in bytes. | +{ #disk-usage-spec } Disk Usage + +| Field | Type | Description | +| ------------- | :----: | ---------------------------------------------------------- | +| `name` | string | The name of the root node for the requested directory. | +| `permissions` | string | The available [permissions](#file-permissions-desc) of the | +| | | root node. |^ +{ #root-info-spec } Root Info + +/// + +## Create directory + +Creates a directory at the specified path. + +```{.http .apirequest title="HTTP Request"} +POST /server/files/directory +Content-Type: application/json + +{ + "path": "gcodes/my_new_dir" +} +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.post_directory", + "params": { + "path": "gcodes/my_new_dir" + }, + "id": 6548 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------------ | ---------------------------------------------------------- | +| `path` | string | **REQUIRED** | The path to the directory to create, including its `root`. | +| | | | Note that the parent directory must exist. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "item": { + "path": "my_new_dir", + "root": "gcodes", + "modified": 1676983427.3732708, + "size": 4096, + "permissions": "rw" + + }, + "action": "create_dir" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------------------- | +| `item` | object | An `Item Details` object describing the directory created. | +| | | #create-dir-item-details-spec |+ +| `action` | string | A description of the action taken by the host. Will always | +| | | be `create_dir` for this request. |^ + +| Field | Type | Description | +| ------------- | :----: | ---------------------------------------------------- | +| `path` | string | The path of the new directory, relative to the root. | +| `root` | string | The root node the directory was created under. | +| `modified` | float | The last modified date in Unix Time (seconds). | +| `size` | int | The size of the directory. Will generally be 4096. | +| `permissions` | string | Permissions available on the new directory. | +{ #create-dir-item-details-spec } Item Details + +/// + +## Delete directory +Deletes a directory at the specified path. + +```{.http .apirequest title="HTTP Request"} +DELETE /server/files/directory?path=gcodes/my_subdir&force=false +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.delete_directory", + "params": { + "path": "gcodes/my_subdir", + "force": false + }, + "id": 6545 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------- | :----: | ------------ | ----------------------------------------------------------- | +| `path` | string | **REQUIRED** | The path to the directory to delete, including its `root`. | +| | | | Note that the directory must be empty if `force` is `false` |^ +| `force` | bool | `false` | When set to `true` the directory and all of its contents | +| | | | will be deleted. |^ + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "item": { + "path": "my_subdir", + "root": "gcodes", + "modified": 0, + "size": 0, + "permissions": "" + + }, + "action": "delete_dir" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------------------- | +| `item` | object | An `Item Details` object describing the directory deleted. | +| | | #delete-dir-item-details-spec |+ +| `action` | string | A description of the action taken by the host. Will always | +| | | be `delete_dir` for this request. |^ + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------------------- | +| `path` | string | The path of the deleted directory, relative to the root. | +| `root` | string | The root node the directory existed under prior to removal. | +| `modified` | float | The last modified date in Unix Time (seconds). Should be | +| | | 0 if the delete was successful. |^ +| `size` | int | The size of the removed directory. Should be 0 if the delete | +| | | was successful. |^ +| `permissions` | string | Permissions available on the removed directory. Should be | +| | | an empty string if the delete was successful. |^ +{ #delete-dir-item-details-spec } Item Details + +/// + +## Move a file or directory + +Moves a file or directory from one location to another. The following +conditions must be met for a move successful move: + +- The source item must exist. +- The user that owns the Moonraker process must have the appropriate + file permissions. +- Neither the source nor destination can be loaded by Klipper's `virtual_sdcard`. + If the source is a directory, it must not contain a file loaded by the + `virtual_sdcard`. + +When specifying the `source` and `dest`, the `root` directory should be +prefixed. Currently the only supported roots for `dest` are `gcodes`" +and `config`". + +This endpoint may also be used to rename a file or directory. Be aware that an +attempt to rename a directory to a directory that exists with the same name will +*move* the source directory into the destination directory. Also be aware +that renaming a file to a file that already exists will overwrite the existing +file. + +```{.http .apirequest title="HTTP Request"} +POST /server/files/move +Content-Type: application/json + +{ + "source": "gcodes/orig_dir/my_file.gcode", + "dest": "gcodes/new_dir/my_file.gcode" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.move", + "params": { + "source": "gcodes/orig_dir/my_file.gcode", + "dest": "gcodes/new_dir/my_file.gcode" + }, + "id": 5664 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :----: | ------------ | --------------------------------------------------- | +| `source` | string | **REQUIRED** | The source file or directory to move. | +| | | | This is a path that must start with the root node. |^ +| `dest` | string | **REQUIRED** | The destination path. The path must start with the | +| | | | root node. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "item": { + "root": "gcodes", + "path": "subdir/my_file.gcode", + "modified": 1676940082.8595376, + "size": 384096, + "permissions": "rw" + }, + "source_item": { + "path": "testdir/my_file.gcode", + "root": "gcodes" + }, + "action": "move_file" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------------- | +| `item` | object | A `Destination Item` object. | +| | | #move-dest-item-spec |+ +| `source_item` | object | A `Source Item` object. | +| | | #move-source-item-spec |+ +| `action` | string | A description of the action taken. Will be `move_file` | +| | | if a file was moved or `move_dir` if a directory was |^ +| | | moved. |^ + +| Field | Type | Description | +| ------------- | :----: | --------------------------------------------------------- | +| `root` | string | The root node of the destination file or directory. | +| `path` | string | The path, relative to the root node, of the destination | +| | | file or directory. |^ +| `modified` | float | The last modified time of the destination file or | +| | | directory. This is expressed in Unix Time (seconds). |^ +| `size` | int | The size, in bytes, of the destination file or directory. | +| `permissions` | string | The permissions available on the destination file or | +| | | directory. |^ +{ #move-dest-item-spec } Destination Item + +| Field | Type | Description | +| ------ | :----: | ------------------------------------------------------------- | +| `root` | string | The root node of the source file or directory that was moved. | +| `path` | string | The path, relative to the root node, of the source file or | +| | | directory that was moved. |^ +{ #move-source-item-spec } Source Item + +/// + + +## Copy a file or directory + +Copies a file or directory from one location to another. A successful copy has +the same prerequisites as a move with one exception, a copy may complete if the +source file or directory is loaded by the `virtual_sdcard`. + +```{.http .apirequest title="HTTP Request"} +POST /server/files/copy +Content-Type: application/json + +{ + "source": "gcodes/my_file.gcode", + "dest": "gcodes/new_dir/my_file.gcode" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.copy", + "params": { + "source": "gcodes/my_file.gcode", + "dest": "gcodes/new_dir/my_file.gcode" + }, + "id": 5623 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :----: | ------------ | --------------------------------------------------- | +| `source` | string | **REQUIRED** | The source file or directory to copy. | +| | | | This is a path that must start with the root node. |^ +| `dest` | string | **REQUIRED** | The destination path. The path must start with the | +| | | | root node. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "item": { + "root": "gcodes", + "path": "subdir/my_file.gcode", + "modified": 1676940082.8595376, + "size": 384096, + "permissions": "rw" + }, + "action": "create_file" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ------------------------------------------------------- | +| `item` | object | A `Destination Item` object. | +| | | #copy-dest-item-spec |+ +| `action` | string | A description of the action taken. Expand for details. | +| | | #copy-action-desc |+ + +| Field | Type | Description | +| ------------- | :----: | --------------------------------------------------------- | +| `root` | string | The root node of the destination file or directory. | +| `path` | string | The path, relative to the root node, of the destination | +| | | file or directory. |^ +| `modified` | float | The last modified time of the destination file or | +| | | directory. This is expressed in Unix Time (seconds). |^ +| `size` | int | The size, in bytes, of the destination file or directory. | +| `permissions` | string | The permissions available on the destination file or | +| | | directory. |^ +{ #copy-dest-item-spec } Destination Item + +| Name | Description | +| ------------- | ------------------------------------------------------- | +| `create_file` | A new file was created by the copy operation. | +| `modify_file` | An existing file was modified (overwritten) by the copy | +| | operation. |^ +| `create_dir` | A new directory was created by the copy operation. In | +| | addition, children files and directories may have been |^ +| | created. |^ +{ #copy-action-desc } +/// + +## Create a ZIP archive + +Creates a `zip` file consisting of one or more files. + +```{.http .apirequest title="HTTP Request"} +POST /server/files/zip +Content-Type: application/json + +{ + "dest": "config/error_logs.zip", + "items": [ + "config/printer.cfg", + "logs", + "gcodes/subfolder" + ], + "store_only": false +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.zip", + "params": { + "dest": "config/error_logs.zip", + "items": [ + "config/printer.cfg", + "logs", + "gcodes/subfolder" + ], + "store_only": false + }, + "id": 5623 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------------ | :------: | ----------------------------------- | ------------------------------------------ | +| `dest` | string | `config/collection-{timestamp}.zip` | Path to the destination archive file. | +| | | | The path must begin with a valid "root" |^ +| | | | that has write permission. |^ +| `items` | [string] | **REQUIRED** | An array of paths indicating the items | +| | | | to be included in the archive. Each |^ +| | | | path must start with a valid root. An |^ +| | | | item may be a file or directory. |^ +| `store_only` | bool | `false` | When set to `true` the contents of the zip | +| | | | archive are not compressed. Otherwise the |^ +| | | | `deflation` algorithm will be used to |^ +| | | | compress the contents. |^ + + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "destination": { + "root": "config", + "path": "error_logs.zip", + "modified": 1676984423.8892415, + "size": 420, + "permissions": "rw" + }, + "action": "zip_files" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | ---------------------------------------------------------- | +| `destination` | object | A `Zip Destination` object containing details about the | +| | | archived file. |^ +| | | #zip-destination-spec |+ +| `action` | string | The action taken be the file manager. Will be `zip_files`. | + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------------------------- | +| `root` | string | The root node of the destination file or directory. | +| `path` | string | The path of the zip archive, relative to the `root`. | +| `modified` | float | The last modified time in unix time. | +| `size` | int | The size of the file in bytes. | +| `permissions` | string | The available [permissions](#file-permissions-desc) of the archive. | +{ #zip-destination-spec } Zip Destination + +/// + +## File download +Retrieves file `filename` at root `root`. The `filename` must include +the relative path if it is not in the root folder. + +```{.http .apirequest title="HTTP Request"} +GET /server/files/{root}/{filename} +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// api-response-spec + open: True + +The body of the response contains the contents of the requested file. + +/// + +## File upload +Upload a file. Currently files may be uploaded to the `gcodes` or `config` +roots, with `gcodes` being the default. If one wishes to upload +to a subdirectory, the path may be added to the upload's file name +(relative to the root). If the directory does not exist an error will be +returned. Alternatively, the `path` form argument may be set, as explained +below. + +```{.http .apirequest title="HTTP Request"} +POST /server/files/upload` +Content-Type: multipart/form-data + +------FormBoundaryemap3PkuvKX0B3HH +Content-Disposition: form-data; name="file"; filename="myfile.gcode" +Content-Type: application/octet-stream + + +------FormBoundaryemap3PkuvKX0B3HH-- +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// api-parameters + open: True + +The file data must be included in the request's body as `multipart/form-data` +(ie: ``). The following *optional* arguments may also be +added to the form-data: + +| Name | Type | Default | Description | +| ---------- | :----: | -------- | -------------------------------------------------------------- | +| `root` | string | `gcodes` | The root location in which to upload the file. Currently | +| | | | this may only be `gcodes` or `config`. |^ +| `path` | string | | An optional path, relative to the `root`, indicating a | +| | | | subfolder in which to save the file. If the subfolder does |^ +| | | | not exist it will be created. |^ +| `checksum` | string | | An optional SHA256 hex digest calculated by the client for | +| | | | the uploaded file. If this argument is supplied the server |^ +| | | | will compare it to its own checksum calculation after the |^ +| | | | upload has completed. A checksum mismatch will result in a |^ +| | | | 422 error. |^ +| `print` | string | `false` | Available only for files uploaded to the `gcodes` root. When | +| | | | set to `true` Moonraker will command Klippy to start the print |^ +| | | | after the upload has successfully completed. |^ + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "item": { + "path": "Lock Body Shim 1mm_0.2mm_FLEX_MK3S_2h30m.gcode", + "root": "gcodes", + "modified": 1676984527.636818, + "size": 71973, + "permissions": "rw" + }, + "print_started": false, + "print_queued": false, + "action": "create_file" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------------- | :----: | --------------------------------------------------------------------- | +| `item` | object | An `Uploaded Item` object. | +| | | #uploaded-item-spec |+ +| `print_started` | bool | Set to `true` if the uploaded file has successfully started printing. | +| `print_queued ` | bool | Set to `true` if the uploaded file has been queued for printing | +| | | at a later time. |^ +| `action` | string | Action taken by the file manager. Will always be "create_file". | + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------------------------- | +| `path` | string | The path of the uploaded file, relative to the `root`. | +| `root` | string | The root node of the destination file or directory. | +| `modified` | float | The last modified time in unix time. | +| `size` | int | The size of the file in bytes. | +| `permissions` | string | The available [permissions](#file-permissions-desc) of the archive. | +{ #uploaded-item-spec } Uploaded Item + +In addition to the above returned object, all successful uploads will respond with +a 201 response code and set the `Location` response header to the full path of +the uploaded file. +/// + +## File delete +Delete a file in the requested root. If the file exists in a subdirectory, +its relative path must be part of the `{filename}` argument. + +```{.http .apirequest title="HTTP Request"} +DELETE /server/files/{root}/{filename} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.files.delete_file", + "params": { + "path": "{root}/{filename}" + }, + "id": 1323 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "item": { + "path": "Lock Body Shim 1mm_0.2mm_FLEX_MK3S_2h30m.gcode", + "root": "gcodes", + "size": 0, + "modified": 0, + "permissions": "" + }, + "action": "delete_file" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | --------------------------------------------------------------- | +| `item` | object | A `Deleted Item` object. | +| | | #deleted-item-spec |+ +| `action` | string | Action taken by the file manager. Will always be "delete_file". | + +| Field | Type | Description | +| ------------- | :----: | -------------------------------------------------------------------- | +| `path` | string | The path of the deleted file, relative to the `root`. | +| `root` | string | The root node of the deleted file or directory. | +| `modified` | float | The last modified time in unix time. Should always be 0 as the file | +| | | no longer exists. |^ +| `size` | int | The size of the file in bytes. Should always be 0 as the file no | +| | | longer exits. |^ +| `permissions` | string | The available [permissions](#file-permissions-desc) of the archive. | +| | | should always be an empty string as the file no longer exists. |^ +{ #deleted-item-spec } Deleted Item + +/// + +## Download klippy.log + +/// Note +Logs are now available in the `logs` root. Front ends should consider +presenting all available logs using "file manager" type of UI. That said, +If Klipper has not been configured to write logs in the `logs` root then +this endpoint is available as a fallback. +/// + +```{.http .apirequest title="HTTP Request"} +GET /server/files/klippy.log +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// api-response-spec + open: True + +The body of the response contains contents of `klippy.log`. + +/// + +## Download moonraker.log + +/// Note +Logs are now available in the `logs` root. Front ends should consider +presenting all available logs using "file manager" type of UI. That said, +If Moonraker has not been configured to write logs in the `logs` root then +this endpoint is available as a fallback. +/// + +```{.http .apirequest title="HTTP Request"} +GET /server/files/moonraker.log +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// api-response-spec + open: True + +The body of the response contains the contents of `moonraker.log`. + +/// \ No newline at end of file diff --git a/docs/external_api/history.md b/docs/external_api/history.md new file mode 100644 index 0000000..1d2d1ad --- /dev/null +++ b/docs/external_api/history.md @@ -0,0 +1,452 @@ +# Job History + +Moonraker's `history` component tracks print job completion data. +The following endpoints are available to manage Moonraker's job +history data. + +## Get job list + +```{.http .apirequest title="HTTP Request"} +GET /server/history/list?limit=50&start=50&since=1&before=5&order=asc +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.history.list", + "params":{ + "limit": 50, + "start": 10, + "since": 464.54, + "before": 1322.54, + "order": "asc" + }, + "id": 5656 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :----: | --------- | ----------------------------------------------- | +| `limit` | int | 50 | Maximum number of job entries to return. | +| `start` | int | 0 | The record number indicating the first entry | +| | | | of the returned list. |^ +| `before` | float | undefined | A timestamp in unix time. When specified, the | +| | | | returned list will only contain entries created |^ +| | | | before this date. |^ +| `since` | float | undefined | A timestamp in unix time. When specified, the | +| | | | returned list will only contain entries created |^ +| | | | after this date. |^ +| `order` | string | "desc" | The order of the list returned. May be `asc` | +| | | | (ascending) or `desc` (descending). |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "count": 1, + "jobs": [ + { + "job_id": "000001", + "exists": true, + "end_time": 1615764265.6493807, + "filament_used": 7.83, + "filename": "test/history_test.gcode", + "metadata": { + // Object containing metadata at time of job + }, + "print_duration": 18.37201827496756, + "status": "completed", + "start_time": 1615764496.622146, + "total_duration": 18.37201827496756, + "user": "testuser", + "auxiliary_data": [ + { + "provider": "sensor hist_test", + "name": "power_consumption", + "value": 4.119977, + "description": "Printer Power Consumption", + "units": "kWh" + }, + { + "provider": "sensor hist_test", + "name": "max_current", + "value": 2.768851, + "description": "Maximum current draw", + "units": "A" + }, + { + "provider": "sensor hist_test", + "name": "min_current", + "value": 0.426725, + "description": "Minimum current draw", + "units": "A" + }, + { + "provider": "sensor hist_test", + "name": "avg_current", + "value": 1.706872, + "description": "Average current draw", + "units": "A" + }, + { + "provider": "sensor hist_test", + "name": "status", + "value": 2, + "description": "Power Switch Status", + "units": null + }, + { + "provider": "sensor hist_test", + "name": "filament", + "value": 19.08058495194607, + "description": "filament usage tracker", + "units": "mm" + }, + { + "provider": "spoolman", + "name": "spool_ids", + "value": [ + 1 + ], + "description": "Spool IDs used", + "units": null + } + ] + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------- | :------: | -------------------------------------------------- | +| `count` | int | The number of entries returned by the query. | +| `jobs` | [object] | An array of [Job History](#job-history-entry-spec) | +| | | objects. |^ + +| Field | Type | Description | +| ---------------- | :------------: | ----------------------------------------------------------- | +| `job_id` | string | A unique ID for the entry. | +| `user` | string \| null | The user that started the job. Will be `null` | +| | | if Moonraker cannot identify a user (ie: job) |^ +| | | was started via Klipper's display. |^ +| `filename` | string | The path, relative to `gcodes` root, of the file | +| | | associated with the job. |^ +| `exists` | bool | A value of `true` indicates that the file | +| | | associated with the job exists on disk and has |^ +| | | not been modified. |^ +| `status` | string | The [job status](#job-history-status-desc) | +| | | at the time of query. |^ +| `start_time` | float | A timestamp, in unix time, indicating when | +| | | the job started. |^ +| `end_time` | float \| null | A timestamp, in unix time, indicating when | +| | | the job finished. Will be `null` if the |^ +| | | job is in progress or if Moonraker is |^ +| | | interrupted prior to the job completion. |^ +| `print_duration` | float | The amount of time, in seconds, the job | +| | | spent printing (ie: printer not idle). |^ +| `total_duration` | float | The total amount of time, in seconds, the | +| | | job took to print. This includes time paused. |^ +| `filament_used` | float | The amount of filament (in mm) used during the job. | +| `metadata` | object | The [gcode metadata](./file_manager.md#gcode-metadata-spec) | +| | | object associated with the job. The `job_id` and |^ +| | | `print_start_time` fields are removed from the metadata as |^ +| | | they are redundant. |^ +| `auxiliary_data` | [object] | An array of [auxiliary field](#job-history-aux-field-spec) | +| | | objects containing supplemental history data related to |^ +| | | the job. |^ +{ #job-history-entry-spec } Job History + +| Field | Type | Description | +| ------------- | :------------: | ------------------------------------------------- | +| `provider` | string | The component or extension that generated the | +| | | auxiliary field. |^ +| `name` | string | A name identifying the field. | +| `description` | string | A brief description of the data in this entry. | +| `value` | any | The value associated with the field. Can be any | +| | | valid JSON type. |^ +| `units` | string \| null | The unit type associated with the value. For | +| | | example this would be `mm` for millimeters. Can |^ +| | | be `null` if no unit abbreviation is appropriate. |^ +{ #job-history-aux-field-spec } Auxiliary Field + +| Status | Description | +| ------------------- | ----------------------------------------------------- | +| `in_progress` | The job is currently active. | +| `completed` | The job successfully completed. | +| `cancelled` | The job was cancelled by the user. | +| `error` | The job was aborted due to an error during execution. | +| `klippy_shutdown` | The job was aborted due to Klippy Shutdown. | +| `klippy_disconnect` | Moonraker's connection to Klippy was lost while the | +| | job was in progress. |^ +| `interrupted` | Moonraker was abruptly terminated while the job was | +| | in progress. |^ +{ #job-history-status-desc } Job Status + +/// +## Get job totals + +```{.http .apirequest title="HTTP Request"} +GET /server/history/totals +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.history.totals", + "id": 5656 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "job_totals": { + "total_jobs": 3, + "total_time": 11748.077333278954, + "total_print_time": 11348.794790096988, + "total_filament_used": 11615.718840001999, + "longest_job": 11665.191012736992, + "longest_print": 11348.794790096988 + }, + "auxiliary_totals": [ + { + "provider": "sensor hist_test", + "field": "power_consumption", + "maximum": 4.119977, + "total": 4.119977 + }, + { + "provider": "sensor hist_test", + "field": "avg_current", + "maximum": 1.706872, + "total": null + }, + { + "provider": "sensor hist_test", + "field": "filament", + "maximum": 19.08058495194607, + "total": 19.08058495194607 + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------------ | :------: | ----------------------------------------------- | +| `job_totals` | object | A [Job Totals](#job-history-totals-spec) object | +| | | reporting all current totals. |^ +| `auxiliary_totals` | [object] | An array of | +| | | [Auxiliary Total](#job-auxiliary-totals-spec) |^ +| | | objects. |^ + +| Field | Type | Description | +| --------------------- | :------: | ------------------------------------------------------ | +| `total_jobs` | int | The total number of jobs tracked. | +| `total_time` | float | The total amount of job work time (in seconds) | +| | | across all jobs, including time paused. |^ +| `total_print_time` | float | The total amount of time printing (in seconds) | +| | | across all jobs. |^ +| `total_filament_used` | float | The total amount of filament used (in mm) across | +| | | all jobs. |^ +| `longest_job` | float | The maximum time spent working on a single job, | +| | | including time paused. |^ +| `longest_print` | float | The maximum time spent printing a single job. | +| `auxiliary_totals` | [object] | An array of | +| | | [Auxiliary Total](#job-auxiliary-totals-spec) objects. |^ +{ #job-history-totals-spec } Job Totals + +| Field | Type | Description | +| ---------- | :-----------: | ----------------------------------------------- | +| `provider` | string | The component or extension that generated the | +| | | auxiliary totals. |^ +| `field` | string | The corresponding `name` of the auxiliary field | +| | | used to generate totals. |^ +| `maximum` | float \| null | The maximum value observed across all prints. | +| | | Will be `null` if the maximum is not available. |^ +| `total` | float \| null | The accumulated total value across all prints. | +| | | Will be `null` if the total is not available. |^ +{ #job-auxiliary-totals-spec } Auxiliary Total + +/// + +## Reset totals +Resets the persistent "job totals" to zero. + +```{.http .apirequest title="HTTP Request"} +POST /server/history/reset_totals +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.history.reset_totals", + "id": 5534 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "last_totals": { + "total_jobs": 3, + "total_time": 11748.077333278954, + "total_print_time": 11348.794790096988, + "total_filament_used": 11615.718840001999, + "longest_job": 11665.191012736992, + "longest_print": 11348.794790096988 + }, + "last_auxiliary_totals": [ + { + "provider": "sensor hist_test", + "field": "power_consumption", + "maximum": 4.119977, + "total": 4.119977 + }, + { + "provider": "sensor hist_test", + "field": "avg_current", + "maximum": 1.706872, + "total": null + }, + { + "provider": "sensor hist_test", + "field": "filament", + "maximum": 19.08058495194607, + "total": 19.08058495194607 + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------------------- | :------: | ----------------------------------------------- | +| `last_totals` | object | A [Job Totals](#job-history-totals-spec) object | +| | | reporting all totals prior to the reset. |^ +| `last_auxiliary_totals` | [object] | An array of | +| | | [Auxiliary Total](#job-auxiliary-totals-spec) |^ +| | | objects reporting totals prior to the reset. |^ + +/// + +## Get a single job + +```{.http .apirequest title="HTTP Request"} +GET /server/history/job?uid= +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.history.get_job", + "params":{"uid": "{uid}"}, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----- | :----: | ------------ | --------------------------------------- | +| `uid` | string | **REQUIRED** | The unique identifier for the requested | +| | | | job history. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "job": { + "job_id": "000001", + "exists": true, + "end_time": 1615764265.6493807, + "filament_used": 7.83, + "filename": "test/history_test.gcode", + "metadata": { + // Object containing metadata at time of job + }, + "print_duration": 18.37201827496756, + "status": "completed", + "start_time": 1615764496.622146, + "total_duration": 18.37201827496756 + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----- | :----: | ------------------------------------------------------------- | +| `job` | object | The requested [Job History](#job-history-entry-spec ) object. | + +/// + +## Delete a job + +```{.http .apirequest title="HTTP Request"} +DELETE /server/history/job?uid= +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.history.delete_job", + "params":{ + "uid": "{uid}" + }, + "id": 5534 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----- | :----: | --------------- | ------------------------------------------ | +| `uid` | string | **REQUIRED** | The unique identifier for the job entry | +| | | if `all==false` | to delete. |^ +| `all` | bool | false | When set to `true` all job history entries | +| | | | will be removed. |^ + +//// tip +If `all = true` is specified the `uid` parameter should be omitted. +//// + +/// + +```{.json .apiresponse title="Example Response"} +{ + "deleted_jobs": [ + "000000", + "000001" + ] +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------------- | :------: | ------------------------------------- | +| `deleted_jobs` | [string] | An array of unique IDs indicating the | +| | | job entries that were deleted. |^ + +/// diff --git a/docs/external_api/integrations.md b/docs/external_api/integrations.md new file mode 100644 index 0000000..2fa8b87 --- /dev/null +++ b/docs/external_api/integrations.md @@ -0,0 +1,782 @@ +# Third Party Integrations + +## Apprise Notifier + +Moonraker supports configurable push notifications using the +[apprise](https://github.com/caronc/apprise) library. The +endpoints in this section may be used to manage/view registered +notifiers. + +The following endpoints are available when at least one +`[notifier ]` section has been configured in `moonraker.conf`. + +### List Notifiers + +```{.http .apirequest title="HTTP Request"} +GET /server/notifiers/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.notifiers.list", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "notifiers": [ + { + "name": "print_start", + "url": "tgram://{bottoken}/{ChatID}", + "events": [ + "started" + ], + "body": "Your printer started printing '{event_args[1].filename}'", + "title": null, + "attach": null + }, + { + "name": "print_complete", + "url": "tgram://{bottoken}/{ChatID}", + "events": [ + "complete" + ], + "body": "Your printer completed printing '{event_args[1].filename}", + "title": null, + "attach": "http://192.168.1.100/webcam/?action=snapshot" + }, + { + "name": "print_error", + "url": "tgram://{bottoken}/{ChatID}", + "events": [ + "error" + ], + "body": "{event_args[1].message}", + "title": null, + "attach": "http://192.168.1.100/webcam/?action=snapshot" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :------: | ---------------------------------------------------- | +| `notifiers` | [object] | An array of [Notifier Status](#notifier-status-spec) | +| | | objects. |^ + +| Field | Type | Description | +| -------- | :------------: | ---------------------------------------------------- | +| `name` | string | The configured name of the notifier. | +| `url` | string | The notifier's destination url. | +| `events` | [string] | An array that contains one or more | +| | | [events](#notifier-event-desc) which will trigger |^ +| | | the push notification. |^ +| `body` | string \| null | The content to send in the body of the notification. | +| | | Will be `null` if no body is configured. |^ +| `title` | string \| null | The title of the notification. Will be `null` if no | +| | | title is configured |^ +| `attach` | string \| null | One or more attachments added to the notification. | +| | | Multiple attachments are separated by newlines. Will |^ +| | | be `null` if no attachment is configured. |^ +{ #notifier-status-spec } Notifier Status + +| Event | Description | +| ----------- | ------------------------------------------ | +| `standby` | The printer has entered its standby state. | +| `started` | A print job has started. | +| `paused` | A print job has paused. | +| `resumed` | A print job has resumed. | +| `complete` | A print job has successfully finished. | +| `error` | A print job exited with an error. | +| `cancelled` | A print job was cancelled by the user. | +{ #notifier-event-desc } Available Notifier Events + +//// note +The `url`, `body`, `title`, and `attach` parameters may contain Jinja 2 +templates. All templates are evaluated before the notification is +pushed. +//// + +/// + +### Test a notifier (debug) + +Forces a registered notifier to push a notification. + +/// note +This endpoint is only available when Moonraker's debug +features are enabled and should not be implemented +in production code +/// + + +```{.http .apirequest title="HTTP Request"} +POST /debug/notifiers/test +Content-Type: application/json + +{ + "name": "notifier_name" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "debug.notifiers.test", + "params": { + "name": "notifier_name" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------------ | --------------------------------- | +| `name` | string | **REQUIRED** | The name of the notifier to test. | + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "status": "success", + "stats": { + "print_duration": 0.0, + "total_duration": 0.0, + "filament_used": 0.0, + "filename": "notifier_test.gcode", + "state": "standby", + "message": "", + "info": { + "total_layer": null, + "current_layer": null + } + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------- | +| `status` | string | The status of the test result. Currently | +| | | will always be `success`. |^ +| `stats` | object | A [Print Stats](#print-stats-spec) object.| | + +| Field | Type | Description | +| ---------------- | :----: | -------------------------------------------------- | +| `print_duration` | float | Time spent printing the current job in seconds. | +| | | Does not include time paused. |^ +| `total_duration` | float | Total job duration in seconds. | +| `filament_used` | float | Amount of filament used for the current job in mm. | +| `filename` | string | File path of the current job, relative to the | +| | | `gcodes` root. |^ +| `state` | string | The current job [state](#print-stats-state-desc). | +| `message` | string | A status message set by Klipper. Will be an empty | +| | | string if no message is set. |^ +| `info` | object | A `Print Stats Supplemental Info` object. | +| | | #print-stats-supplemental-info-spec |+ +{#print-stats-spec} Print Stats + +| Field | Type | Description | +| --------------- | :---------: | -------------------------------------------- | +| `total_layer` | int \| null | The total layer count of the current | +| | | job. Will be null if the total layer |^ +| | | count is not set. |^ +| `current_layer` | int \| null | The index of the layer the job is currently | +| | | printing. Will be null of the current layer |^ +| | | is not set. |^ +{#print-stats-supplemental-info-spec} Print Stats Supplemental Info + +| State | Description | +| ----------- | ---------------------------------------------- | +| `standby` | The printer is standing by for a job to begin. | +| `printing` | A job is currently printing. | +| `paused` | A print job has paused. | +| `complete` | A print job has successfully finished. | +| `error` | A print job exited with an error. | +| `cancelled` | A print job was cancelled by the user. | +{ #print-stats-state-desc } Print Stats State + +/// + +## Spoolman + +[Spoolman](https://github.com/Donkie/Spoolman) is a spool +tracking web service that can manage spool data across +multiple printers. Moonraker has support for updating and retrieving +spool data through its `[spoolman]` integration. + +The following endpoints are available when the `[spoolman]` component +has been configured. + +### Get Spoolman Status + +Returns the current status of the spoolman module. + +```{.http .apirequest title="HTTP Request"} +GET /server/spoolman/status +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.spoolman.status", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "spoolman_connected": false, + "pending_reports": [ + { + "spool_id": 1, + "filament_used": 10 + } + ], + "spool_id": 2 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------------------- | :---------: | ---------------------------------------------- | +| `spoolman_connected` | bool | Indicates that Moonraker has an established | +| | | websocket connection to Spoolman. |^ +| `pending_reports` | [object] | An array of `Pending Spoolman Report` objects. | +| | | A pending report is a report that has not yet |^ +| | | been sent to Spoolman. This may be because |^ +| | | Spoolman is not available or because the |^ +| | | current batch of reports are waiting for the |^ +| | | internal report timer to schedule them. |^ +| | | #spoolman-report-spec |+ +| `spool_id` | int \| null | The ID of the currently tracked spool. A value | +| | | of `null` indicates that no spool ID is set |^ +| | | and tracking is disabled. |^ + +| Field | Type | Description | +| --------------- | :---: | --------------------------------------------- | +| `spool_id` | int | The ID of the spool with pending report data. | +| `filament_used` | float | The amount of used filament to report in mm. | +{ #spoolman-report-spec } Pending Reports + +/// + +### Set active spool + +Set the active ID of the spool to track filament usage and report +to Spoolman. + +```{.http .apirequest title="HTTP Request"} +POST /server/spoolman/spool_id +Content-Type: application/json + +{ + "spool_id": 1 +} +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.spoolman.post_spool_id", + "params": { + "spool_id": 1 + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :---------: | ------- | ---------------------------------- | +| `spool_id` | int \| null | null | The new active spool ID. A `null` | +| | | | value will unset the previous |^ +| | | | active spool and disable tracking. |^ + +/// + +```{.json .apiresponse title="Example Response"} +{ + "spool_id": 1 +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :---------: | ---------------------------------------------- | +| `spool_id` | int \| null | The ID of the currently tracked spool. A value | +| | | of `null` indicates that no spool ID is set |^ +| | | and tracking is disabled. |^ + +/// + +### Get active spool +Retrieve the ID of the spool to which Moonraker reports usage for Spoolman. + +```{.http .apirequest title="HTTP Request"} +GET /server/spoolman/spool_id +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.spoolman.get_spool_id", + "id": 4654 +} +``` + +```{.json .apiresponse title="Example Response"} +{ + "spool_id": 1 +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------- | :---------: | ---------------------------------------------- | +| `spool_id` | int \| null | The ID of the currently tracked spool. A value | +| | | of `null` indicates that no spool ID is set |^ +| | | and tracking is disabled. |^ + +/// + +### Proxy + +Proxy an API request to the Spoolman Server. + +See Spoolman's [OpenAPI Description](https://donkie.github.io/Spoolman/) for +detailed information about it's API. + +/// Note +The version 2 response has been added to eliminate ambiguity between +Spoolman errors and Moonraker errors. With version 1 a frontend +is not able to reliably to determine if the error is sourced from +Spoolman or Moonraker. Version 2 responses will return success +unless Moonraker is the source of the error. + +The version 2 response is currently opt-in to avoid breaking +existing implementations, however in the future it will be +required, at which point the version 1 response will be removed. +The version 1 response is now deprecated. +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/spoolman/proxy +Content-Type: application/json + +{ + "request_method": "POST", + "path": "/v1/spool", + "query": "a=1&b=4", + "body": { + "filament_id": 1 + } +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.spoolman.proxy", + "params": { + "use_v2_response": true, + "request_method": "POST", + "path": "/v1/spool", + "query": "a=1&b=4", + "body": { + "filament_id": 1 + } + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------------- | :----: | ------------ | ---------------------------------------- | +| `use_v2_response` | bool | false | When set to `true` the request will | +| | | | return a version 2 response. |^ +| `request_method` | string | **REQUIRED** | The HTTP request method of the API | +| | | | call to proxy. |^ +| `path` | string | **REQUIRED** | The path section of the API endpoint to | +| | | | proxy. It must include the version, ie: |^ +| | | | `/v1/filament` |^ +| `query` | string | null | An optional query string component of | +| | | | the URL to proxy. A `null` value |^ +| | | | will omit the query string. |^ +| `body` | object | null | An optional body containing request | +| | | | parameters for the API call. This |^ +| | | | should be a JSON encodable object. |^ +| | | | A `null` value will send an empty body. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Success Response (Version 2)"} +{ + "response": { + "id": 2, + "registered": "2023-11-23T12:18:31Z", + "first_used": "2023-11-22T12:17:56.123000Z", + "last_used": "2023-11-23T10:17:59.900000Z", + "filament": { + "id": 2, + "registered": "2023-11-23T12:17:44Z", + "name": "Reactor Red", + "vendor": { + "id": 2, + "registered": "2023-06-26T21:00:42Z", + "name": "Fusion" + }, + "material": "PLA", + "price": 25, + "density": 1.24, + "diameter": 1.75, + "weight": 1000, + "color_hex": "BD0B0B" + }, + "remaining_weight": 950, + "used_weight": 50, + "remaining_length": 318519.4384459262, + "used_length": 16764.18097083822, + "archived": false + }, + "error": null +} +``` +/// + +/// collapse-code +```{.json .apiresponse title="Example Error Response (Version 2)"} +{ + "response": null, + "error": { + "status_code": 404, + "message": "No spool with ID 3 found." + } +} +``` +/// + +/// api-response-spec + open: True + +//// Note +Version 1 responses are proxied directly. See Spoolman's API +documentation for response specifications. Errors are also +proxied directly. +//// + +| Field | Type | Description | +| ---------- | :------------: | ------------------------------------------- | +| `response` | object \| null | On success will be an object containing the | +| | | response received from Spoolman. Will be |^ +| | | `null` if an error is received. |^ +| `error` | object \| null | On error will be a `Spoolman Error` object. | +| | | Will be `null` on successful requests. |^ +| | | #spoolman-error-spec |+ +{ #version2-success-spec} Version 2 response + +| Field | Type | Description | +| ------------- | :----: | --------------------------------------------- | +| `status_code` | int | The HTTP status code of the response. | +| `message` | string | The error message received with the response. | +{ #spoolman-error-spec} Spoolman Error + +/// + + +## OctoPrint API emulation + +Supports the minimal API requirements necessary to add compatibility +with the `upload G-Code to OctoPrint` feature present on 3rd party +applications, such as slicers. Developers of Moonraker applications +*should not* implement these APIs. + +These endpoints are available when the `[octoprint_compat]` feature +has been configured in `moonraker.conf` + +/// tip +Most slicers now support Moonraker's native upload interface, +reducing the need for these endpoints. +/// + +/// note +Unlike all other Moonraker responses, OctoPrint responses are +not wrapped in an object with a `result` field. This section +will not include parameter and response specifications, they +can be found in OctoPrint's API documentation. + +In addition, many values in the responses returned by Moonraker +are simply placeholders and have no real meaning with regard +to Moonraker's internal state. +/// + +### Version information + +```{.http .apirequest title="HTTP Request"} +GET /api/version +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "server": "1.5.0", + "api": "0.1", + "text": "OctoPrint (Moonraker v0.3.1-12)" +} +``` +/// + +### Server status + +```{.http .apirequest title="HTTP Request"} +GET /api/server +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "server": "1.5.0", + "safemode": "settings" +} +``` +/// + +### Login verification & User information + +```{.http .apirequest title="HTTP Request"} +GET /api/login +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "_is_external_client": false, + "_login_mechanism": "apikey", + "name": "_api", + "active": true, + "user": true, + "admin": true, + "apikey": null, + "permissions": [], + "groups": ["admins", "users"] +} +``` +/// + +### Get settings + +```{.http .apirequest title="HTTP Request"} +GET /api/settings +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "plugins": { + "UltimakerFormatPackage": { + "align_inline_thumbnail": false, + "inline_thumbnail": false, + "inline_thumbnail_align_value": "left", + "inline_thumbnail_scale_value": "50", + "installed": true, + "installed_version": "0.2.2", + "scale_inline_thumbnail": false, + "state_panel_thumbnail": true + } + }, + "feature": { + "sdSupport": false, + "temperatureGraph": false + }, + "webcam": { + "flipH": false, + "flipV": false, + "rotate90": false, + "streamUrl": "/webcam/?action=stream", + "webcamEnabled": true + } +} +``` +/// + +/// note +The webcam route in the response is hardcoded to Fluidd/Mainsail +default path. The UFP plugin reports that it is installed so the +Cura-OctoPrint plugin will upload in the preferred UFP format. +/// + +### OctoPrint File Upload + +```{.http .apirequest title="HTTP Request"} +POST /api/files/local +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +Alias for Moonraker's [file upload API](./file_manager.md#file-upload). + +### Get Job status + +```{.http .apirequest title="HTTP Request"} +GET /api/job +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "job": { + "file": {"name": null}, + "estimatedPrintTime": null, + "filament": {"length": null}, + "user": null + }, + "progress": { + "completion": null, + "filepos": null, + "printTime": null, + "printTimeLeft": null, + "printTimeOrigin": null + }, + "state": "Offline" +} +``` +/// + +### Get Printer status + +```{.http .apirequest title="HTTP Request"} +GET /api/printer +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "temperature": { + "tool0": { + "actual": 22.25, + "offset": 0, + "target": 0 + }, + "bed": { + "actual": 22.25, + "offset": 0, + "target": 0 + }, ... + }, + "state": { + "text": "state", + "flags": { + "operational": true, + "paused": false, + "printing": false, + "cancelling": false, + "pausing": false, + "error": false, + "ready": false, + "closedOrError": false + } + } +} +``` +/// + +### Send GCode command + +```{.http .apirequest title="HTTP Request"} +POST /api/printer/command +Content-Type: application/json + +{ + "commands": ["G28"] +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +```{.json .apiresponse title="Example Response"} +{} +``` + +### List Printer profiles + +```{.http .apirequest title="HTTP Request"} +GET /api/printerprofiles +``` + +```{.json .apirequest title="JSON-RPC Request"} +Not Available +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "profiles": { + "_default": { + "id": "_default", + "name": "Default", + "color": "default", + "model": "Default", + "default": true, + "current": true, + "heatedBed": true, + "heatedChamber": false + } + } +} +``` +/// diff --git a/docs/external_api/introduction.md b/docs/external_api/introduction.md new file mode 100644 index 0000000..f9c2530 --- /dev/null +++ b/docs/external_api/introduction.md @@ -0,0 +1,510 @@ +# Introduction + +Moonraker provides APIs over two protocols, HTTP and JSON-RPC. Most +endpoints have corresponding APIs over both protocols. When requests +are exclusive to a protocol or depend on some other condition it will +be noted in the documentation for that specific API. + +At a high level, file transfer requests (upload and download) are exclusive +to the HTTP API. The Websocket is required to receive events such as status +updates and gcode responses. For information on how to set up the Websocket, +please see the [Miscellaneous Tutorials](#websocket-setup). + +## HTTP API Overview + +Moonraker's HTTP API could be described as "REST-ish". Attempts are made to +conform to REST standards, however the dynamic nature of Moonraker's endpoint + registration along with the need to keep consistency between two API protocols +results in an HTTP API that deviates + +Moonraker is capable of parsing request arguments from the both the body +(either JSON or form-data depending on the `Content-Type` header) and from +the query string. All arguments are grouped together in one data structure, +with body arguments taking precedence over query arguments. Thus +if the same argument is supplied both in the body and in the +query string the body argument would be used. It is left up to the front end +developer to decide exactly how they want to provide arguments. + +Nearly all successful HTTP requests will return a json encoded object in +the form of: + +```text +{ + "result": +} +``` + +The response data may be any valid JSON type. In most cases it will be +a JSON object itself, but some requests may return a simple string. + +If the response is not wrapped in an object with a `result` field it will +be noted in the documentation for that API (or API set). Generally this +only applies to endpoints that attempt to emulate other backends. + +Should a request result in an error, a standard error code along with +an error specific message is returned, wrapped in a JSON object. + +### Query string type hints + +By default all arguments passed via the query string are represented as +strings. Most endpoint handlers know the data type for each of their +arguments, thus they can perform conversion from a string type if necessary. +However some endpoints accept arguments of a "generic" type, thus the +client is responsible for specifying the type if "string" is not desirable. +This is not a problem for websocket requests as the JSON parser can extract +the appropriate type. HTTP requests must provide "type hints" in these +scenarios. Moonraker supplies support for the following query string type hints: +- int +- bool +- float +- json +The `json` type hint can be specified to pass an array or an object via +the query string. Remember to percent encode the json string so that +the query string is correctly parsed. + +Type hints may be specified by post-fixing them to a key, with a ":" +separating the key and the hint. For example, lets assume that we +have a request that takes `seconds` (integer) and `enabled` (boolean) +arguments. The query string with type hints might look like: +``` +?seconds:int=120&enabled:bool=true +``` +A query string that takes a `value` argument with which we want to +pass an object, `{foo: 21.5, bar: "hello"}` might look like: +``` +?value:json=%7B%22foo%22%3A21.5%2C%22bar%22%3A%22hello%22%7D +``` +As you can see, a percent encoded json string is not human readable, +thus using this functionality should be seen as a "last resort." If at +all possible clients should attempt to put these arguments in the body +of a request. + +## JSON-RPC API Overview + +Websocket, Unix Socket, and MQTT connections exclusively use the API +available over [JSON-RPC 2.0](https://jsonrpc.org). In addition, Moonraker +provides an [JSON-RPC HTTP endpoint](#json-rpc-over-http-endpoint) giving +developers who want to avoid persistent connections a choice to use JSON-RPC. + +The Websocket transmits and receives JSON-RPC objects in text frames. MQTT +transmits them in the payload of a topic defined when MQTT is configured. By +default, Moonraker receives JSON-RPC requests from the +`{instance_name}/moonraker/api/request` topic, and publishes responses to the +`{instance_name}/moonraker/api/response` topic. The `{instance_name}` must be +a unique identifier for each instance of Moonraker connected to the broker. +It defaults to the machine's host name. + +An encoded request should look something like: +```json +{ + "jsonrpc": "2.0", + "method": "API method", + "params": {"arg_one": 1, "arg_two": true}, + "id": 354 +} +``` + +The `params` field may be left out if the API request takes no arguments. +The `id` should be a unique value that has no chance of colliding +with other JSON-RPC requests. The `method` is the API method, as defined +for each API in this document. + +/// details | Optional MQTT timestamp + type: tip +MQTT requests may provide an optional `mqtt_timestamp` keyword +argument in the `params` field of the JSON-RPC request. To avoid +potential collisions from time drift it is recommended to specify +the timestamp in microseconds since the Unix Epoch. If provided +Moonraker will use the timestamp to discard duplicate requests. +It is recommended to either provide a timestamp or publish API +requests at a QoS level of 0 or 2. +/// + +A successful request will return a response like the following: +```json +{ + "jsonrpc": "2.0", + "result": {"res_data": "success"}, + "id": 354 +} +``` +The `result` will generally contain an object, but as with the HTTP API in some +cases it may simply return a string. The `id` field will return an id that +matches the one provided by the request. + +Requests that result in an error will receive a properly formatted +JSON-RPC response: +```json +{ + "jsonrpc": "2.0", + "error": {"code": 36000, "message": "Error Message"}, + "id": 354 +} +``` +Some errors may not return a request ID, such as an improperly formatted request. + +The [moontest](https://www.github.com/arksine/moontest) repo includes a basic +test interface with example usage for most of the requests below. It also +includes a basic JSON-RPC implementation that uses promises to return responses +and errors (see json-rpc.js). + +## Websocket Connections + +### Primary websocket + +The primary websocket supports Moonraker's JSON-RPC API. Most applications that +desire a websocket connection will make use of the primary websocket. + +The primary websocket is available at: +``` + ws://host_or_ip:port/websocket` +``` + +The primary websocket will remain connected until the application disconnects +or Moonraker is shutdown. + +### Bridge websocket + +The "bridge" websocket provides a near direct passthrough to Klipper's API +Server. Klipper uses its own RPC protocol, which is effectively a simplified +version of the JSON-RPC specification. Developers should refer to +[Klipper's API documentation](https://www.klipper3d.org/API_Server.html) +for details on the protocol and available APIs. + +!!! Note + The bridge websocket is described as "near direct passthrough" because + Moonraker handles the ETX (`0x03`) terminator internally. Applications + can expect to receive complete JSON encoded messages in a text frame + without the ETX terminator. Likewise applications should send JSON encoded + messages without the ETX terminator. Messages may be sent using either + text frames or binary frames. + +The bridge websocket provides access to diagnostic APIs that are not generally +suitable for Moonraker's primary connection. These requests stream a +substantial amount of data; bridge connections allow Moonraker to avoid +decoding and re-encoding this data, reducing CPU load on the host. The "dump" +requests, such as `motion_report/dump_stepper` and `adxl345/dump_adxl345`, are +examples of APIs that should make use of the bridge websocket. + +The bridge websocket is available at: +``` +ws://host_or_ip:port/klippysocket +``` + +The availability of bridge connections depends on Klippy's availability. +If Klippy is not running or its API server is not enabled then a bridge +websocket connection cannot be established. Established bridge connections +will close when Klippy is shutdown or restarted. Such connections will also +be closed if Moonraker is restarted or shutdown. + +!!! Note + If JWT or API Key authentication is required the application must use a + [oneshot token](./authorization.md#generate-a-oneshot-token) when connecting + to a bridge socket. Since Moonraker does not decode bridge requests it is + not possible to perform JWT authentication post connection. + +## Unix Socket Connection + +All JSON-RPC APIs available over the Websocket transport are also available +over the Unix Domain Socket connection. Moonraker creates the socket file at +`/comms/moonraker.sock` (ie: `~/printer_data/comms/moonraker.sock`). +The Unix Socket expects UTF-8 encoded JSON-RPC byte strings. Each JSON-RPC +request must be terminated with an ETX character (`0x03`). + +The Unix Socket is desirable for front ends and extensions running on the +local machine as authentication is not necessary. There should be a small +performance improvement due to the simplified transport protocol, however +the impact of this is likely negligible. + +The `moontest` repo contains a +[python script](https://github.com/Arksine/moontest/blob/master/scripts/unix_socket_test.py) +to test comms over the unix socket. + +## JSON-RPC over HTTP Endpoint + +Exposes the JSON-RPC interface over HTTP. Most JSON-RPC methods with +corresponding HTTP APIs are available. Methods exclusive to other +transports, such as [Identify Connection](./server.md#identify-connection), are +not available. + +/// note +If authentication is required it must be part of the HTTP request, +either using the API Key Header (`X-Api-Key`) or JWT Bearer Token. +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/jsonrpc +Content-Type: application/json +{ + "jsonrpc": "2.0", + "method": "printer.info", + "id": 5153 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "jsonrpc": "2.0", + "id": 5153, + "result": { + "state": "ready", + "state_message": "Printer is ready", + "hostname": "my-pi-hostname", + "software_version": "v0.9.1-302-g900c7396", + "cpu_info": "4 core ARMv7 Processor rev 4 (v7l)", + "klipper_path": "/home/pi/klipper", + "python_path": "/home/pi/klippy-env/bin/python", + "log_file": "/tmp/klippy.log", + "config_file": "/home/pi/printer.cfg" + } +} +``` +//// + +/// note +If an error is encountered while processing a JSON-RPC request +over HTTP the request itself will still return success. The +error will be returned in the response as a JSON-RPC encoded +object. +/// + +## Jinja2 Template API Calls + +Some template options in Moonraker's configuration, such as those in the +[button](../configuration.md#button) component, may call Moonraker APIs through +the `call_method(method_name, kwargs)` context function. The `call_method` +function takes the API's JSON-RPC method name as its first parameter, followed +by a set of keyword arguments as per the method's requirements. + +```ini +# moonraker.conf + +# Query Printer Objects example +[button check_status] +pin: gpio26 +on_press: + {% set query = {"toolhead": ["position"], "print_stats": None} %} + # JSON-RPC method is "printer.objects.query", which takes a single "objects" + # argument + {% set status = call_method("printer.objects.query", objects=query) %} + # do something with the value returned from the object query, perhaps + # send a websocket notification or publish a mqtt topic + +# Publish button event to MQTT Topic +[button check_status] +pin: gpio26 +on_release: + # JSON-RPC method is "server.mqtt.publish" + {% do call_method("server.mqtt.publish", + topic="moonraker/my-button", + payload="Button Released") %} +``` + +## Miscellaneous Tutorials + +### Websocket setup + +The websocket is located at `ws://host:port/websocket`, for example: +```javascript +var s = new WebSocket("ws://" + location.host + "/websocket"); +``` + +/// Note +It may be necessary to authenticate the connection first. This +tutorial assumes that the client is running on a trusted connection. +/// + +The following is a basic startup sequence that may be used to establish +a full connection to Moonraker and ensure that Klipper is running and +available: + +1. Attempt to connect to `/websocket` until successful using a timer-like + mechanism. +2. Once connected, query the [server info](./server.md#query-server-info) + endpoint to check Klippy's state. + - If the response returns an error then either the client + is not authorized or Moonraker is not running. Direct the user to + SSH into the machine and check ` + ``` + Or via JSON-RPC 2.0: + ```json + { + "jsonrpc": "2.0", + "method": "server.files.metadata", + "params": { + "filename": "{filename}" + }, + "id": 5643 + } + ``` + If metadata extraction failed then this request will return an error. + Some metadata fields are only populated for specific slicers, and + unsupported slicers will only return the size and modified date. + +- There are multiple ways to calculate the ETA, this example will use + file progress, as it is possible calculate the ETA with or without + metadata. + - If `metadata.estimated_time` is available, the eta calculation can + be done as: + ```javascript + // assume "result" is the response from the status query + let vsd = result.status.virtual_sdcard; + let prog_time = vsd.progress * metadata.estimated_time; + let eta = metadata.estimated_time - prog_time + ``` + Alternatively, one can simply subtract the print duration from + the estimated time: + ```javascript + // assume "result" is the response from the status query + let pstats = result.status.print_status; + let eta = metadata.estimated_time - pstats.print_duration; + if (eta < 0) eta = 0; + ``` + - If no metadata is available, print duration and progress can be used to + calculate the ETA: + ```javascript + // assume "result" is the response from the status query + let vsd = result.status.virtual_sdcard; + let pstats = result.status.print_stats; + let total_time = pstats.print_duration / vsd.progress; + let eta = total_time - pstats.print_duration; + ``` + +- It is possible to query additional objects if a client wishes to display + more information (ie: temperatures). See Klipper's + [Status Reference](https://www.klipper3d.org/Status_Reference.html) + documentation for details on objects available for query. + +### Bed Mesh Coordinates + +The [Bed Mesh](../printer_objects.md#bed_mesh) printer object may be used +to generate three dimensional coordinates of a probed area (or mesh). +Below is an example (in javascript) of how to transform the data received +from a bed_mesh object query into an array of 3D coordinates. + +```javascript +// assume that we have executed an object query for bed_mesh and have the +// result. This example generates 3D coordinates for the probed matrix, +// however it would work with the mesh matrix as well +function process_mesh(result) { + let bed_mesh = result.status.bed_mesh; + let matrix = bed_mesh.probed_matrix; + if (!(matrix instanceof Array) || matrix.length < 3 || + !(matrix[0] instanceof Array) || matrix[0].length < 3) + // make sure that the matrix is valid + return; + let coordinates = []; + // calculate the distance between each sample on both the X an Y + // axes + let x_distance = (bed_mesh.mesh_max[0] - bed_mesh.mesh_min[0]) / + (matrix[0].length - 1); + let y_distance = (bed_mesh.mesh_max[1] - bed_mesh.mesh_min[1]) / + (matrix.length - 1); + let x_idx = 0; + let y_idx = 0; + // transform the matrix of z values into (x, y, z) coordinates + for (const x_axis of matrix) { + x_idx = 0; + // mesh_min is the (x, y) coordinate of the first z sample + let y_coord = bed_mesh.mesh_min[1] + (y_idx * y_distance); + for (const z_coord of x_axis) { + let x_coord = bed_mesh.mesh_min[0] + (x_idx * x_distance); + x_idx++; + coordinates.push([x_coord, y_coord, z_coord]); + } + y_idx++; + } +} +// Use the array of coordinates to visualize the "probed area" +// or mesh.. +``` + +### Converting to Unix Time + +Some of Moonraker's APIs return a date represented in Unix time. +Most languages have functionality built in to convert Unix +time to a workable object or string. For example, in JavaScript +one might do something like the following: + +```javascript +for (let resp of result.gcode_store) { + let date = new Date(resp.time * 1000); + // Do something with date and resp.message ... +} +``` diff --git a/docs/external_api/job_queue.md b/docs/external_api/job_queue.md new file mode 100644 index 0000000..a41108c --- /dev/null +++ b/docs/external_api/job_queue.md @@ -0,0 +1,440 @@ +# Job Queue Management + +The following endpoints may be used to manage Moonraker's job queue. +Note that Moonraker's Job Queue is implemented as a FIFO queue and it may +contain multiple references to the same job. + +The queue maintains an internal state attribute, which will always be one +of the following: + +| State | Description | +| ---------- | ---------------------------------------------------------------- | +| `ready` | The queue is active and will load the next queued job upon | +| | completion of the current job. |^ +| `loading` | The queue is currently loading the next job. If the | +| | `job_queue` configuration specifies a `job_transition_delay` |^ +| | and/or a `job_transition_gcode` the queue will remain in the |^ +| | `loading` state until both are completed. |^ +| `starting` | The state reported while the Job Queue is requesting Klipper | +| | to start the print. |^ +| `paused` | When the queue is `paused` it will not load the next queued | +| | job after a working job has completed. The queue will enter |^ +| | this state if a pause is requested through the "pause" endpoint, |^ +| | an error is encountered during the startup or loading phases, or |^ +| | after completion of a job when the `job_queue` configuration |^ +| | specifies that `automatic_transition` is set to false. |^ +{ #queue-state-desc } Queue State + +/// note +All filenames provided to and returned by these endpoints are relative to +the `gcodes` root. +/// + +## Get job queue status + +Retrieves the current state of the job queue. + +```{.http .apirequest title="HTTP Request"} +GET /server/job_queue/status +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.job_queue.status", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "queued_jobs": [ + { + "filename": "job1.gcode", + "job_id": "0000000066D99C90", + "time_added": 1636151050.7666452, + "time_in_queue": 21.89680004119873 + }, + { + "filename": "job2.gcode", + "job_id": "0000000066D991F0", + "time_added": 1636151050.7766452, + "time_in_queue": 21.88680004119873 + }, + { + "filename": "subdir/job3.gcode", + "job_id": "0000000066D99D80", + "time_added": 1636151050.7866452, + "time_in_queue": 21.90680004119873 + } + ], + "queue_state": "ready" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :------: | -------------------------------------------------------- | +| `queued_jobs` | [object] | An array of `Queued Job` objects. | +| | | #queued-job-spec |+ +| `queue_state` | string | The current [state](#queue-state-desc) of the job queue. | +{ #job-queue-status-response-spec } + +| Field | Type | Description | +| --------------- | :----: | ------------------------------------------------------- | +| `filename` | string | The name of the gcode file queued. | +| `job_id` | string | A unique ID assigned to the queued job. | +| `time_added` | float | The time (in Unix Time) the job was added to the queue. | +| `time_in_queue` | float | The cumulative amount of time, in seconds, the job has | +| | | been pending in the queue. |^ +{ #queued-job-spec } Queued Job + +/// + +## Enqueue a job + +Adds a job, or an array of jobs, to the end of the job queue. The same +filename may be specified multiple times to queue a job that repeats. +When multiple jobs are specified they will be enqueued in the order they +are received. + +/// note +The request will be aborted and return an error if any of the supplied +files do not exist. +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/job_queue/job +Content-Type: application/json + +{ + "filenames": [ + "job1.gcode", + "job2.gcode", + "subdir/job3.gcode" + ], + "reset": false +} + +``` +/// tip +If it isn't possible for your client to pass parameters in the body +of the request as a json object, they can be added to the query string +as shown below: + +```{.http .apirequest title="HTTP Request"} +POST /server/job_queue/job?filenames=job1.gcode,job2.gcode,subdir/job3.gcode +``` + +Multiple jobs should be comma separated as shown above. +/// + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.job_queue.post_job", + "params": { + "filenames": [ + "job1.gcode", + "job2.gcode", + "subdir/job3.gcode" + ], + "reset": false + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ----------- | :------: | ------------ | ------------------------------------------------------- | +| `filenames` | [string] | **REQUIRED** | An array of filenames of jobs to add to the queue. | +| | | | The file names should be paths relative to the `gcodes` |^ +| | | | root. All of the specified files must exist, otherwise |^ +| | | | the request will return with an error. |^ +| `reset` | bool | false | When set to `true` the job queue will be | +| | | | cleared prior to adding the requested jobs. |^ + +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "queued_jobs": [ + { + "filename": "job1.gcode", + "job_id": "0000000066D99C90", + "time_added": 1636151050.7666452, + "time_in_queue": 0.01680004119873 + }, + { + "filename": "job2.gcode", + "job_id": "0000000066D991F0", + "time_added": 1636151050.7766452, + "time_in_queue": 0.01480004119873 + }, + { + "filename": "subdir/job3.gcode", + "job_id": "0000000066D99D80", + "time_added": 1636151050.7866452, + "time_in_queue": 0.010680004119873 + } + ], + "queue_state": "ready" +} +``` +/// + +/// api-response-spec + open: True + +See the [Job Queue Status](#job-queue-status-response-spec) +Response Specification. + +/// + +## Remove a Job + +Removes one or more jobs from the queue. + +/// Note +Unlike the POST version of this method, it is not necessary that +all job ids exist. If any supplied job id does not exist in the +queue it will be silently ignored. Clients can verify the contents +of the queue via the return value. +/// + +```{.http .apirequest title="HTTP Request"} +DELETE /server/job_queue/job?job_ids=0000000066D991F0,0000000066D99D80 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.job_queue.delete_job", + "params": { + "job_ids": [ + "0000000066D991F0", + "0000000066D99D80" + ] + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| --------- | :------: | --------------- | ------------------------------------------------ | +| `job_ids` | [string] | **REQUIRED** | An array of `job_ids` to remove from the queue. | +| | | if `all==false` | Any job ids that do not exist will be ignored. |^ +| `all` | bool | false | When set to `true` all jobs will be removed | +| | | | from the queue. In this case it is not necessary |^ +| | | | to set the `job_ids` parameter. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "queued_jobs": [ + { + "filename": "job1.gcode", + "job_id": "0000000066D99C90", + "time_added": 1636151050.7666452, + "time_in_queue": 21.89680004119873 + } + ], + "queue_state": "ready" +} +``` +/// + +/// api-response-spec + open: True + +See the [Job Queue Status](#job-queue-status-response-spec) +Response Specification. + +/// + +## Pause the job queue + +Sets the job queue state to "pause", which prevents the next job +in the queue from loading after an job in progress is complete. +If the queue is paused while the queue is in the `loading` state +the load will be aborted. + +```{.http .apirequest title="HTTP Request"} +POST /server/job_queue/pause +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.job_queue.pause", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "queued_jobs": [ + { + "filename": "job1.gcode", + "job_id": "0000000066D99C90", + "time_added": 1636151050.7666452, + "time_in_queue": 21.89680004119873 + }, + { + "filename": "job2.gcode", + "job_id": "0000000066D991F0", + "time_added": 1636151050.7766452, + "time_in_queue": 21.88680004119873 + }, + { + "filename": "subdir/job3.gcode", + "job_id": "0000000066D99D80", + "time_added": 1636151050.7866452, + "time_in_queue": 21.90680004119873 + } + ], + "queue_state": "paused" +} +``` +/// + +/// api-response-spec + open: True + +See the [Job Queue Status](#job-queue-status-response-spec) +Response Specification. + +/// + +## Start the job queue + +Starts the job queue. If Klipper is ready to start a print the next +job in the queue will be loaded. Otherwise the queue will be put +into the "ready" state, where the job will be loaded after the current +job completes. + +```{.http .apirequest title="HTTP Request"} +POST /server/job_queue/start +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.job_queue.start", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "queued_jobs": [ + { + "filename": "job1.gcode", + "job_id": "0000000066D99C90", + "time_added": 1636151050.7666452, + "time_in_queue": 21.89680004119873 + }, + { + "filename": "job2.gcode", + "job_id": "0000000066D991F0", + "time_added": 1636151050.7766452, + "time_in_queue": 21.88680004119873 + }, + { + "filename": "subdir/job3.gcode", + "job_id": "0000000066D99D80", + "time_added": 1636151050.7866452, + "time_in_queue": 21.90680004119873 + } + ], + "queue_state": "loading" +} +``` +/// + +/// api-response-spec + open: True + +See the [Job Queue Status](#job-queue-status-response-spec) +Response Specification. + +/// + +## Perform a Queue Jump + +Jumps a job to the front of the queue. + +```{.http .apirequest title="HTTP Request"} +POST /server/job_queue/jump?job_id=0000000066D991F0 +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.job_queue.jump", + "params": { + "job_id": "0000000066D991F0" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------- | :----: | ------------ | --------------------------------------- | +| `job_id` | string | **REQUIRED** | The `job_id` of the job to jump to the. | +| | | | front of the queue. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "queued_jobs": [ + { + "filename": "job2.gcode", + "job_id": "0000000066D991F0", + "time_added": 1636151050.7766452, + "time_in_queue": 21.88680004119873 + }, + { + "filename": "job1.gcode", + "job_id": "0000000066D99C90", + "time_added": 1636151050.7666452, + "time_in_queue": 21.89680004119873 + }, + { + "filename": "subdir/job3.gcode", + "job_id": "0000000066D99D80", + "time_added": 1636151050.7866452, + "time_in_queue": 21.90680004119873 + } + ], + "queue_state": "loading" +} +``` +/// + +/// api-response-spec + open: True + +See the [Job Queue Status](#job-queue-status-response-spec) +Response Specification. + +/// diff --git a/docs/external_api/jsonrpc_notifications.md b/docs/external_api/jsonrpc_notifications.md new file mode 100644 index 0000000..051edae --- /dev/null +++ b/docs/external_api/jsonrpc_notifications.md @@ -0,0 +1,1455 @@ +# JSON-RPC notifications + +Persistent connections to Moonraker (websocket, unix socket) will +receive asynchronous via JSON-RPC notifications. A "notification" +in JSON-RPC is a method call without an `id` parameter, for example: + +```json +{ + "jsonrpc": "2.0", + "method": "{notification method name}" +} +``` + +OR with parameters: + +```json +{ + "jsonrpc": "2.0", + "method": "{notification method name}", + "params": [true, "pos_param_2", {"obj": "stuff"}] +} +``` + +To keep consistency Moonraker always sends parameters as positional +arguments. Specifically, the `params` field will always contain +an array. This can often lead to a somewhat strange format where +the `params` field contains a single element array, where the element +is an object. + +All of the notifications sent by Moonraker are outlined in this document. + +## Gcode Responses + +Gcode Responses received from Klipper are broadcast to all persistent connections. +All of Klippy's gcode responses are forwarded over the websocket. + +```{.text title="Notification Method Name"} +notify_gcode_response +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_gcode_response", + "params": ["response message"] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | --------------------------- | +| 0 | string | The gcode response message. | + +/// + +## Subscription Updates + +Klipper object subscription data received as a result of invoking the +[subscribe endpoint](./printer.md#subscribe-to-printer-object-status-updates). + +```{.text title="Notification Method Name"} +notify_status_update +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_status_update", + "params": [ + "gcode_move": { + "speed": 1500, + }, + "toolhead": { + "status": "Ready" + }, + 578243.57824499 + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ------------------------------------------------ | +| 0 | object | An object containing changes to subscribed | +| | | Klipper objects. Each key is the name of a |^ +| | | printer object, each value will be an object |^ +| | | containing fields that have changed since |^ +| | | the last update. |^ +| 1 | float | A timestamp indicating the time the subscription | +| | | data was sent. This time is relative to the |^ +| | | monotonic clock used by Klipper. |^ + +/// + +/// Tip +See Klipper's [status reference](https://www.klipper3d.org/Status_Reference.html) +for details on printer objects and the fields they report. +/// + +## Klippy Ready + +Indicates that the Klippy Host has entered the `ready` state. + +```{.text title="Notification Method Name"} +notify_klippy_ready +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_klippy_ready" +} +``` + +## Klippy Shutdown + +Indicates that the Klippy Host has entered the `shutdown` state. + +```{.text title="Notification Method Name"} +notify_klippy_shutdown +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_klippy_shutdown" +} +``` + +## Klippy Disconnected + +Indicates that Moonraker's connection to Klippy has terminated. + +```{.text title="Notification Method Name"} +notify_klippy_disconnected +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_klippy_disconnected" +} +``` + +## File List Changed + +Moonraker's `file_manager` will emit notifications when a change +to one of its watched `root` directories is detected. This includes +changes to files and subdirectories within the root. + + +```{.text title="Notification Method Name"} +notify_filelist_changed +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_filelist_changed", + "params": [ + { + "item": { + "root": "gcodes", + "path": "subdir/my_file.gcode", + "modified": 1676940082.8595376, + "size": 384096, + "permissions": "rw" + }, + "source_item": { + "path": "testdir/my_file.gcode", + "root": "gcodes" + }, + "action": "move_file" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ----------------------------------------------------- | +| 0 | object | A [Changed Item Info](#fileinfo-changed-spec) object. | + +| Field | Type | Description | +| ------------- | :----: | ----------------------------------------------- | +| `action` | string | The [action](#filelist-changed-action-desc) | +| | | that caused the notification. |^ +| `item` | object | The `destination item` affected by the change. | +| | | #dest-item-info-spec |+ +| `source_item` | object | The `source item` affected by the change. Only | +| | | present for `move_file` and `move_dir` actions. |^ +| | | #source-item-info-spec |+ +{ #fileinfo-changed-spec } Changed Item Info + +| Field | Type | Description | +| ------------- | :----: | ---------------------------------------------- | +| `path` | string | The path of the destination item relative to | +| | | the root directory. |^ +| `root` | string | The root node of the destination item. | +| `modified` | float | The last modified date in Unix Time (seconds). | +| `size` | int | The size of the destination item. | +| `permissions` | string | Permissions available on the changed item | +| | | (if applicable). |^ +{ #dest-item-info-spec } Destination Item Info + +| Field | Type | Description | +| ------ | :----: | --------------------------------------- | +| `path` | string | The path of the source item relative to | +| | | the root directory. |^ +| `root` | string | The root node of the source item. | +{ #source-item-info-spec } Source Item Info + +| Action | Description | +| ------------- | -------------------------------------------------------- | +| `create_file` | A file has been created within the watched root. | +| `create_dir` | A subdirectory has been created within the watched root. | +| `delete_file` | A file has been deleted within the watched root. | +| `delete_dir` | A subdirectory has been deleted within the watched root. | +| `move_file` | A file in a watched root has been moved. | +| `move_dir` | A subdirectory in a watched root has been moved. | +| `modify_file` | A file in a watched root has been modified. | +| `root_update` | A root folder's location on disk has changed. | +{ #filelist-changed-action-desc } Filelist Changed Action + +/// + +/// tip +Notifications are bundled where applicable. For example, when a +directory containing children is deleted a single `delete_dir` +notification is pushed. Likewise, when a directory is moved or copied, +a single `move_dir` or `create_dir` notification is pushed. Children +that are moved, copied, or deleted as a result of a parent's action will +not receive individual notifications. +/// + +## Update Manager Response + +While the `update_manager` is in the process of updating one or more +registered software items, it will emit notifications containing information +about the current status of the update. + +```{.text title="Notification Method Name"} +notify_update_response +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_update_response", + "params": [ + { + "application": "{app_name}", + "proc_id": 446461, + "message": "Update Response Message", + "complete": false + } + ] +} +``` +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | --------------------------------------------- | +| 0 | object | An update manager notification status object. | + +| Field | Type | Description | +| ------------- | :-----: | ----------------------------------------------- | +| `application` | string | The name of the software currently updating. | +| `proc_id` | int | A unique ID associated with the current update. | +| `message` | message | A message containing status and/or information | +| | | about the current update. |^ +| `complete` | bool | When set to `true` it indicates that the update | +| | | has finished and this will be the last status |^ +| | | response notification sent for this update. |^ + +/// + +## Update Manager Refreshed + +After the update manager has performed a refresh of the +registered software update state it will send a notification +to all connections containing the complete current status. + +```{.text title="Notification Method Name"} +notify_update_refreshed +``` + +/// collapse-code +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_update_refreshed", + "params": [ + { + "busy": false, + "github_rate_limit": 60, + "github_requests_remaining": 57, + "github_limit_reset_time": 1615836932, + "version_info": { + "system": { + "name": "system", + "configured_type": "system", + "package_count": 4, + "package_list": [ + "libtiff5", + "raspberrypi-sys-mods", + "rpi-eeprom-images", + "rpi-eeprom" + ] + }, + "moonraker": { + "channel": "dev", + "debug_enabled": true, + "is_valid": true, + "configured_type": "git_repo", + "corrupt": false, + "info_tags": [], + "detected_type": "git_repo", + "name": "moonraker", + "remote_alias": "arksine", + "branch": "master", + "owner": "arksine", + "repo_name": "moonraker", + "version": "v0.7.1-364", + "remote_version": "v0.7.1-364", + "rollback_version": "v0.7.1-360", + "current_hash": "ecfad5cff15fff1d82cb9bdc64d6b548ed53dfaf", + "remote_hash": "ecfad5cff15fff1d82cb9bdc64d6b548ed53dfaf", + "is_dirty": false, + "detached": true, + "commits_behind": [], + "git_messages": [], + "full_version_string": "v0.7.1-364-gecfad5c", + "pristine": true, + "recovery_url": "https://github.com/Arksine/moonraker.git", + "remote_url": "https://github.com/Arksine/moonraker.git", + "warnings": [], + "anomalies": [ + "Unofficial remote url: https://github.com/Arksine/moonraker-fork.git", + "Repo not on official remote/branch, expected: origin/master, detected: altremote/altbranch", + "Detached HEAD detected" + ] + }, + "mainsail": { + "name": "mainsail", + "owner": "mainsail-crew", + "version": "v2.1.1", + "remote_version": "v2.1.1", + "rollback_version": "v2.0.0", + "configured_type": "web", + "channel": "stable", + "info_tags": [ + "desc=Mainsail Web Client", + "action=some_action" + ], + "warnings": [], + "anomalies": [], + "is_valid": true + }, + "fluidd": { + "name": "fluidd", + "owner": "fluidd-core", + "version": "v1.16.2", + "remote_version": "v1.16.2", + "rollback_version": "v1.15.0", + "configured_type": "web", + "channel": "beta", + "info_tags": [], + "warnings": [], + "anomalies": [], + "is_valid": true + }, + "klipper": { + "channel": "dev", + "debug_enabled": true, + "is_valid": true, + "configured_type": "git_repo", + "corrupt": false, + "info_tags": [], + "detected_type": "git_repo", + "name": "klipper", + "remote_alias": "origin", + "branch": "master", + "owner": "Klipper3d", + "repo_name": "klipper", + "version": "v0.10.0-1", + "remote_version": "v0.10.0-41", + "rollback_version": "v0.9.1-340", + "current_hash": "4c8d24ae03eadf3fc5a28efb1209ce810251d02d", + "remote_hash": "e3cbe7ea3663a8cd10207a9aecc4e5458aeb1f1f", + "is_dirty": false, + "detached": false, + "commits_behind": [ + { + "sha": "e3cbe7ea3663a8cd10207a9aecc4e5458aeb1f1f", + "author": "Kevin O'Connor", + "date": "1644534721", + "subject": "stm32: Clear SPE flag on a change to SPI CR1 register", + "message": "The stm32 specs indicate that the SPE bit must be cleared before\nchanging the CPHA or CPOL bits.\n\nReported by @cbc02009 and @bigtreetech.\n\nSigned-off-by: Kevin O'Connor ", + "tag": null + }, + { + "sha": "99d55185a21703611b862f6ce4b80bba70a9c4b5", + "author": "Kevin O'Connor", + "date": "1644532075", + "subject": "stm32: Wait for transmission to complete before returning from spi_transfer()", + "message": "It's possible for the SCLK pin to still be updating even after the\nlast byte of data has been read from the receive pin. (In particular\nin spi mode 0 and 1.) Exiting early from spi_transfer() in this case\ncould result in the CS pin being raised before the final updates to\nSCLK pin.\n\nAdd an additional wait at the end of spi_transfer() to avoid this\nissue.\n\nSigned-off-by: Kevin O'Connor ", + "tag": null + } + ], + "git_messages": [], + "full_version_string": "v0.10.0-1-g4c8d24ae-shallow", + "pristine": true, + "recovery_url": "https://github.com/Klipper3d/klipper.git", + "remote_url": "https://github.com/Klipper3d/klipper.git", + "warnings": [], + "anomalies": [] + } + } + } + ] +} +``` +/// + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | --------------------------------------------------------------- | +| 0 | object | An [Update Status Info](./update_manager.md#update-status-spec) | +| | | object. |^ + +/// + +## CPU Throttled + +If the system supports CPU monitoring via `vcgencmd` Moonraker will emit +notifications when the CPU's throttled state changes. + +```{.text title="Notification Method Name"} +notify_cpu_throttled +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_cpu_throttled", + "params": [ + { + "bits": 0, + "flags": [] + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ------------------------------------------------------ | +| 0 | object | A [CPU Throttled State](#throttled-state-spec) object. | + +| Field | Type | Description | +| ------- | :------: | ----------------------------------------------------------- | +| `bits` | int | The current throttled state as an integer. A bitwise AND | +| | | can be performed against this value to generate custom flag |^ +| | | descriptions. |^ +| `flags` | [string] | A list of `Throttled Flags` describing the current state. | +| | | #throttled-flags-desc |+ +{ #throttled-state-spec } Throttled State + +| Flag | Bit Offset | +| ------------------------------ | :--------: | +| Under-Voltage Detected | `1 << 0` | +| Frequency Capped | `1 << 1` | +| Currently Throttled | `1 << 2` | +| Temperature Limit Active | `1 << 3` | +| Previously Under-Volted | `1 << 16` | +| Previously Frequency Capped | `1 << 17` | +| Previously Throttled | `1 << 18` | +| Previously Temperature Limited | `1 << 16` | +{ #throttled-flags-desc } Throttled Flags +/// + +/// note +It is possible for clients to receive this notification multiple times +if the system repeatedly transitions between an active and inactive throttled +condition. +/// + +## Moonraker Process Statistic Update + +Moonraker performs live monitoring of host machine data and periodically +emits a notification with the most recent statistics. + +```{.text title="Notification Method Name"} +notify_proc_stat_update +``` + +/// collapse-code +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_proc_stat_update", + "params": [{ + "moonraker_stats": { + "time": 1615837812.0894408, + "cpu_usage": 1.99, + "memory": 23636, + "mem_units": "kB" + }, + "cpu_temp": 44.008, + "network": { + "lo": { + "rx_bytes": 114555457, + "tx_bytes": 114555457, + "bandwidth": 2911.49 + }, + "wlan0": { + "rx_bytes": 48773134, + "tx_bytes": 115035939, + "bandwidth": 3458.77 + } + }, + "system_cpu_usage": { + "cpu": 2.53, + "cpu0": 3.03, + "cpu1": 5.1, + "cpu2": 1.02, + "cpu3": 1 + }, + "websocket_connections": 2 + }] +} +``` +/// + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | -------------------------------------------------------------- | +| 0 | object | A [proc stats response](./machine.md#proc-stats-response-spec) | +| | | object. The `throttled_state` and `system_uptime` fields are |^ +| | | omitted from the notification. |^ + +/// + +## History Changed + +When Moonraker's `[history]` component detects a new or completed +job a notification will be emitted. + +```{.text title="Notification Method Name"} +notify_history_changed +``` + +/// collapse-code +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_history_changed", + "params": [ + { + "action": "added", + "job": { + "end_time": null, + "filament_used": 20.09796999999998, + "filename": "calicat_0.3mm_PLA_MK3S_33m.gcode", + "metadata": { + "size": 538254, + "modified": 1646770808, + "uuid": "4022d6bd-e5f5-45d7-84af-f38bcc57a5d3", + "slicer": "PrusaSlicer", + "slicer_version": "2.4.0+linux-x64-GTK3", + "gcode_start_byte": 51238, + "gcode_end_byte": 528796, + "object_height": 34.4, + "estimated_time": 1954, + "nozzle_diameter": 0.4, + "layer_height": 0.3, + "first_layer_height": 0.2, + "first_layer_extr_temp": 225, + "first_layer_bed_temp": 60, + "filament_name": "Fusion PLA Carbon Rod Black", + "filament_type": "PLA", + "filament_total": 1754.96, + "filament_weight_total": 5.23, + "thumbnails": [ + { + "width": 32, + "height": 24, + "size": 1829, + "relative_path": ".thumbs/calicat_0.3mm_PLA_MK3S_33m-32x32.png" + }, + { + "width": 400, + "height": 300, + "size": 36586, + "relative_path": ".thumbs/calicat_0.3mm_PLA_MK3S_33m-400x300.png" + } + ] + }, + "print_duration": 0.911540990229696, + "status": "in_progress", + "start_time": 1738671939.433274, + "total_duration": 0.911540990229696, + "auxiliary_data": [ + { + "provider": "sensor hist_test", + "name": "power_consumption", + "value": 0, + "description": "Printer Power Consumption", + "units": "kWh" + }, + { + "provider": "sensor hist_test", + "name": "max_current", + "value": 0, + "description": "Maximum current draw", + "units": "A" + }, + { + "provider": "sensor hist_test", + "name": "min_current", + "value": 0, + "description": "Maximum current draw", + "units": "A" + }, + { + "provider": "sensor hist_test", + "name": "avg_current", + "value": 0, + "description": "Maximum current draw", + "units": "A" + }, + { + "provider": "sensor hist_test", + "name": "status", + "value": null, + "description": "Power Switch State", + "units": null + }, + { + "provider": "sensor hist_test", + "name": "id", + "value": [], + "description": "Test ID", + "units": null + }, + { + "provider": "sensor hist_test", + "name": "filament", + "value": 0, + "description": "filament tracker", + "units": "mm" + }, + { + "provider": "spoolman", + "name": "spool_ids", + "value": [ + 1 + ], + "description": "Spool IDs used", + "units": null + } + ], + "user": "testuser", + "exists": true, + "job_id": "000027" + } + } + ] +} +``` +/// + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ----------- | +| 0 | object | A [Job History Notification](#job-hist-notify-spec) object. | + +| Field | Type | Description | +| -------- | :----: | ---------------------------------------------------------- | +| `action` | string | The `action` that triggered the notification. | +| | | #job-hist-notify-action |+ +| `job` | string | A [Job History Entry](./history.md#job-history-entry-spec) | +| | | object. |^ +{ #job-hist-notify-spec } Job History Notification + +| Action | Description | +| ---------- | -------------------------------------------- | +| `added` | A new job was added to the job history. | +| `finished` | A running job was completed. This includes | +| | jobs successfully completed, cancelled jobs, |^ +| | and jobs that encountered an error. |^ +{ #job-hist-notify-action } Job History Notify Action + +/// + +## Authorized User Created + +Moonraker's `[authorization]` component will emit a notification when +a new user entry has been created. + +```{.text title="Notification Method Name"} +notify_user_created +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_user_created", + "params": [ + { + "username": "Eric" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ------------------------------------------------------- | +| 0 | object | An [Auth Notification](#auth-notification-spec) object. | + +| Field | Type | Description | +| ---------- | :----: | ---------------------------------------------- | +| `username` | string | The username of the user entry associated with | +| | | the notification. |^ +{ #auth-notification-spec } Auth Notification + +/// + +## Authorized User Deleted + +Moonraker's `[authorization]` component will emit a notification when +an existing user entry has been deleted. + +```{.text title="Notification Method Name"} +notify_user_deleted +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_user_deleted", + "params": [ + { + "username": "Eric" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ------------------------------------------------------- | +| 0 | object | An [Auth Notification](#auth-notification-spec) object. | + +/// + +## Authorized User Logged Out + +Moonraker's `[authorization]` component will emit a notification when +a user has logged out. + +```{.text title="Notification Method Name"} +notify_user_logged_out +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_user_logged_out", + "params": [ + { + "username": "Eric" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ------------------------------------------------------- | +| 0 | object | An [Auth Notification](#auth-notification-spec) object. | + +/// + + +## Service State Changed + +If Moonraker's `[machine]` component is configured with its `systemd` +integration enabled it will monitor the state of various systemd services. +When a change is detected in service state Moonraker will emit a +notification. + +```{.text title="Notification Method Name"} +notify_service_state_changed +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_service_state_changed", + "params": [ + { + "klipper": { + "active_state": "inactive", + "sub_state": "dead" + } + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ---------------------------------------------------------- | +| 0 | object | A [Service State Notification](#service-state-notify-spec) | +| | | object. |^ + +| Field | Type | Description | +| -------------- | :----: | --------------------------------------------------- | +| *service_name* | object | A [Unit Status](#unit-status-spec) object. The key | +| | | for this field is the service name. |^ +| | | #unit-status-spec |+ +{ #service-state-notify-spec } Service State Notification + +| Field | Type | Description | +| -------------- | :----: | ---------------------------------------------------------------- | +| `active_state` | string | The new `ACTIVE` state reported by the provider for the service. | +| `sub_state` | string | The new `SUB` state reported by the provider for the service. | +{ #unit-status-spec } Unit Status + +/// + +## Job Queue Changed + +Moonraker's `[job_queue]` component emits a notification when the job +queue state changes and when the queue is modified. + +```{.text title="Notification Method Name"} +notify_job_queue_changed +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_job_queue_changed", + "params": [ + { + "action": "state_changed", + "updated_queue": null, + "queue_state": "paused" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | -------------------------------------------------------- | +| 0 | object | A [Job Queue Notification](#job-queue-notification-spec) | +| | | object. |^ + +| Field | Type | Description | +| --------------- | :--------------: | ------------------------------------------- | +| `action` | string | The [action](#job-queue-notify-action) that | +| | | triggered the notification. |^ +| `queue_state` | string | The current queue | +| | | [state](./job_queue.md#queue-state-desc). |^ +| `updated_queue` | [object] \| null | An array of `Queued Job` objects reflecting | +| | | the updated queue. Will be `null` if the |^ +| | | queue has not changed. |^ +| | | #queued-job-spec |+ +{ #job-queue-notification-spec } + +| Field | Type | Description | +| --------------- | :----: | ------------------------------------------------------- | +| `filename` | string | The name of the gcode file queued. | +| `job_id` | string | A unique ID assigned to the queued job. | +| `time_added` | float | The time (in Unix Time) the job was added to the queue. | +| `time_in_queue` | float | The cumulative amount of time, in seconds, the job has | +| | | been pending in the queue. |^ +{ #queued-job-spec } Queued Job + +| Action | Description | +| --------------- | ----------------------------------------------------- | +| `state_changed` | The internal job queue state has changed. | +| `jobs_added` | One or more jobs have been added to the queue. | +| `jobs_removed` | One or more jobs have been removed from the queue. | +| `job_loaded` | A job has been popped from the queue and successfully | +| | started. |^ +{ #job-queue-notify-action } Job Queue Notification Action + +/// + + +## Button Event + +Moonraker `[button]` component supports optional notifications +sent when a button is pressed and/or released. + +```{.text title="Notification Method Name"} +notify_button_event +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_button_event", + "params": [ + { + "name": "my_button", + "type": "gpio", + "event": { + "elapsed_time": 0.09323832602240145, + "received_time": 698614.214597004, + "render_time": 698614.214728513, + "pressed": false + }, + "aux": null + } + ] +} +``` +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | --------------------------------------- | +| 0 | object | A [Button Notification](#button-notify-spec) object. | + +| Field | Type | Description | +| ------- | :----: | --------------------------------------------- | +| `name` | string | The name of the button sending the event. | +| `type` | string | The configured type of the button. Currently | +| | | only the `gpio` type is supported. |^ +| `event` | object | A `Button Event` object. | +| | | #button-event-spec |+ +| `aux` | any | Auxiliary data attached to the event. Can be | +| | | any JSON encodable type. If no aux data is |^ +| | | sent with the event the value will be `null` |^ +{#button-notify-spec} Button Notification + +| Field | Type | Description | +| --------------- | :---: | ------------------------------------------- | +| `elapsed_time` | float | The time elapsed (in seconds) since the | +| | | last detected button event. |^ +| `received_time` | float | The time the event was detected relative | +| | | to Moonraker's monotonic clock. |^ +| `render_time` | float | The time the button's template started | +| | | rendering relative to Moonraker's monotonic |^ +| | | clock. |^ +| `pressed` | bool | Set to `true` if the button is pressed. | +{ #button-event-spec } Button Event +/// + +## Announcement update event + +Moonraker's `[announcements]` component will emit a notification +when announcement entries are added or removed. + +```{.text title="Notification Method Name"} +notify_announcement_update +``` + +/// collapse-code +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_announcement_update", + "params": [ + { + "entries": [ + { + "entry_id": "arksine/moonlight/issue/3", + "url": "https://github.com/Arksine/moonlight/issues/3", + "title": "Test announcement 3", + "description": "Test Description [with a link](https://moonraker.readthedocs.io).", + "priority": "normal", + "date": 1647459219, + "dismissed": false, + "date_dismissed": null, + "dismiss_wake": null, + "source": "moonlight", + "feed": "moonlight" + }, + { + "entry_id": "arksine/moonlight/issue/2", + "url": "https://github.com/Arksine/moonlight/issues/2", + "title": "Announcement Test Two", + "description": "This is a high priority announcement. This line is included in the description.", + "priority": "high", + "date": 1646855579, + "dismissed": false, + "date_dismissed": null, + "dismiss_wake": null, + "source": "moonlight", + "feed": "moonlight" + } + { + "entry_id": "arksine/moonraker/issue/349", + "url": "https://github.com/Arksine/moonraker/issues/349", + "title": "PolicyKit warnings; unable to manage services, restart system, or update packages", + "description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.", + "priority": "normal", + "date": 1643392406, + "dismissed": false, + "source": "moonlight", + "feed": "Moonraker" + } + ] + } + ] +} +``` +/// + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | --------------------------------------------------------- | +| 0 | object | An [Announcement Notification](#announcement-notify-spec) | +| | | object. |^ + +| Field | Type | Description | +| --------- | :------: | ----------------------------------------------------------------- | +| `entries` | [object] | An array of | +| | | [Announcement Entry](./announcements.md#announcement-entry-spec ) |^ +| | | objects. |^ +{ #announcement-notify-spec } Announcement Notification + +/// + +## Announcement dismissed event + +Moonraker's `[announcements]` component will emit a notification +when an announcement is dismissed. + +```{.text title="Notification Method Name"} +notify_announcement_dismissed +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_announcement_dismissed", + "params": [ + { + "entry_id": "arksine/moonlight/issue/3" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ----------------------------------------------------------------------------- | +| 0 | object | An [Announcement Dismissed Notification](#announcement-dismissed-notify-desc) | +| | | object. |^ + +| Field | Type | Description | +| ---------- | :----: | -------------------------------------------------- | +| `entry_id` | string | The unique entry ID of the dismissed announcement. | +{ #announcement-dismissed-notify-desc} Announcement Dismissed Notification + +/// + +## Announcement wake event + +Moonraker's `[announcements]` component will emit a notification +when an announcement "wakes" from a dismissed state. + +```{.text title="Notification Method Name"} +notify_announcement_wake +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_announcement_wake", + "params": [ + { + "entry_id": "arksine/moonlight/issue/1" + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ----------------------------------------------------------------------------- | +| 0 | object | An [Announcement Wake Notification](#announcement-wake-notify-desc) | +| | | object. |^ + +| Field | Type | Description | +| ---------- | :----: | ----------------------------------------------- | +| `entry_id` | string | The unique entry ID of the awoken announcement. | +| | | The announcement's `dismissed` field will |^ +| | | be set to `false`. |^ +{ #announcement-wake-notify-desc} Announcement Wake Notification + +/// + +## Sudo alert event + +At times Moonraker may require sudo permission to perform a specific task. +This is rare and generally involves an upgrade (ie: Moonraker's systemd +service file needs to be modified). When Moonraker runs a command that +requires sudo permission an alert will be sent via notification. + +Pending sudo requests that cannot be executed until the user +[sets their sudo password](./machine.md#set-sudo-password) will +also emit a notification. + +```{.text title="Notification Method Name"} +notify_sudo_alert +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_sudo_alert", + "params": [ + { + "sudo_requested": true, + "sudo_messages": [ + "Sudo password required to update Moonraker's systemd service." + ] + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ----------------------------------------------------------- | +| 0 | object | A [Sudo Alert Notification](#sudo-alert-notify-spec) object | + +| Pos | Type | Description | +| ---------------- | :------: | ------------------------------------------ | +| `sudo_requested` | bool | When `true` there are pending tasks that | +| | | require super user permission. |^ +| `sudo_messages` | [string] | An array of messages describing the action | +| | | or actions requiring sudo permission. |^ +{ #sudo-alert-notify-spec} Sudo Alert Notification + +//// note +Each `sudo message` can fall into one of the following categories: + +- An explanation of a pending sudo request. +- A response from a task that successfully ran with sudo permissions. +- A response from a task that returned an error. +//// + +/// + +## Webcams changed event + +Moonraker's `[webcam]` component will send a notification when +a webcam is added, removed, or updated. + +```{.text title="Notification Method Name"} +notify_webcams_changed +``` + +/// collapse-code +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_webcams_changed", + "params": [ + { + "webcams": [ + { + "name": "tc2", + "location": "printer", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 15, + "target_fps_idle": 5, + "stream_url": "http://printer.lan/webcam?action=stream", + "snapshot_url": "http://printer.lan/webcam?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "database" + }, + { + "name": "TestCam", + "location": "printer", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 15, + "target_fps_idle": 5, + "stream_url": "/webcam/?action=stream", + "snapshot_url": "/webcam/?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "database" + } + ] + } + ] +} +``` +/// + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ---------------------------------------------------- | +| 0 | object | A [Webcam Notification](#webcam-notify-spec) object. | + +| Field | Type | Description | +| --------- | :------: | ---------------------------------------------------------- | +| `webcams` | [object] | An array of [Webcam Entry](./webcams.md#webcam-entry-spec) | +| | | objects. |^ +{ #webcam-notify-spec } Webcam Notification + +/// + +## Spoolman active spool ID changed + +Moonraker's `[spoolman]` component will emit a notification +when the current active spool ID has changed. + +```{.text title="Notification Method Name"} +notify_active_spool_set +``` + +```json +{ + "jsonrpc": "2.0", + "method": "notify_active_spool_set", + "params": [ + { + "spool_id": 1 + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ---------------------------------------------------------------------- | +| 0 | object | An [Active Spool Set Notification](#spoolman-active-spool-notify-spec) | +| | | object. |^ + +| Field | Type | Description | +| ---------- | :---------: | ---------------------------------------- | +| `spool_id` | int \| null | The spool ID of the new active spool. A | +| | | value of `null` indicates that no active |^ +| | | spool is set and tracking is disabled. |^ +{ #spoolman-active-spool-notify-spec } Active Spool Set Notification + +/// + +## Spoolman Status Changed + +Moonraker's `[spoolman]` component holds a persistent websocket +connection to the server. This allow Moonraker to remain aware +of the server's status. Moonraker will emit a notification when +the connection status to Spoolman changes. + +```{.text title="Notification Method Name"} +notify_spoolman_status_changed +``` + +```json +{ + "jsonrpc": "2.0", + "method": "notify_spoolman_status_changed", + "params": [ + { + "spoolman_connected": false + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | --------------------------------------------------------------- | +| 0 | object | A [Spoolman Status Notification](#spoolman-status-notify-spec ) | +| | | object. |^ + +| Field | Type | Description | +| -------------------- | :--: | --------------------------------------------- | +| `spoolman_connected` | bool | A value of `true` indicates that Moonraker is | +| | | currently connected to the Spoolman Server. |^ +{ #spoolman-status-notify-spec } Spoolman Status Notification + +/// + +## Agent Events + +Moonraker has limited support for third party extensions through +client connections that identify themselves as +[agents](./extensions.md#agent-specific-endpoints). +Agents are granted access to additional endpoints that allow +them to extend Moonraker's functionality. One such method +allows agents to send events that are broadcast to all of Moonraker's +connected clients. Moonraker proxies agent events through a +notification. + +```{.text title="Notification Method Name"} +notify_agent_event +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_agent_event", + "params": [ + { + "agent": "moonagent", + "event": "connected", + "data": { + "name": "moonagent", + "version": "0.0.1", + "type": "agent", + "url": "https://github.com/arksine/moontest" + } + } + ] +} +``` + +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ------------------------------------------------------- | +| 0 | object | An [Agent Event Notification](#agent-event-notify-spec) | +| | | object. |^ + +| Field | Type | Description | +| ------- | :------: | -------------------------------------------------------- | +| `agent` | `string` | The name of the agent sending the event. | +| `event` | `string` | The name of the event. Can be any name | +| | | other than those [reserved](#reserved-agent-event-desc) |^ +| | | by Moonraker. |^ +| `data` | any | Additional data sent with the event. Can be any JSON | +| | | encodable value. If the event does not attach data this |^ +| | | field will be omitted. |^ +{ #agent-event-notify-spec } Agent Event Notification + +| Event | Description | +| -------------- | ----------------------------------------------------- | +| `connected` | An agent has connected to Moonraker. The `data` | +| | field will contain an |^ +| | [Agent Info](./extensions.md#agent-info-spec) object. |^ +| `disconnected` | An agent has disconnected from Moonraker. The `data` | +| | field is omitted for this event. |^ +{ #reserved-agent-event-desc} Reserved Agent Events + +/// + +/// note +The agent that sends an event will not receive a notification for that +event. It will however receive events from other agents. +/// + +## Sensor Events + +Moonraker's `[sensor]` component will take periodic measurements +of configured sensors. When one or more new measurement is received +a notification will be emitted containing the new measurement data. + +```{.text title="Notification Method Name"} +notify_sensor_update +``` + +```{.json .apiresponse title="Example Notification"} +{ + "jsonrpc": "2.0", + "method": "notify_sensor_update", + "params": [ + { + "sensor1": { + "humidity": 28.9, + "temperature": 22.4 + } + } + ] +} +``` +/// api-notification-spec + open: True + +| Pos | Type | Description | +| --- | :----: | ---------------------------------------------------------- | +| 0 | object | A [Sensor Notification](#sensor-notification-spec) object. | + +| Field | Type | Description | +| ------------- | :----: | ------------------------------------------------ | +| *sensor_name* | object | The object may contain multiple `sensors`, where | +| | | each key is the name of a sensor and the value |^ +| | | is a `Sensor Values` object. |^ +| | | #sensor-values-spec |+ +{ #sensor-notification-spec } Sensor Notification + +| Field | Type | Description | +| ------------ | :--: | ----------------------------------------------- | +| *value_name* | any | The object may contain multiple `values`, where | +| | | each key is the name of a parameter tracked |^ +| | | by the sensor, and the value is the most |^ +| | | recent reported measurement. |^ +{ #sensor-values-spec } Sensor Values + +/// diff --git a/docs/external_api/machine.md b/docs/external_api/machine.md new file mode 100644 index 0000000..fd0e3d6 --- /dev/null +++ b/docs/external_api/machine.md @@ -0,0 +1,1473 @@ +# System Administration + +The endpoints in this section provide administrative functions for +the host machine and operating system. + +## Get System Info + +```{.http .apirequest title="HTTP Request"} +GET /machine/system_info +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.system_info", + "id": 4665 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "system_info": { + "provider": "systemd_dbus", + "cpu_info": { + "cpu_count": 4, + "bits": "32bit", + "processor": "armv7l", + "cpu_desc": "ARMv7 Processor rev 4 (v7l)", + "serial_number": "b898bdb4", + "hardware_desc": "BCM2835", + "model": "Raspberry Pi 3 Model B Rev 1.2", + "total_memory": 945364, + "memory_units": "kB" + }, + "sd_info": { + "manufacturer_id": "03", + "manufacturer": "Sandisk", + "oem_id": "5344", + "product_name": "SU32G", + "product_revision": "8.0", + "serial_number": "46ba46", + "manufacturer_date": "4/2018", + "capacity": "29.7 GiB", + "total_bytes": 31914983424 + }, + "distribution": { + "name": "Raspbian GNU/Linux 10 (buster)", + "id": "raspbian", + "version": "10", + "version_parts": { + "major": "10", + "minor": "", + "build_number": "" + }, + "like": "debian", + "codename": "buster" + }, + "available_services": [ + "klipper", + "klipper_mcu", + "moonraker" + ], + "instance_ids": { + "moonraker": "moonraker", + "klipper": "klipper" + }, + "service_state": { + "klipper": { + "active_state": "active", + "sub_state": "running" + }, + "klipper_mcu": { + "active_state": "active", + "sub_state": "running" + }, + "moonraker": { + "active_state": "active", + "sub_state": "running" + } + }, + "virtualization": { + "virt_type": "none", + "virt_identifier": "none" + }, + "python": { + "version": [ + 3, + 9, + 2, + "final", + 0 + ], + "version_string": "3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110]" + }, + "network": { + "wlan0": { + "mac_address": "", + "ip_addresses": [ + { + "family": "ipv4", + "address": "192.168.1.127", + "is_link_local": false + }, + { + "family": "ipv6", + "address": "", + "is_link_local": false + }, + { + "family": "ipv6", + "address": "fe80::", + "is_link_local": true + } + ] + } + }, + "canbus": { + "can0": { + "tx_queue_len": 128, + "bitrate": 500000, + "driver": "mcp251x" + }, + "can1": { + "tx_queue_len": 128, + "bitrate": 500000, + "driver": "gs_usb" + } + } + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :----: | ----------------------------------------------------------- | +| `system_info` | object | A top level [System Info](#sys-info-spec) object containing | +| | | various attributes that report info. |^ +| | | #sys-info-spec |+ + + +| Field | Type | Description | +| -------------------- | :------: | -------------------------------------------------------------- | +| `python` | object | A `Python Info` object containing details about the Python | +| | | interpreter. |^ +| | | #python-info-spec |+ +| `cpu_info` | object | A `CPU Info` object containing details about the host CPU. | +| | | #cpu-info-spec |+ +| `sd_info` | object | An `SDCard Info` object containing data about the host SD | +| | | Card. If no SD Card is detected this object will be empty. |^ +| | | #sdcard-info-spec |+ +| `distribution` | object | A `Distribution` object containing details about the host | +| | | Linux Distribution. |^ +| | | #distribution-spec |+ +| `virtualization` | object | A `Virtualization` object containing virtualization status | +| | | of the environment running Moonraker. |^ +| | | #virtualization-spec |+ +| `network` | object | A `Network Info` object containing information about the | +| | | system's current network state. |^ +| | | #network-info-spec |+ +| `canbus` | object | A `Canbus Info` object containing information about CAN | +| | | interfaces detected on the system. |^ +| | | #canbus-info-spec |+ +| `provider` | string | The currently configured system provider type. Expand for a | +| | | list of available providers. |^ +| | | #system-provider-desc |+ +| `available_services` | [string] | A list of detected services Moonraker is authorized to manage. | +| `service_state` | object | A `Service State` object containing information about services | +| | | Moonraker is monitoring. This will be an empty object if no |^ +| | | services are being monitored. |^ +| | | #service-state-spec |+ +| `instance_ids` | object | An `Instance ID` object matching known application names to | +| | | their detected system unit name. |^ +| | | #instance-id-spec |+ +{ #sys-info-spec } System Info + +| Field | Type | Description | +| ---------------- | :-----------: | -------------------------------------------------------------------------------------- | +| `version` | [int\|string] | A tuple indicating the version of the Python interpreter running Moonraker. | +| | | A complete description of the reported value can be found in |^ +| | | [Python's Documentation](https://docs.python.org/3/library/sys.html#sys.version_info). |^ +| `version_string` | string | The Python version in string form. | +{ #python-info-spec } Python Info + +| Field | Type | Description | +| --------------- | :---------: | -------------------------------------------------------------------- | +| `cpu_count` | int \| null | The number of CPU's detected. Will be `null` if detection fails. | +| `bits` | string | The bit width of the architecture. This is based on how the Python | +| | | binary was compiled. It is possible for a 64-bit capable processor |^ +| | | to report 32-bits. Will be an empty string if detection fails. |^ +| `cpu_desc` | string | The CPU description as reported in `/proc/cpuinfo`. Will be an | +| | | empty string if detection fails. |^ +| `serial_number` | string | The serial number of the CPU as reported in `/proc/cpuinfo`. Will be | +| | | an empty string if detection fails. |^ +| `hardware_desc` | string | The hardware description as reported in `/proc/cpu_info`. Will be an | +| | | empty string if detection fails. |^ +| `model` | string | The model as reported in `/proc/cpu_info.` Will be an empty string | +| | | if detection fails. |^ +| `total_memory` | int \| null | The total system memory as reported in `/proc/meminfo`. This is an | +| | | integer value and should always be specified in kilobytes. The |^ +| | | `memory_units` field may be used to validate the unit type. Will |^ +| | | be null if memory detection fails. |^ +| `memory_units` | string | The detected units for memory reporting. Should always be "kB", or | +| | | an empty string if detection fails. |^ +{ #cpu-info-spec } CPU Info + +| Field | Type | Description | +| ------------------- | :----: | -------------------------------------------------------------- | +| `manufacturer_id` | string | A 2 character hex string identifying the manufacturer. | +| `manufacturer` | string | The manufacturer's name, if known. | +| `oem_id` | string | A 4 character hex string identifying the OEM. | +| `product_name` | string | An ascii string, up to 10 characters, identifying the | +| | | name of the SD Card. |^ +| `product_revision` | string | Version of the product. Reported as `major.minor`, ie: `1.0` | +| `serial_number` | string | Serial number of the SD Card. Will be 8 hex characters. | +| `manufacturer_date` | string | Date of manufacture. Reported as `month/year`, ie: `10/2022`. | +| `capacity` | string | Reported capacity of the card. Units are postfixed. | +| `total_bytes` | int | Reported capacity in bytes. | +{ #sdcard-info-spec } SDCard Info + +| Field | Type | Description | +| --------------- | :----: | ----------------------------------------------------------- | +| `name` | string | Full name of the Linux distribution. Will be an empty | +| | | string if the name cannot be determined. |^ +| `id` | string | Distribution ID, ie: `ubuntu`. Will be an empty string if | +| | | the id cannot be determined. |^ +| `like` | string | Parent distribution, ie: `debian`. Will be an empty string | +| | | if there is no parent distribution. |^ +| `codename` | string | The codename of the distribution, ie: `bookworm`. Will be | +| | | an empty string if there is no codename. |^ +| `version` | string | The version number of the distribution. Will be an empty | +| | | string if the version cannot be determined. |^ +| `version_parts` | object | The version broken into parts and reported as an object. | +| | | Fields are `major`, `minor`, and `release`. Any values |^ +| | | not present will be reported as empty strings. |^ +| `release_info` | object | An object containing key-value pairs extracted from the | +| | | `os-release` file. The keys are variable, the values will |^ +| | | always be strings. If the `os-release` file does not exist |^ +| | | this will be an empty object. |^ +{ #distribution-spec } Distribution + +| Field | Type | Description | +| ----------- | :----: | -------------------------------------------------------------------- | +| `virt_type` | string | The type of virtualization detected. Expand to view available types. | +| | | #virt-type-desc |+ +| `virt_id` | string | The virtualization identifier. Will be `none` if no virtualization | +| | | is detected. Otherwise the value describes the virtualization |^ +| | | software hosting the instance of Moonraker. |^ +{ #virtualization-spec } Virtualization + +| Name | Description | +| ----------- | ---------------------------------------------- | +| `none` | No virtualization detected. | +| `container` | Moonraker is running inside a container. | +| `vm` | Moonraker is running inside a virtual machine. | +{ #virt-type-desc } Virtualization Types + +| Field | Type | Description | +| ---------- | :----: | ---------------------------------------------------------- | +| _variable_ | object | This object contains zero or more items, where each field | +| | | is the name of a detected network interface and each value |^ +| | | is a `Network Interface` object. |^ +| | | #network-interface-spec |+ +{ #network-info-spec } Network Info + +| Field | Type | Description | +| -------------- | :------: | ---------------------------------------------------------- | +| `ip_addresses` | [object] | A list of `IP Address` objects describing the IPs assigned | +| | | to the interface. |^ +| | | #ip-address-spec |+ +| `mac_address` | string | The MAC address of the hardware bound to the interface. | +{ #network-interface-spec } Network Interface + +| Field | Type | Description | +| --------------- | :----: | --------------------------------------------------------- | +| `address` | string | The detected IP address. | +| `family` | string | The family type of the address. Can be `ipv4` or `ipv6`. | +| `is_link_local` | bool | A boolean value indicating if the address is "link local" | +| | | address. |^ +{ #ip-address-spec } IP Address + +| Field | Type | Description | +| ---------- | :----: | --------------------------------------------------------- | +| _variable_ | object | This object contains zero or more items, where each field | +| | | is the name of a CAN interface and each value is a |^ +| | | `CAN Interface` object. |^ +| | | #can-interface-spec |+ +{ #canbus-info-spec } Canbus Info + +| Field | Type | Description | +| -------------- | :----: | -------------------------------------------------- | +| `tx_queue_len` | int | The configured TX queue length of the interface. | +| `bitrate` | int | The configured CAN bitrate of the interface. | +| `driver` | string | The name of the hardware driver used to manage the | +| | | interface. |^ +{ #can-interface-spec } CAN interface + +| Field | Type | Description | +| ---------- | :----: | --------------------------------------------------------- | +| _variable_ | object | This object contains zero or more items, where the fields | +| | | are the services Moonraker is monitoring and the values |^ +| | | are `Unit Status` objects. |^ +| | | #unit-status-spec |+ +{ #service-state-spec } Service State + +| Field | Type | Description | +| -------------- | :----: | -------------------------------------------------------------------- | +| `active_state` | string | The current `ACTIVE` state reported by the provider for the service. | +| `sub_state` | string | The current `SUB` state reported by the provider for the service. | +{ #unit-status-spec } Unit Status + +| Field | Type | Description | +| ----------- | :----: | --------------------------------------------------- | +| `moonraker` | string | The detected unit name for the `moonraker` service. | +| `klipper` | string | The detected unit name for the `klipper` service. | +{ #instance-id-spec } + +| Provider | description | +| ----------------- | --------------------------------------------------------------------- | +| `none` | No system provider is configured. This disables service management | +| | and monitoring. | +| `systemd_cli` | System management and monitoring is performed using systemd over | +| | the command line, ie: `systemctl`. |^ +| `systemd_dbus` | System management and monitoring is performed using systemd over | +| | DBus. When DBus is available this is preferable to the CLI provider. |^ +| `supervisord_cli` | System management and monitoring is performed using supervisord over | +| | the command line. |^ +{ #system-provider-desc } +/// + +## Shutdown the Operating System + +Commands the Operating System to shutdown. The following pre-requisites must be +met to successfully perform this action: + +- The `provider` must be `systemd_cli` or `systemd_dbus`. +- Moonraker must have permission to shutdown the host. +- Moonraker must not be running inside a container. + +```{.http .apirequest title="HTTP Request"} +POST /machine/shutdown +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.shutdown", + "id": 4665 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Reboot the Operating System + +Commands the Operating System to shutdown. The following pre-requisites must be +met to successfully perform this action: + +- The `provider` must be `systemd_cli` or `systemd_dbus`. +- Moonraker must have permission to reboot the host. +- Moonraker must not be running inside a container. + +```{.http .apirequest title="HTTP Request"} +POST /machine/reboot +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.reboot", + "id": 4665 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Restart a system service + +Commands a service to restart. The following pre-requisites must be +met to successfully perform this action: + +- The `provider` must NOT be `none`. +- The service must be present in the list of `allowed_services`. +- Moonraker must have the necessary permissions to manage services. + +```{.http .apirequest title="HTTP Request"} +POST /machine/services/restart +Content-Type: application/json + +{ + "service": "klipper" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.services.restart", + "params": { + "service": "klipper" + }, + "id": 4656 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| --------- | :----: | ------------ | ----------------------------------- | +| `service` | string | **REQUIRED** | The name of the service to restart. | +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Stop a system service + +Commands a service to stop. The following pre-requisites must be +met to successfully perform this action: + +- The `provider` must NOT be `none`. +- The service must be present in the list of `allowed_services`. +- Moonraker must have the necessary permissions to manage services. + +```{.http .apirequest title="HTTP Request"} +POST /machine/services/stop +Content-Type: application/json + +{ + "service": "klipper" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.services.stop", + "params": { + "service": "klipper" + }, + "id": 4645 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| --------- | :----: | ------------ | -------------------------------- | +| `service` | string | **REQUIRED** | The name of the service to stop. | +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Start a system service + +Commands a service to start. The following pre-requisites must be +met to successfully perform this action: + +- The `provider` must NOT be `none`. +- The service must be present in the list of `allowed_services`. +- Moonraker must have the necessary permissions to manage services. + +```{.http .apirequest title="HTTP Request"} +POST /machine/services/start +Content-Type: application/json + +{ + "service": "klipper" +} +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.services.start", + "params": { + "service": "klipper" + }, + "id": 4645 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| --------- | :----: | ------------ | --------------------------------- | +| `service` | string | **REQUIRED** | The name of the service to start. | +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Get process statistics + +Requests system usage information. This includes CPU usage, network usage, +etc. + +```{.http .apirequest title="HTTP Request"} +GET /machine/proc_stats +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.proc_stats", + "id": 7896 +} +``` +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "moonraker_stats": [ + { + "time": 1626612666.850755, + "cpu_usage": 2.66, + "memory": 24732, + "mem_units": "kB" + }, + { + "time": 1626612667.8521338, + "cpu_usage": 2.62, + "memory": 24732, + "mem_units": "kB" + } + ], + "throttled_state": { + "bits": 0, + "flags": [] + }, + "cpu_temp": 45.622, + "network": { + "lo": { + "rx_bytes": 113516429, + "tx_bytes": 113516429, + "bandwidth": 3342.68 + }, + "wlan0": { + "rx_bytes": 48471767, + "tx_bytes": 113430843, + "bandwidth": 4455.91 + } + }, + "system_cpu_usage": { + "cpu": 2.53, + "cpu0": 3.03, + "cpu1": 5.1, + "cpu2": 1.02, + "cpu3": 1 + }, + "system_uptime": 2876970.38089603, + "websocket_connections": 4 +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------------------- | :------------: | ----------------------------------------------------------------- | +| `moonraker_stats` | [object] | An array of `Moonraker Stats` objects. The array is a | +| | | FIFO queue, where the first index is the oldest sample. |^ +| | | Moonraker process stats are sampled roughly every second. |^ +| | | The maximum size of the queue is 30 samples. |^ +| | | #moonraker-proc-stat-spec |+ +| `throttled_state` | object \| null | An `Throttled State` object containing details about the CPU's | +| | | throttled state. This information is only available on Raspberry |^ +| | | Pi hosts, on other hardware this value will be `null`. |^ +| | | #throttled-state-spec |+ +| `cpu_temp` | float \| null | The current CPU temperature. Will be `null` if the | +| | | temperature data is unavailable. |^ +| `network` | object | A `Network Usage` object containing detailed network usage | +| | | data. Will be an empty object if this information is |^ +| | | unavailable. |^ +| | | #network-usage-spec |+ +| `system_cpu_usage` | object | A `CPU Usage` object containing detailed CPU usage data. | +| | | Will be an empty object if this information is unavailable. |^ +| | | #cpu-usage-spec |+ +| `system_memory` | object | A `Memory Usage` object containing detailed memory usage | +| | | data. Will be an empty object if this information is |^ +| | | unavailable. |^ +| | | #memory-usage-spec |+ +| `system_uptime` | float | The time elapsed, in seconds, since system boot. | +| `websocket_connections` | int | The current number of open websocket connections. | +{ #proc-stats-response-spec} + +| Field | Type | Description | +| ----------- | :------------: | ------------------------------------------------------------------- | +| `time` | float | The Unix time, in seconds, the sample was taken. | +| `cpu_usage` | float | The CPU usage of the moonraker process at this sample. | +| `memory` | int \| null | The memory usage of the moonraker process at this sample. This | +| | | should always be in kilobytes. The `mem_units` field may be used |^ +| | | to validate. Will be `null` if memory information is not available. |^ +| `mem_units` | string \| null | The memory units reported. Should always be `kB`. Will be `null` | +| | | if memory information is not available. |^ +{ #moonraker-proc-stat-spec } Moonraker Stats + +| Field | Type | Description | +| ------- | :------: | ----------------------------------------------------------- | +| `bits` | int | The current throttled state as an integer. A bitwise AND | +| | | can be performed against this value to generate custom flag |^ +| | | descriptions. |^ +| `flags` | [string] | A list of `Throttled Flags` describing the current state. | +| | | #throttled-flags-desc |+ +{ #throttled-state-spec } Throttled State + +| Flag | Bit Offset | +| ------------------------------ | :--------: | +| Under-Voltage Detected | `1 << 0` | +| Frequency Capped | `1 << 1` | +| Currently Throttled | `1 << 2` | +| Temperature Limit Active | `1 << 3` | +| Previously Under-Volted | `1 << 16` | +| Previously Frequency Capped | `1 << 17` | +| Previously Throttled | `1 << 18` | +| Previously Temperature Limited | `1 << 16` | +{ #throttled-flags-desc } Throttled Flags + +| Field | Type | Description | +| ---------- | :----: | ---------------------------------------------------------- | +| _variable_ | object | An object where the keys indicate a network interface name | +| | | and the values are `Interface Usage` objects containing |^ +| | | detailed usage info. Will be empty if no network |^ +| | | interfaces are detected. |^ +| | | #net-interface-usage-spec |+ +{ #network-usage-spec } Network Usage + +| Field | Type | Description | +| ------------ | :---: | ---------------------------------------------------------------------- | +| `bandwidth` | float | The current estimated bandwidth used in `bytes/sec`. This | +| | | value combines bytes received and bytes transmitted. |^ +| `rx_bytes` | int | Total bytes received since the interface was brought up. | +| `tx_bytes` | int | Total bytes transmitted since the interface was brought up. | +| `rx_packets` | int | Total packets received since the interface was brought up. | +| `tx_packets` | int | Total packets transmitted since the interface was brought up. | +| `rx_errs` | int | Total receive errors since the interface was brought up. | +| `tx_errs` | int | Total transmission errors since the interface was brought up. | +| `rx_drop` | int | Total receive packets dropped since the interface was brought up. | +| `tx_drop` | int | Total transmission packets dropped since the interface was brought up. | +{ #net-interface-usage-spec } Network Interface Usage + +| Field | Type | Description | +| ------ | :---: | ------------------------------------------------------------- | +| `cpu` | float | Current overall CPU usage expressed as a percentage (0-100). | +| `cpuX` | float | On multi-core CPU the usage for each individual CPU will be | +| | | reported. Starts at `cpu0`, the postfixed decimal increasing |^ +| | | for each core. As with `cpu`, this value is expressed as a |^ +| | | percentage. |^ +{ #cpu-usage-spec } CPU Usage + +| Field | Type | Description | +| ----------- | :--: | -------------------------------------- | +| `total` | int | Total memory in kilobytes. | +| `available` | int | Current available memory in kilobytes. | +| `used` | int | Currently used memory in kilobytes. | +{ #memory-usage-spec } Memory Usage + +/// + +## Get Sudo Info + +Retrieves sudo information status. Optionally checks if Moonraker has +permission to run commands as root. + +```{.http .apirequest title="HTTP Request"} +GET /machine/sudo/info?check_access=false +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.sudo.info", + "params": { + "check_access": false + }, + "id": 7896 +} +``` +/// api-parameters + open: True + +| Name | Type | Default | Description | +| -------------- | :--: | ------- | ------------------------------------------------ | +| `check_access` | bool | `false` | When `true` Moonraker will attempt to run a sudo | +| | | | command in a effort to check if sudo permission |^ +| | | | is available. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "sudo_access": null, + "linux_user": "pi", + "sudo_requested": false, + "request_messages": [] +} +``` +/// + +/// api-response-spec + open: True +| Field | Type | Description | +| ------------------ | :----------: | ------------------------------------------------------------------ | +| `sudo_access` | bool \| null | The result of a requested sudo permission check. Will | +| | | be `null` if the check was not requested via the |^ +| | | `check_access` parameter. |^ +| `linux_user` | string | The name of the linux user the Moonraker process belongs to. | +| `sudo_requested` | bool | Returns `true` if an internal Moonraker component has | +| | | requested a sudo password to perform some task, `false` otherwise. | +| `request_messages` | [string] | If one or more internal components have requested sudo access, | +| | | each will provide a description of the request available in this |^ +| | | array. |^ +/// + +## Set sudo password + +Sets the sudo password currently used by Moonraker. The password +is not persistent across Moonraker restarts. If Moonraker has one or +more pending sudo requests they will be processed. + +```{.http .apirequest title="HTTP Request"} +POST /machine/sudo/password +Content-Type: application/json + +{ + "password": "linux_user_password" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.sudo.password", + "params": { + "password": "linux_user_password" + }, + "id": 7896 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | ------------------------------------------ | +| `password` | string | **REQUIRED** | The linux user password necessary to grant | +| | | | Moonraker sudo permission. |^ + + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "sudo_responses": [ + "Sudo password successfully set." + ], + "is_restarting": false +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------------- | :------: | -------------------------------------------------------- | +| `sudo_responses` | [string] | If any Moonraker component has an outstanding sudo | +| | | it will process the task and provide a response included |^ +| | | in this array. |^ +| `is_restarting` | bool | If a processed sudo request intends to restart Moonraker | +| | | this value will be `true`, otherwise `false`. |^ + +/// + +/// Note +This request will return an error if the supplied password is +incorrect or if any pending sudo requests fail. +/// + +## List USB Devices + +Returns a list of all USB devices currently detected on the system. + +```{.http .apirequest title="HTTP Request"} +GET /machine/peripherals/usb +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.peripherals.usb", + "id": 7896 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "usb_devices": [ + { + "device_num": 1, + "bus_num": 1, + "vendor_id": "1d6b", + "product_id": "0002", + "usb_location": "1:1", + "manufacturer": "Linux 6.1.0-rpi7-rpi-v8 dwc_otg_hcd", + "product": "DWC OTG Controller", + "serial": "3f980000.usb", + "class": "Hub", + "subclass": null, + "protocol": "Single TT", + "description": "Linux Foundation 2.0 root hub" + }, + { + "device_num": 3, + "bus_num": 1, + "vendor_id": "046d", + "product_id": "0825", + "usb_location": "1:3", + "manufacturer": "Logitech, Inc.", + "product": "Webcam C270", + "serial": "", + "class": "Miscellaneous Device", + "subclass": null, + "protocol": "Interface Association", + "description": "Logitech, Inc. Webcam C270" + }, + { + "device_num": 2, + "bus_num": 1, + "vendor_id": "1a40", + "product_id": "0101", + "usb_location": "1:2", + "manufacturer": "Terminus Technology Inc.", + "product": "USB 2.0 Hub", + "serial": null, + "class": "Hub", + "subclass": null, + "protocol": "Single TT", + "description": "Terminus Technology Inc. Hub" + }, + { + "device_num": 5, + "bus_num": 1, + "vendor_id": "0403", + "product_id": "6001", + "usb_location": "1:5", + "manufacturer": "FTDI", + "product": "FT232R USB UART", + "serial": "", + "class": null, + "subclass": null, + "protocol": null, + "description": "Future Technology Devices International, Ltd FT232 Serial (UART) IC" + }, + { + "device_num": 4, + "bus_num": 1, + "vendor_id": "1d50", + "product_id": "614e", + "usb_location": "1:4", + "manufacturer": "Klipper", + "product": "stm32f407xx", + "serial": "", + "class": "Communications", + "subclass": null, + "protocol": null, + "description": "OpenMoko, Inc. Klipper 3d-Printer Firmware" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :------: | --------------------------------- | +| `usb_devices` | [object] | An array of `USB Device` objects. | +| | | #usb-device-spec |+ + + +| Field | Type | Description | +| -------------- | :-----: | --------------------------------------------------- | +| `bus_num` | int | The USB bus number as reported by the host. | +| `device_num` | int | The USB device number as reported by the host. | +| `usb_location` | string | A combination of the bus number and device number, | +| | | yielding a unique location ID on the host system. |^ +| `vendor_id` | string | The vendor ID as reported by the driver. | +| `product_id` | string | The product ID as reported by the driver. | +| `manufacturer` | string | The manufacturer name as reported by the driver. | +| | \| null | This will be `null` if no manufacturer is found. |^ +| `product` | string | The product description as reported by the driver. | +| | \| null | This will be `null` if no description is found. |^ +| `class` | string | The class description as reported by the driver. | +| | \| null | This will be `null` if no description is found. |^ +| `subclass` | string | The subclass description as reported by the driver. | +| | \| null | This will be `null` if no description is found. |^ +| `protocol` | string | The protocol description as reported by the driver. | +| | \| null | This will be `null` if no description is found. |^ +| `description` | string | The full device description string as reported by | +| | \| null | the usb.ids file. This will be `null` if no |^ +| | | description is found. |^ +{ #usb-device-spec } USB Device +/// + +## List Serial Devices + +Returns a list of all serial devices detected on the system. These may be USB +CDC-ACM devices or hardware UARTs. + +```{.http .apirequest title="HTTP Request"} +GET /machine/peripherals/serial +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.peripherals.serial", + "id": 7896 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "serial_devices": [ + { + "device_type": "hardware_uart", + "device_path": "/dev/ttyS0", + "device_name": "ttyS0", + "driver_name": "serial8250", + "path_by_hardware": null, + "path_by_id": null, + "usb_location": null + }, + { + "device_type": "usb", + "device_path": "/dev/ttyACM0", + "device_name": "ttyACM0", + "driver_name": "cdc_acm", + "path_by_hardware": "/dev/serial/by-path/platform-3f980000.usb-usb-0:1.2:1.0", + "path_by_id": "/dev/serial/by-id/usb-Klipper_stm32f407xx_unique_serial-if00", + "usb_location": "1:4" + }, + { + "device_type": "usb", + "device_path": "/dev/ttyUSB0", + "device_name": "ttyUSB0", + "driver_name": "ftdi_sio", + "path_by_hardware": "/dev/serial/by-path/platform-3f980000.usb-usb-0:1.4:1.0-port0", + "path_by_id": "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_unique_serial-if00-port0", + "usb_location": "1:5" + }, + { + "device_type": "hardware_uart", + "device_path": "/dev/ttyAMA0", + "device_name": "ttyAMA0", + "driver_name": "uart-pl011", + "path_by_hardware": null, + "path_by_id": null, + "usb_location": null + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ---------------- | -------- | ------------------------------------ | +| `serial_devices` | [object] | An array of `Serial Device` objects. | +| | | #serial-device-spec |+ + + +| Field | Type | Description | +| ------------------ | :-----: | ----------------------------------------------------------- | +| `device_type` | string | The type of serial device. Can be `hardware_uart` or `usb`. | +| `device_path` | string | The absolute file path to the device. | +| `device_name` | string | The device file name as reported by sysfs. | +| `driver_name` | string | The name of the device driver. | +| `path_by_hardware` | string | A symbolic link to the device based on its physical | +| | \| null | connection, ie: usb port. Will be `null` if no |^ +| | | matching link exists. |^ +| `path_by_id` | string | A symbolic link the the device based on its reported IDs. | +| | \| null | Will be `null` if no matching link exists. |^ +| `usb_location` | string | An identifier derived from the reported usb bus and . | +| | \| null | device numbers Can be used to match results from |^ +| | | `/machine/peripherals/usb`. Will be `null` for non-usb |^ +| | | devices. |^ +{ #serial-device-spec } Serial Device +/// + +## List Video Capture Devices + +Retrieves a list of V4L2 video capture devices on the system. If +the python3-libcamera system package is installed this request will +also return libcamera devices. + +```{.http .apirequest title="HTTP Request"} +GET /machine/peripherals/video +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.peripherals.video", + "id": 7896 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "v4l2_devices": [ + { + "device_name": "video0", + "device_path": "/dev/video0", + "camera_name": "unicam", + "driver_name": "unicam", + "hardware_bus": "platform:3f801000.csi", + "capabilities": [ + "VIDEO_CAPTURE", + "EXT_PIX_FORMAT", + "READWRITE", + "STREAMING", + "IO_MC" + ], + "version": "6.1.63", + "path_by_hardware": "/dev/v4l/by-path/platform-3f801000.csi-video-index0", + "path_by_id": null, + "alt_name": "unicam-image", + "usb_location": null, + "modes": [] + }, + { + "device_name": "video1", + "device_path": "/dev/video1", + "camera_name": "UVC Camera (046d:0825)", + "driver_name": "uvcvideo", + "hardware_bus": "usb-3f980000.usb-1.1", + "modes": [ + { + "format": "YUYV", + "description": "YUYV 4:2:2", + "flags": [], + "resolutions": [ + "640x480", + "160x120", + "176x144", + "320x176", + "320x240", + "352x288", + "432x240", + "544x288", + "640x360", + "752x416", + "800x448", + "800x600", + "864x480", + "960x544", + "960x720", + "1024x576", + "1184x656", + "1280x720", + "1280x960" + ] + }, + { + "format": "MJPG", + "description": "Motion-JPEG", + "flags": [ + "COMPRESSED" + ], + "resolutions": [ + "640x480", + "160x120", + "176x144", + "320x176", + "320x240", + "352x288", + "432x240", + "544x288", + "640x360", + "752x416", + "800x448", + "800x600", + "864x480", + "960x544", + "960x720", + "1024x576", + "1184x656", + "1280x720", + "1280x960" + ] + } + ], + "capabilities": [ + "VIDEO_CAPTURE", + "EXT_PIX_FORMAT", + "STREAMING" + ], + "version": "6.1.63", + "path_by_hardware": "/dev/v4l/by-path/platform-3f980000.usb-usb-0:1.1:1.0-video-index0", + "path_by_id": "/dev/v4l/by-id/usb-046d_0825_66EF0390-video-index0", + "alt_name": "UVC Camera (046d:0825)", + "usb_location": "1:3", + "modes": [ + { + "format": "YUYV", + "description": "YUYV 4:2:2", + "flags": [], + "resolutions": [ + "640x480", + "160x120", + "176x144", + "320x176", + "320x240", + "352x288", + "432x240", + "544x288", + "640x360", + "752x416", + "800x448", + "800x600", + "864x480", + "960x544", + "960x720", + "1024x576", + "1184x656", + "1280x720", + "1280x960" + ] + }, + { + "format": "MJPG", + "description": "Motion-JPEG", + "flags": [ + "COMPRESSED" + ], + "resolutions": [ + "640x480", + "160x120", + "176x144", + "320x176", + "320x240", + "352x288", + "432x240", + "544x288", + "640x360", + "752x416", + "800x448", + "800x600", + "864x480", + "960x544", + "960x720", + "1024x576", + "1184x656", + "1280x720", + "1280x960" + ] + } + ] + }, + { + "device_name": "video14", + "device_path": "/dev/video14", + "camera_name": "bcm2835-isp", + "driver_name": "bcm2835-isp", + "hardware_bus": "platform:bcm2835-isp", + "modes": [], + "capabilities": [ + "VIDEO_CAPTURE", + "EXT_PIX_FORMAT", + "STREAMING" + ], + "version": "6.1.63", + "path_by_hardware": null, + "path_by_id": null, + "alt_name": "bcm2835-isp-capture0", + "usb_location": null, + "modes": [] + }, + { + "device_name": "video15", + "device_path": "/dev/video15", + "camera_name": "bcm2835-isp", + "driver_name": "bcm2835-isp", + "hardware_bus": "platform:bcm2835-isp", + "modes": [], + "capabilities": [ + "VIDEO_CAPTURE", + "EXT_PIX_FORMAT", + "STREAMING" + ], + "version": "6.1.63", + "path_by_hardware": null, + "path_by_id": null, + "alt_name": "bcm2835-isp-capture1", + "usb_location": null, + "modes": [] + }, + { + "device_name": "video21", + "device_path": "/dev/video21", + "camera_name": "bcm2835-isp", + "driver_name": "bcm2835-isp", + "hardware_bus": "platform:bcm2835-isp", + "modes": [], + "capabilities": [ + "VIDEO_CAPTURE", + "EXT_PIX_FORMAT", + "STREAMING" + ], + "version": "6.1.63", + "path_by_hardware": "/dev/v4l/by-path/platform-bcm2835-isp-video-index1", + "path_by_id": null, + "alt_name": "bcm2835-isp-capture0", + "usb_location": null, + "modes": [] + }, + { + "device_name": "video22", + "device_path": "/dev/video22", + "camera_name": "bcm2835-isp", + "driver_name": "bcm2835-isp", + "hardware_bus": "platform:bcm2835-isp", + "modes": [], + "capabilities": [ + "VIDEO_CAPTURE", + "EXT_PIX_FORMAT", + "STREAMING" + ], + "version": "6.1.63", + "path_by_hardware": "/dev/v4l/by-path/platform-bcm2835-isp-video-index2", + "path_by_id": null, + "alt_name": "bcm2835-isp-capture1", + "usb_location": null, + "modes": [] + } + ], + "libcamera_devices": [ + { + "libcamera_id": "/base/soc/i2c0mux/i2c@1/ov5647@36", + "model": "ov5647", + "modes": [ + { + "format": "SGBRG10_CSI2P", + "resolutions": [ + "640x480", + "1296x972", + "1920x1080", + "2592x1944" + ] + } + ] + }, + { + "libcamera_id": "/base/soc/usb@7e980000/usb-port@1/usb-port@1-1.1:1.0-046d:0825", + "model": "UVC Camera (046d:0825)", + "modes": [ + { + "format": "MJPEG", + "resolutions": [ + "160x120", + "176x144", + "320x176", + "320x240", + "352x288", + "432x240", + "544x288", + "640x360", + "640x480", + "752x416", + "800x448", + "864x480", + "800x600", + "960x544", + "1024x576", + "960x720", + "1184x656", + "1280x720", + "1280x960" + ] + }, + { + "format": "YUYV", + "resolutions": [ + "160x120", + "176x144", + "320x176", + "320x240", + "352x288", + "432x240", + "544x288", + "640x360", + "640x480", + "752x416", + "800x448", + "864x480", + "800x600", + "960x544", + "1024x576", + "960x720", + "1184x656", + "1280x720", + "1280x960" + ] + } + ] + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------------- | :------: | --------------------------------------- | +| `v4l2_devices` | [object] | An array of `V4L2 Device` objects. | +| | | #v4l2-device-spec |+ +| `libcamera_devices` | [object] | An array of `Libcamera Device` objects. | +| | | #libcamera-device-spec |+ + +| Field | Type | Description | +| ------------------ | :------: | -------------------------------------------------------- | +| `device_name` | string | The V4L2 name assigned to the device. This is typically | +| | | the name of the file associated with the device. |^ +| `device_path` | string | The absolute system path to the device file. | +| `camera_name` | string | The camera name reported by the device driver. | +| `driver_name` | string | The name of the driver loaded for the device. | +| `alt_name` | string | An alternative device name optionally reported by | +| | \| null | sysfs. Will be `null` if the name file does not exist. |^ +| `hardware_bus` | string | A description of the hardware location of the device | +| `capabilities` | array | An array of strings indicating the capabilities the | +| | | device supports as reported by V4L2. |^ +| `version` | string | The device version as reported by V4L2. | +| `path_by_hardware` | string | A symbolic link to the device based on its physical | +| | \| null | connection, ie: usb port.. Will be `null` if no |^ +| | | matching link exists. |^ +| `path_by_id` | string | A symbolic link the the device based on its reported | +| | \| null | ID. Will be `null` if no matching link exists. |^ +| `usb_location` | string | An identifier derived from the reported usb bus and | +| | \| null | device numbers. Will be `null` for non-usb devices. |^ +| `modes` | [object] | An array of V4L2 Mode objects, each describing supported | +| | | modes. If no modes reporting discrete resolutions are |^ +| | | detected this array will be empty. |^ +| | | #v4l2-mode-spec |+ +{ #v4l2-device-spec } V4L2 Device + +| Field | Type | Description | +| ------------- | :------: | ------------------------------------------------------------ | +| `format` | string | The pixel format of the mode in V4l2 fourcc format. | +| `description` | string | A description of the mode provided by the driver. | +| `flags` | [string] | A list of strings indicating the special conditions relating | +| | | to the format. Can include `COMPRESSED` and/or `EMULATED`. |^ +| | | An empty array indicates no flags set. |^ +| `resolutions` | [string] | An array of strings describing the discrete resolutions | +| | | supported by the mode. Each entry is reported as |^ +| | | `x` |^ +{ #v4l2-mode-spec } V4L2 Mode + +| Field | Type | Description | +| -------------- | :------: | ------------------------------------------------------- | +| `libcamera_id` | string | The ID of the device as reported by libcamera. | +| `model` | string | The model name of the device. | +| `modes` | [object] | An array of `Libcamera Mode` objects, each describing a | +| | | mode supported by the device. |^ +| | | #libcamera-mode-spec |+ +{ #libcamera-device-spec } Libcamera Device + +| Field | Type | Description | +| ------------- | :------: | ----------------------------------------------------------- | +| `format` | string | The pixel format of the mode. | +| `resolutions` | [string] | An array of strings describing the resolutions supported by | +| | | the mode. Each entry is reported as `x` |^ +{ #libcamera-mode-spec } Libcamera Mode +/// + +## Query Unassigned Canbus UUIDs + +Queries the provided canbus interface for unassigned Klipper or Katapult +node IDs. + +!!! Warning + It is recommended that frontends provide users with an explanation + of how UUID queries work and the potential pitfalls when querying + a bus with multiple unassigned nodes. An "unassigned" node is a + CAN node that has not been activated by Katapult or Klipper. If + either Klipper or Katapult has connected to the node, it will be + assigned a Node ID and therefore will no longer respond to queries. + A device reset is required to remove the assignment. + + When multiple unassigned nodes are on the network, each responds to + the query at roughly the same time. This results in arbitration + errors. Nodes will retry the send until the response reports success. + However, nodes track the count of arbitration errors, and once a + specific threshold is reached they will go into a "bus off" state. A + device reset is required to reset the counter and recover from "bus off". + + For this reason, it is recommended that users only issue a query when + a single unassigned node is on the network. If a user does wish to + query multiple unassigned nodes it is vital that they reset all nodes + on the network before running Klipper. + +```{.http .apirequest title="HTTP Request"} +GET /machine/peripherals/canbus?interface=can0 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.peripherals.canbus", + "params": { + "interface": "can0" + }, + "id": 7896 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| ----------- | :----: | ------- | --------------------------------- | +| `interface` | string | `can0` | The cansocket interface to query. | +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "can_uuids": [ + { + "uuid": "11AABBCCDD", + "application": "Klipper" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ----------- | :------: | -------------------------------------------------------------- | +| `can_uuids` | [object] | An array of discovered `CAN UUID` objects, Will be empty if no | +| | | unassigned CAN nodes are found. |^ +| | | #can-uuid-spec |+ + + +| Field | Type | Description | +| ------------- | :----: | ----------------------------------------------------------- | +| `uuid` | string | The UUID of the unassigned node. | +| `application` | string | The name of the application running on the unassigned Node. | +| | | Should be "Klipper" or "Katapult". |^ +{ #can-uuid-spec } Can UUID +/// \ No newline at end of file diff --git a/docs/external_api/printer.md b/docs/external_api/printer.md new file mode 100644 index 0000000..0c5c24f --- /dev/null +++ b/docs/external_api/printer.md @@ -0,0 +1,667 @@ +# Printer Administration + +These endpoints provide access to printer state and printer control. +Klippy must be connected to Moonraker to receive a successful response, +and in many cases Klippy must be in the `ready` state. + +Most of these endpoints are registered with Moonraker by Klipper. +Requests pass from the frontend through Moonraker directly to Klipper. +Moonraker does not intervene. + +## Get Klippy host information + +```{.http .apirequest title="HTTP Request"} +GET /printer/info +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.info", + "id": 5445 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "state": "ready", + "state_message": "Printer is ready", + "hostname": "pi-debugger", + "klipper_path": "/home/pi/klipper", + "python_path": "/home/pi/klipper/venv/bin/python", + "process_id": 275124, + "user_id": 1000, + "group_id": 1000, + "log_file": "/home/pi/printer_data/logs/klippy.log", + "config_file": "/home/pi/printer_data/config/printer.cfg", + "software_version": "v0.12.0-85-gd785b396", + "cpu_info": "4 core ?" +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| ------------------ | :----: | ----------------------------------------------------- | +| `state` | string | Klippy's current [state](#klippy-state-desc). | +| `state_message` | string | A message describing Klippy's current state. | +| `hostname` | string | Hostname of the machine running Klippy. | +| `klipper_path` | string | Path on disk to the Klipper application. | +| `python_path` | string | Path on disk to the Python executable running Klippy. | +| `process_id` | int | The PID of the current Klippy process. | +| `user_id` | int | The UID of the user the Klippy process belongs to. | +| `group_id` | int | The GID of the group the Klippy process belongs to. | +| `log_file` | string | Path on disk to Klipper's log file. | +| `configfile` | string | Path on disk to Klipper's configuration file. | +| `software_version` | string | Version of the currently running instance of Klipper. | +| `cpu_info` | string | A brief description of the host machine's CPU. | + +| State | Description | +| ---------- | ------------------------------------------------------- | +| `ready` | Klippy has initialized and is ready for commands. | +| `startup` | Klippy is currently in its startup phase. | +| `error` | Klippy encountered an error during startup. | +| `shutdown` | Klippy is in the shutdown state. This can be initiated | +| | by the user via an emergency stop, or by the software |^ +| | if it encounters a critical error during operation. |^ +{ #klippy-state-desc } Klippy State +/// + +## Emergency Stop + +```{.http .apirequest title="HTTP Request"} +POST /printer/emergency_stop +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.emergency_stop", + "id": 4564 +} +``` + +/// Note +This endpoint will immediately halt the printer and put it in a "shutdown" +state. It should be used to implement an "emergency stop" button and +also used if a user enters `M112`(emergency stop) via a console. +/// + + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Host Restart + +Requests a Klipper "soft" restart. This will reload the Klippy application +and configuration. Connected MCUs will not be reset. + +```{.http .apirequest title="HTTP Request"} +POST /printer/restart +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.restart", + "id": 4894 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Firmware Restart + +Requests a complete Klipper restart. Both the Klippy Application and connected +MCUs will be reset. + +```{.http .apirequest title="HTTP Request"} +POST /printer/firmware_restart +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.firmware_restart", + "id": 8463 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Printer Status Requests + +### List loaded printer objects + +Returns a list of Klipper `printer objects` that are currently loaded. +This can be used to determine if a specific object is available for query +and/or subscription. + +```{.http .apirequest title="HTTP Request"} +GET /printer/objects/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.objects.list", + "id": 1454 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "objects": [ + "gcode", + "webhooks", + "configfile", + "mcu", + "mcu linux", + "heaters", + "bme280 chamber", + "temperature_sensor chamber", + "filament_switch_sensor extruder_sensor", + "output_pin sensor_toggle", + "gcode_move", + "bed_mesh", + "exclude_object", + "temperature_host RPi", + "temperature_sensor RPi", + "gcode_macro TURN_OFF_MOTORS", + "gcode_macro SET_HOMING_CURRENT", + "temperature_sensor ambient", + "gcode_macro query_bme280", + "pause_resume", + "print_stats", + "virtual_sdcard", + "probe", + "stepper_enable", + "tmc2130 stepper_x", + "tmc2130 stepper_y", + "tmc2130 stepper_z", + "tmc2130 extruder", + "heater_bed", + "heater_fan nozzle_cooling_fan", + "fan", + "menu", + "display_status", + "output_pin BEEPER_pin", + "idle_timeout", + "motion_report", + "query_endstops", + "system_stats", + "manual_probe", + "toolhead", + "extruder" + ] +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| --------- | :------: | ------------------------------------------------ | +| `objects` | [string] | A list Klipper printer objects currently loaded. | +/// + +### Query printer object status + +Requests the status of a provided set of printer objects. + +/// Tip +See the [Printer Objects](../printer_objects.md) document +for details on the objects available for query. +/// + +```{.http .apirequest title="HTTP Request"} +POST /printer/objects/query +Content-Type: application/json + +{ + "objects": { + "gcode_move": null, + "toolhead": ["position", "status"] + } +} +``` + +/// details | Using the Query String +The HTTP Request may also be performed using the query string. It is +recommended to send the request in the body unless otherwise not possible. + +```{.http .apirequest title="HTTP Request"} +GET /printer/objects/query?gcode_move&toolhead&extruder=target,temperature +``` + +The above will request a status update for all `gcode_move` and `toolhead` +attributes. Only the `temperature` and `target` attributes are requested +for the `extruder`. +/// + + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.objects.query", + "params": { + "objects": { + "gcode_move": null, + "toolhead": ["position", "status"] + } + }, + "id": 4654 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| --------- | :----: | ------------ | ------------------------------------------------------ | +| `objects` | object | **REQUIRED** | An object whose key, value pairs represent one or | +| | | | more [Printer Object Requests](#printer-obj-req-desc). |^ + +| Key Description | Value Description | +| ------------------------------- | ----------------------------------------------------------- | +| The `key`should be an available | The `value` specifies the attributes of the object that | +| Klipper printer object. | should be returned. If the value is `null` all attributes |^ +| { width=40% } | will be returned. Alternatively a list of strings |^ +| | specifying the desired attributes can be provided. |^ +{ #printer-obj-req-desc } Printer Object Request + +//// Note +If a requested printer object or attribute does not exist then the result +will be omitted from the response. No error is returned. +//// + +/// + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "eventtime": 578243.57824499, + "status": { + "gcode_move": { + "absolute_coordinates": true, + "absolute_extrude": true, + "extrude_factor": 1, + "gcode_position": [0, 0, 0, 0], + "homing_origin": [0, 0, 0, 0], + "position": [0, 0, 0, 0], + "speed": 1500, + "speed_factor": 1 + }, + "toolhead": { + "position": [0, 0, 0, 0], + "status": "Ready" + } + } +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| ----------- | :----: | -------------------------------------------------------------- | +| `eventtime` | float | The time at which the status was received, according Klipper's | +| | | monotonic clock. |^ +| `status` | object | An object containing the current state of the requested | +| | | printer objects. |^ +{ #object-query-response-spec } +/// + +### Subscribe to printer object status updates + +Requests status updates for a set of printer objects. A persistent +connection (Websocket or Unix Socket) is required to fulfill this +request. + +Status updates for subscribed objects are sent asynchronously over the +connection. See the +[notify_status_update](./jsonrpc_notifications.md#subscription-updates) +notification for details. + +/// Tip +See the [Printer Objects](../printer_objects.md) document +for details on the objects available for subscription. +/// + +```{.text .apirequest title="HTTP Request"} +Not available +``` + +```{.json .apirequest title="JSON-RPC Request (Websocket and Unix Socket Only)"} +{ + "jsonrpc": "2.0", + "method": "printer.objects.subscribe", + "params": { + "objects": { + "gcode_move": null, + "toolhead": ["position", "status"] + } + }, + "id": 5434 +} +``` + +/// api-parameters + open: True +Parameters are identical to the [query](#query-printer-object-status) +status parameters. A new request will override a previous request. +If `objects` is set to an empty object then the subscription will be +cancelled. +/// + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "eventtime": 578243.57824499, + "status": { + "gcode_move": { + "absolute_coordinates": true, + "absolute_extrude": true, + "extrude_factor": 1, + "gcode_position": [0, 0, 0, 0], + "homing_origin": [0, 0, 0, 0], + "position": [0, 0, 0, 0], + "speed": 1500, + "speed_factor": 1, + }, + "toolhead": { + "position": [0, 0, 0, 0], + "status": "Ready" + } + } +} +``` +//// + +/// api-response-spec + open: True +The response spec is identical to the [query response specification](#object-query-response-spec) +The response may be used to initialize local state without performing a +separate query. +/// + +### Query Endstops + +```{.http .apirequest title="HTTP Request"} +GET /printer/query_endstops/status +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.query_endstops.status", + "id": 3456 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "x": "TRIGGERED", + "y": "open", + "z": "open" +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| ---------- | :----: | ----------------------------------------------------------- | +| *variable* | string | The field is the name of the registered endstop. The value | +| | | will be `open` or `TRIGGERED`. |^ +/// + +## GCode APIs + +### Run a gcode command + +Executes a gcode command. Multiple commands may be executed by separating +them with a newline (`\n`). The request returns when the command or series +of commands have completed, or when the command results in an error. + +/// warning +When `M112`(emergency stop) is requested via this endpoint it will not +immediately stop the printer. `M112` will be placed on the gcode queue and +executed after all previous gcodes are complete. If a frontend detects +`M112` via user input (such as a console) it should request the +`/printer/emergency_stop` endpoint to immediately halt the printer. This +may be done in addition to sending the `M112` gcode if desired. +/// + +```{.http .apirequest title="HTTP Request"} +POST /printer/gcode/script +Content-Type: application/json + +{ + "script": "G28" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.gcode.script", + "params": { + "script": "G28" + }, + "id": 7466} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| -------- | :----: | ------------ | ------------------------------------------------- | +| `script` | string | **REQUIRED** | A GCode Command to run. Multiple commands may be | +| | | | specified, separated by a newline (`\n`). |^ +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +### Get GCode Help + +Retrieves a list of registered GCode Command Descriptions. Not all registered +GCode commands have a description, so this list should not be treated as an +exhaustive list of all supported commands. + +```{.http .apirequest title="HTTP Request"} +GET /printer/gcode/help +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.gcode.help", + "id": 4645 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "RESTART": "Reload config file and restart host software", + "FIRMWARE_RESTART": "Restart firmware, host, and reload config", + "STATUS": "Report the printer status", + "HELP": "Report the list of available extended G-Code commands", + "SAVE_CONFIG": "Overwrite config file and restart", + "SHUTDOWN_MACHINE": "G-Code macro", + "SET_GCODE_VARIABLE": "Set the value of a G-Code macro variable", + "REBOOT_MACHINE": "G-Code macro", + "UPDATE_DELAYED_GCODE": "Update the duration of a delayed_gcode", + "TURN_OFF_HEATERS": "Turn off all heaters", + "TEMPERATURE_WAIT": "Wait for a temperature on a sensor", + "QUERY_ADC": "Report the last value of an analog pin", + "QUERY_FILAMENT_SENSOR": "Query the status of the Filament Sensor", + "SET_FILAMENT_SENSOR": "Sets the filament sensor on/off", + "SET_PIN": "Set the value of an output pin", + "BED_MESH_CALIBRATE": "Perform Mesh Bed Leveling", + "BED_MESH_PROFILE": "Bed Mesh Persistent Storage management", + "BED_MESH_OUTPUT": "Retrieve interpolated grid of probed z-points", + "BED_MESH_MAP": "Serialize mesh and output to terminal", + "BED_MESH_CLEAR": "Clear the Mesh so no z-adjustment is made", + "BED_MESH_OFFSET": "Add X/Y offsets to the mesh lookup", + "SET_GCODE_OFFSET": "Set a virtual offset to g-code positions", + "SAVE_GCODE_STATE": "Save G-Code coordinate state", + "RESTORE_GCODE_STATE": "Restore a previously saved G-Code state", + "GET_POSITION": "Return information on the current location of the toolhead", + "EXCLUDE_OBJECT_START": "Marks the beginning the current object as labeled", + "EXCLUDE_OBJECT_END": "Marks the end the current object", + "EXCLUDE_OBJECT": "Cancel moves inside a specified objects", + "EXCLUDE_OBJECT_DEFINE": "Provides a summary of an object", + "TURN_OFF_MOTORS": "G-Code macro", + "CLEAR_PAUSE": "Clears the current paused state without resuming the print", + "SET_PRINT_STATS_INFO": "Pass slicer info like layer act and total to klipper", + "SDCARD_RESET_FILE": "Clears a loaded SD File. Stops the print if necessary", + "SDCARD_PRINT_FILE": "Loads a SD file and starts the print. May include files in subdirectories.", + "RESPOND": "Echo the message prepended with a prefix", + "PROBE": "Probe Z-height at current XY position", + "QUERY_PROBE": "Return the status of the z-probe", + "PROBE_CALIBRATE": "Calibrate the probe's z_offset", + "PROBE_ACCURACY": "Probe Z-height accuracy at current XY position", + "Z_OFFSET_APPLY_PROBE": "Adjust the probe's z_offset", + "GET_CURRENT_SKEW": "Report current printer skew", + "CALC_MEASURED_SKEW": "Calculate skew from measured print", + "SET_SKEW": "Set skew based on lengths of measured object", + "SKEW_PROFILE": "Profile management for skew_correction", + "SET_STEPPER_ENABLE": "Enable/disable individual stepper by name", + "SET_TMC_FIELD": "Set a register field of a TMC driver", + "INIT_TMC": "Initialize TMC stepper driver registers", + "SET_TMC_CURRENT": "Set the current of a TMC driver", + "DUMP_TMC": "Read and display TMC stepper driver registers", + "PID_CALIBRATE": "Run PID calibration test", + "SET_HEATER_TEMPERATURE": "Sets a heater temperature", + "SET_DISPLAY_TEXT": "Set or clear the display message", + "SET_DISPLAY_GROUP": "Set the active display group", + "STEPPER_BUZZ": "Oscillate a given stepper to help id it", + "FORCE_MOVE": "Manually move a stepper; invalidates kinematics", + "SET_KINEMATIC_POSITION": "Force a low-level kinematic position", + "SET_IDLE_TIMEOUT": "Set the idle timeout in seconds", + "QUERY_ENDSTOPS": "Report on the status of each endstop", + "SET_VELOCITY_LIMIT": "Set printer velocity limits", + "MANUAL_PROBE": "Start manual probe helper script", + "TUNING_TOWER": "Tool to adjust a parameter at each Z height", + "SET_PRESSURE_ADVANCE": "Set pressure advance parameters", + "SET_EXTRUDER_ROTATION_DISTANCE": "Set extruder rotation distance", + "SYNC_EXTRUDER_MOTION": "Set extruder stepper motion queue", + "SET_EXTRUDER_STEP_DISTANCE": "Set extruder step distance", + "SYNC_STEPPER_TO_EXTRUDER": "Set extruder stepper", + "ACTIVATE_EXTRUDER": "Change the active extruder", + "BASE_PAUSE": "Renamed builtin of 'PAUSE'", + "BASE_RESUME": "Renamed builtin of 'RESUME'", + "BASE_CANCEL_PRINT": "Renamed builtin of 'CANCEL_PRINT'", + "ACCEPT": "Accept the current Z position", + "ABORT": "Abort manual Z probing tool", + "TESTZ": "Move to new Z height" +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| ---------- | :----: | ----------------------------------------------------------------- | +| *variable* | string | The field is the name of the registered gcode command. The value | +| | | is a string containing the associated help descriptions. |^ + +//// Note +As mentioned previously, this list is not exhaustive. Help strings are not +available for default gcode handlers such as G1, G28, etc, nor are they +available for extended handlers that failed to register a description in +Klipper's python source. +//// + +/// + +## Print Job Management + +### Start a print job +```{.http .apirequest title="HTTP Request"} +POST /printer/print/start?filename=test_print.gcode +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.print.start", + "params": { + "filename": "test_print.gcode" + }, + "id": 4654 +} +``` + +/// api-parameters + open: true +| Name | Type | Default | Description | +| ---------- | :----: | ------------ | --------------------------------------------------- | +| `filename` | string | **REQUIRED** | The name of the gcode file to print. May be a path | +| | | | relative to the gcode folder. |^ +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +### Pause a print job +```{.http .apirequest title="HTTP Request"} +POST /printer/print/pause +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.print.pause", + "id": 4564 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +### Resume a print job + +```{.http .apirequest title="HTTP Request"} +POST /printer/print/resume +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.print.resume", + "id": 1465 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +### Cancel a print job + +```{.http .apirequest title="HTTP Request"} +POST /printer/print/cancel +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "printer.print.cancel", + "id": 2578 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` diff --git a/docs/external_api/server.md b/docs/external_api/server.md new file mode 100644 index 0000000..95c2884 --- /dev/null +++ b/docs/external_api/server.md @@ -0,0 +1,648 @@ +# Server Administration + +These endpoints provide access to server status, data tracking, and +administrative requests. + +## Query Server Info + +```{.http .apirequest title="HTTP Request"} +GET /server/info +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.info", + "id": 9546 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "klippy_connected": true, + "klippy_state": "ready", + "components": [ + "database", + "file_manager", + "klippy_apis", + "machine", + "data_store", + "shell_command", + "proc_stats", + "history", + "octoprint_compat", + "update_manager", + "power" + ], + "failed_components": [], + "registered_directories": ["config", "gcodes", "config_examples", "docs"], + "warnings": [ + "Invalid config option 'api_key_path' detected in section [authorization]. Remove the option to resolve this issue. In the future this will result in a startup error.", + "Unparsed config section [fake_section] detected. This may be the result of a component that failed to load. In the future this will result in a startup error." + ], + "websocket_count": 2, + "moonraker_version": "v0.7.1-105-ge4f103c", + "api_version": [1, 4, 0], + "api_version_string": "1.4.0" +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| ------------------------ | :------: | -------------------------------------------------------- | +| `klippy_connected` | bool | Moonraker's connection status to the Klippy Host. | +| `klippy_state` | string | Klippy's current state. Expand for available values. | +| #klippy-state-desc | | |+ +| `components` | [string] | A list of Moonraker components that are currently | +| | | loaded. |^ +| `failed_components` | [string] | A list of Moonraker components that failed to load. | +| `registered_directories` | [string] | A list "roots" registered with Moonraker's file manager. | +| `warnings` | [string] | A list of warning messages describing errors encountered | +| | | during initialization or regular operation. |^ +| `websocket_count` | int | The number of currently active websocket connections. | +| `moonraker_version` | string | The version of the Moonraker Application. | +| `api_version` | [int] | The version of the API in tuple format. | +| `api_version_string` | string | The version of the API in string format. | + +| State | Description | +| -------------- | ------------------------------------------------------------------ | +| `disconnected` | Moonraker is currently disconnect from Klippy. | +| `startup` | Klippy is currently initializing. | +| `ready` | Klippy is active and ready to receive commands. | +| `error` | Klippy experienced an error during startup. | +| `shutdown` | Klippy has been emergency stopped. This can occur at user request | +| | or if a critical error is encountered while running. |^ +{ #klippy-state-desc } +/// + +## Get Server Configuration + +```{.http .apirequest title="HTTP Request"} +GET /server/config +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.config", + "id": 5616 +} +``` + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "config": { + "server": { + "host": "0.0.0.0", + "port": 7125, + "ssl_port": 7130, + "enable_debug_logging": true, + "enable_asyncio_debug": false, + "klippy_uds_address": "/tmp/klippy_uds", + "max_upload_size": 210, + "ssl_certificate_path": null, + "ssl_key_path": null + }, + "dbus_manager": {}, + "database": { + "database_path": "~/.moonraker_database", + "enable_database_debug": false + }, + "file_manager": { + "enable_object_processing": true, + "queue_gcode_uploads": true, + "config_path": "~/printer_config", + "log_path": "~/logs" + }, + "klippy_apis": {}, + "machine": { + "provider": "systemd_dbus" + }, + "shell_command": {}, + "data_store": { + "temperature_store_size": 1200, + "gcode_store_size": 1000 + }, + "proc_stats": {}, + "job_state": {}, + "job_queue": { + "load_on_startup": true, + "automatic_transition": false, + "job_transition_delay": 2, + "job_transition_gcode": "\nM118 Transitioning to next job..." + }, + "http_client": {}, + "announcements": { + "dev_mode": false, + "subscriptions": [] + }, + "authorization": { + "login_timeout": 90, + "force_logins": false, + "cors_domains": [ + "*.home", + "http://my.mainsail.xyz", + "http://app.fluidd.xyz", + "*://localhost:*" + ], + "trusted_clients": [ + "192.168.1.0/24" + ] + }, + "zeroconf": {}, + "octoprint_compat": { + "enable_ufp": true, + "flip_h": false, + "flip_v": false, + "rotate_90": false, + "stream_url": "/webcam/?action=stream", + "webcam_enabled": true + }, + "history": {}, + "secrets": { + "secrets_path": "~/moonraker_secrets.ini" + }, + "mqtt": { + "address": "eric-work.home", + "port": 1883, + "username": "{secrets.mqtt_credentials.username}", + "password_file": null, + "password": "{secrets.mqtt_credentials.password}", + "mqtt_protocol": "v3.1.1", + "instance_name": "pi-debugger", + "default_qos": 0, + "status_objects": { + "webhooks": null, + "toolhead": "position,print_time", + "idle_timeout": "state", + "gcode_macro M118": null + }, + "api_qos": 0, + "enable_moonraker_api": true + }, + "template": {} + }, + "orig": { + "DEFAULT": {}, + "server": { + "enable_debug_logging": "True", + "max_upload_size": "210" + }, + "file_manager": { + "config_path": "~/printer_config", + "log_path": "~/logs", + "queue_gcode_uploads": "True", + "enable_object_processing": "True" + }, + "machine": { + "provider": "systemd_dbus" + }, + "announcements": {}, + "job_queue": { + "job_transition_delay": "2.", + "job_transition_gcode": "\nM118 Transitioning to next job...", + "load_on_startup": "True" + }, + "authorization": { + "trusted_clients": "\n192.168.1.0/24", + "cors_domains": "\n*.home\nhttp://my.mainsail.xyz\nhttp://app.fluidd.xyz\n*://localhost:*" + }, + "zeroconf": {}, + "octoprint_compat": {}, + "history": {}, + "secrets": { + "secrets_path": "~/moonraker_secrets.ini" + }, + "mqtt": { + "address": "eric-work.home", + "port": "1883", + "username": "{secrets.mqtt_credentials.username}", + "password": "{secrets.mqtt_credentials.password}", + "enable_moonraker_api": "True", + "status_objects": "\nwebhooks\ntoolhead=position,print_time\nidle_timeout=state\ngcode_macro M118" + } + }, + "files": [ + { + "filename": "moonraker.conf", + "sections": [ + "server", + "file_manager", + "machine", + "announcements", + "job_queue", + "authorization", + "zeroconf", + "octoprint_compat", + "history", + "secrets" + ] + }, + { + "filename": "include/extras.conf", + "sections": [ + "mqtt" + ] + } + ] +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| -------- | :------: | ---------------------------------------------------------------------- | +| `config` | object | An object containing the full Moonraker configuration. Each field of | +| | | this object is a section name. The value for each section is an |^ +| | | object mapping option names to values. Values are cast to their |^ +| | | internal type. Default values not specified in the configuration |^ +| | | files are included. |^ +| `orig` | object | An object containing the original configuration as read from the | +| | | configuration file(s). Like `config`, each field is a section name |^ +| | | and each value is a mapping of options to values. Only values present |^ +| | | in the configuration files are reported, and all values are strings. |^ +| `files` | [object] | An array of [File Objects](#file-object-spec) describing the config | +| | | files parsed. |^ + + +| Field | Type | Description | +| ---------- | :------: | ----------------------------------------------------------------- | +| `filename` | string | The name of the configuration file. This name is a path relative | +| | | to the main configuration file's parent folder. |^ +| `sections` | [string] | The config sections parsed from this file. | +{ #file-object-spec } File Object + +/// + +## Request Cached Temperature Data + +```{.http .apirequest title="HTTP Request"} +GET /server/temperature_store?include_monitors=false +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.temperature_store", + "params": { + "include_monitors": false + }, + "id": 2313 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| ------------------ | :--: | ------- | -------------------------------------------- | +| `include_monitors` | bool | `false` | When set to `true` the response will include | +| | | | sensors reported as `temperature monitors.` |^ +| | | | A temperature monitor is a specific type of |^ +| | | | sensor that may include `null` values in |^ +| | | | the `temperatures` field of the response. |^ +/// + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "extruder": { + "temperatures": [21.05, 21.12, 21.1, 21.1, 21.1], + "targets": [0, 0, 0, 0, 0], + "powers": [0, 0, 0, 0, 0] + }, + "temperature_fan my_fan": { + "temperatures": [21.05, 21.12, 21.1, 21.1, 21.1], + "targets": [0, 0, 0, 0, 0], + "speeds": [0, 0, 0, 0, 0] + }, + "temperature_sensor my_sensor": { + "temperatures": [21.05, 21.12, 21.1, 21.1, 21.1] + } +} +``` +//// + +/// api-response-spec + open: True +| Field | Type | Description | +| ---------- | :----: | ----------------------------------------------------------------------------- | +| *variable* | object | A primary object including zero or more [Sensor Objects](#sensor-obj-spec). | +| | | The `fields` in this object will be sensor names as reported by Klippy. |^ +| | | If Klippy has not been initialized or reports no sensors this object will |^ +| | | be empty. |^ + +| Field | Type | Description | +| -------------- | :------: | ------------------------------------------------------------------ | +| `temperatures` | [float?] | Contains the history of temperature measurements of this sensor. | +| | | If the sensor is a `temperature monitor` values may be `null`. A |^ +| | | `null` value indicates that the sensor recorded no measurement at |^ +| | | that time. |^ +| `targets` | [float] | Contains the history of temperature targets for heaters. | +| `speeds` | [float] | Contains a history of `speeds` for fans. This value should be | +| | | between 0 and 1 indicating the pwm duty cycle. |^ +| `powers` | [float] | Contains a history fof `powers` for heaters. This value should be | +| | | between 0 and 1 indicating the pwm duty cycle. |^ +{ #sensor-obj-spec } Sensor Object + +//// Note +Fields not reported by a sensor will be omitted in the `Sensor Object`. + +Each array in the `Sensor Object` is a FIFO queue, where the measurement at index 0 +is the oldest value. The time period between each measurement is 1 second. The +maximum length of the array is set in Moonraker's configuration, where the default is +1200 values. +//// + +/// + +## Request Cached GCode Responses + +```{.http .apirequest title="HTTP Request"} +GET /server/gcode_store?count=100 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.gcode_store", + "params": { + "count": 100 + }, + "id": 7643 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| ------- | :--: | ------------ | --------------------------------------------------- | +| `count` | int | *Store Size* | The number of cached gcode responses to return. The | +| | | | default is to return all cached items. |^ +/// + +//// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "gcode_store": [ + { + "message": "FIRMWARE_RESTART", + "time": 1615832299.1167388, + "type": "command" + }, + { + "message": "// Klipper state: Ready", + "time": 1615832309.9977088, + "type": "response" + }, + { + "message": "M117 This is a test", + "time": 1615834094.8662775, + "type": "command" + }, + { + "message": "G4 P1000", + "time": 1615834098.761729, + "type": "command" + }, + { + "message": "STATUS", + "time": 1615834104.2860553, + "type": "command" + }, + { + "message": "// Klipper state: Ready", + "time": 1615834104.3299904, + "type": "response" + } + ] +} +``` +//// + +/// api-response-spec + open: True + +| Field | Type | Description | +| ------------- | :------: | ---------------------------------------------------------------- | +| `gcode_store` | [object] | An array of [GCode Tracking Objects](#gc-tracking-obj-spec). | +| | | The array is a FIFO queue with the oldest item being at index 0. |^ + +| Field | Type | Description | +| --------- | :----: | ------------------------------------------------------------------ | +| `message` | string | The GCode Message associated with ths object. | +| `time` | float | The time at which the message was received reported in Unix Time. | +| `type` | string | The message type. Can be `command` or `response`. Commands are | +| | | only tracked when received through Moonraker's gcode API endpoint. |^ +{ #gc-tracking-obj-spec } GCode Tracking Object + +/// + +## Rollover Logs + +Requests a manual rollover for log files registered with Moonraker's +log management facility. Currently these are limited to `moonraker.log` +and `klippy.log`. + +/// Warning +Moonraker must be able to manage Klipper's systemd service to +perform a manual rollover. The rollover will fail under the following +conditions: + +- Moonraker cannot detect Klipper's systemd unit +- Moonraker cannot detect the location of Klipper's files +- A print is in progress +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/logs/rollover +Content-Type: application/json + +{ + "application": "moonraker" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.logs.rollover", + "params": { + "application": "moonraker" + }, + "id": 4656 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| ------------- | :----: | ------- | ------------------------------------------------------- | +| `application` | string | *all* | The name of the application for which the log should be | +| | | | rolled over. Can be `moonraker` or `klipper`. When no |^ +| | | | value is specified all logs are rolled over. |^ +/// + + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "rolled_over": [ + "moonraker", + "klipper" + ], + "failed": {} +} +``` +/// + +/// api-response-spec + open: True +| Field | Type | Description | +| ------------- | :------: | -------------------------------------------------------- | +| `rolled_over` | [string] | A list of application names successfully rolled over. | +| `failed` | object | An object where the fields consist of applications names | +| | | that failed the rollover procedure. The value assigned |^ +| | | to each field is an error message. |^ +/// + + +## Restart Server +```{.http .apirequest title="HTTP Request"} +POST /server/restart +``` +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.restart", + "id": 4656 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Identify Connection +This method provides a way for applications with persistent connections +to identify themselves to Moonraker. This information may be used by +Moonraker perform an action or present information based on if a specific +type of frontend is connected. Currently this method is only available +to websocket and unix socket connections. Once this endpoint returns +success it cannot be called again, repeated calls will result in an error. + +```{.text .apirequest title="HTTP request"} +Not Available +``` + +```{.json .apirequest title="JSON-RPC request (Websocket/Unix Socket Only)"} +{ + "jsonrpc": "2.0", + "method": "server.connection.identify", + "params": { + "client_name": "moontest", + "version": "0.0.1", + "type": "web", + "url": "http://github.com/arksine/moontest", + "access_token": "", + "api_key": "" + }, + "id": 4656 +} +``` + +/// api-parameters + open: True +| Name | Type | Default | Description | +| -------------- | :----: | ------------ | -------------------------------------------------- | +| `client_name` | string | **REQUIRED** | The name of the application identifying itself, | +| | | | ie: `Mainsail`, `Fluidd`, `KlipperScreen`, etc. |^ +| `version` | string | **REQUIRED** | The version of the application identifying itself. | +| `type` | string | **REQUIRED** | The type of the application. Expand for available | +| | | | values. |^ +| | | | #valid-id-type-desc |+ +| `url` | string | **REQUIRED** | The project URL or homepage for the application. | +| `access_token` | string | `null` | An optional JSON Web Token used to authenticate | +| | | | the websocket connection. Only needed when the |^ +| | | | app uses JWT authentication and did not |^ +| | | | authenticate through the original Websocket |^ +| | | | Request. |^ +| `api_key` | string | `null` | An optional API Key used to authenticate the | +| | | | connection. Only needed when the APP uses API |^ +| | | | Key authentication and did not authenticate |^ +| | | | through the original Websocket Request. |^ + +| Name | Description | +| --------- | --------------------------------------------------------------- | +| `web` | A web application like `Mainsail` and `Fluidd`. | +| `mobile` | A mobile application like `Mobileraker`. | +| `desktop` | A desktop application like `OrcaSlicer`. | +| `display` | An application intended to drive displays like `KlipperScreen`. | +| `bot` | An interactive bot like `MoonCord`. | +| `agent` | An external extension like `Obico`. | +| `other` | Anything that doesn't fit in to the above categories. | +{: #valid-id-type-desc } + +//// Note +When identifying as an `agent`, only one instance should be connected +to Moonraker at a time. If multiple agents of the same `client_name` +attempt to identify themselves this endpoint will return an error. +See the [extensions](./extensions.md) document for more information about +`agents`. +//// + +//// Tip +See the authorization API documentation for details on JWT and API Key authentication. +//// +/// + + +```{.json .apiresponse title="Example Response"} +{ + "connection_id": 1730367696 +} +``` + +/// api-response-spec + open: True +| Field | Type | Description | +| --------------- | :--: | ---------------------------------------- | +| `connection_id` | int | A unique identifier for this connection. | +/// + +## Get Websocket ID + +!!! Warning + This method is deprecated. Please use the + [identify endpoint](#identify-connection) to retrieve the + Websocket's UID + +```{.text .apirequest title="HTTP request"} +Not Available +``` + +```{json .apirequest title="JSON-RPC request (Websocket/Unix Socket Only)"} +{ + "jsonrpc": "2.0", + "method": "server.websocket.id", + "id": 4656 +} +``` + +```{.json .apiresponse title="Example Response"} +{ + "websocket_id": 1730367696 +} +``` + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------------- | :--: | ---------------------------------------- | +| `websocket_id` | int | A unique identifier for this connection. | +/// diff --git a/docs/external_api/update_manager.md b/docs/external_api/update_manager.md new file mode 100644 index 0000000..eca871c --- /dev/null +++ b/docs/external_api/update_manager.md @@ -0,0 +1,770 @@ +# Update Management + +The endpoints in the section are available when the `[update_manager]` +component has been configured in `moonraker.conf`. They may be used +to manage updates for Moonraker, Klipper, OS Packages, and additional +software added through the configuration. + +## Get update status + +```{.http .apirequest title="HTTP Request"} +GET /machine/update/status +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.status", + "id": 4644 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| --------- | :--: | ------- | ----------------------------------------------- | +| `refresh` | bool | false | *DEPRECATED*. When `true` an attempt will | +| | | | be made to refresh all updaters. The refresh |^ +| | | | will abort under the following conditions:
|^ +| | | | - an update is in progress
|^ +| | | | - a print is in progress
|^ +| | | | - the update manager hasn't completed |^ +| | | | initialization
|^ +| | | | - a refresh has been performed within the last |^ +| | | | 60 seconds
|^ + +//// Note +The `refresh` parameter is deprecated. Front end developers should use the +[refresh endpoint](#refresh-update-status) to request a refresh. +//// +/// + +/// collapse-code +```{.json #status-example-response .apiresponse title="Example Response"} +{ + "busy": false, + "github_rate_limit": 60, + "github_requests_remaining": 57, + "github_limit_reset_time": 1615836932, + "version_info": { + "system": { + "name": "system", + "configured_type": "system", + "package_count": 4, + "package_list": [ + "libtiff5", + "raspberrypi-sys-mods", + "rpi-eeprom-images", + "rpi-eeprom" + ] + }, + "moonraker": { + "channel": "dev", + "debug_enabled": true, + "is_valid": true, + "configured_type": "git_repo", + "corrupt": false, + "info_tags": [], + "detected_type": "git_repo", + "name": "moonraker", + "remote_alias": "arksine", + "branch": "master", + "owner": "arksine", + "repo_name": "moonraker", + "version": "v0.7.1-364", + "remote_version": "v0.7.1-364", + "rollback_version": "v0.7.1-360", + "current_hash": "ecfad5cff15fff1d82cb9bdc64d6b548ed53dfaf", + "remote_hash": "ecfad5cff15fff1d82cb9bdc64d6b548ed53dfaf", + "is_dirty": false, + "detached": true, + "commits_behind": [], + "git_messages": [], + "full_version_string": "v0.7.1-364-gecfad5c", + "pristine": true, + "recovery_url": "https://github.com/Arksine/moonraker.git", + "remote_url": "https://github.com/Arksine/moonraker.git", + "warnings": [], + "anomalies": [ + "Unofficial remote url: https://github.com/Arksine/moonraker-fork.git", + "Repo not on official remote/branch, expected: origin/master, detected: altremote/altbranch", + "Detached HEAD detected" + ] + }, + "mainsail": { + "name": "mainsail", + "owner": "mainsail-crew", + "version": "v2.1.1", + "remote_version": "v2.1.1", + "rollback_version": "v2.0.0", + "configured_type": "web", + "channel": "stable", + "info_tags": [ + "desc=Mainsail Web Client", + "action=some_action" + ], + "warnings": [], + "anomalies": [], + "is_valid": true + }, + "fluidd": { + "name": "fluidd", + "owner": "fluidd-core", + "version": "v1.16.2", + "remote_version": "v1.16.2", + "rollback_version": "v1.15.0", + "configured_type": "web", + "channel": "beta", + "info_tags": [], + "warnings": [], + "anomalies": [], + "is_valid": true + }, + "klipper": { + "channel": "dev", + "debug_enabled": true, + "is_valid": true, + "configured_type": "git_repo", + "corrupt": false, + "info_tags": [], + "detected_type": "git_repo", + "name": "klipper", + "remote_alias": "origin", + "branch": "master", + "owner": "Klipper3d", + "repo_name": "klipper", + "version": "v0.10.0-1", + "remote_version": "v0.10.0-41", + "rollback_version": "v0.9.1-340", + "current_hash": "4c8d24ae03eadf3fc5a28efb1209ce810251d02d", + "remote_hash": "e3cbe7ea3663a8cd10207a9aecc4e5458aeb1f1f", + "is_dirty": false, + "detached": false, + "commits_behind": [ + { + "sha": "e3cbe7ea3663a8cd10207a9aecc4e5458aeb1f1f", + "author": "Kevin O'Connor", + "date": "1644534721", + "subject": "stm32: Clear SPE flag on a change to SPI CR1 register", + "message": "The stm32 specs indicate that the SPE bit must be cleared before\nchanging the CPHA or CPOL bits.\n\nReported by @cbc02009 and @bigtreetech.\n\nSigned-off-by: Kevin O'Connor ", + "tag": null + }, + { + "sha": "99d55185a21703611b862f6ce4b80bba70a9c4b5", + "author": "Kevin O'Connor", + "date": "1644532075", + "subject": "stm32: Wait for transmission to complete before returning from spi_transfer()", + "message": "It's possible for the SCLK pin to still be updating even after the\nlast byte of data has been read from the receive pin. (In particular\nin spi mode 0 and 1.) Exiting early from spi_transfer() in this case\ncould result in the CS pin being raised before the final updates to\nSCLK pin.\n\nAdd an additional wait at the end of spi_transfer() to avoid this\nissue.\n\nSigned-off-by: Kevin O'Connor ", + "tag": null + } + ], + "git_messages": [], + "full_version_string": "v0.10.0-1-g4c8d24ae-shallow", + "pristine": true, + "recovery_url": "https://github.com/Klipper3d/klipper.git", + "remote_url": "https://github.com/Klipper3d/klipper.git", + "warnings": [], + "anomalies": [] + } + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------------------------- | :----: | ----------------------------------------------------- | +| `busy` | bool | Set to `true` if an update is currently in progress. | +| `github_rate_limit` | int | The maximum number of GitHub API requests allowed. | +| | | An unauthenticated user is typically allowed 60 |^ +| | | requests per hour. |^ +| `github_requests_remaining` | int | The number of GitHub API requests remaining until the | +| | | reset time is reached. |^ +| `github_limit_reset_time` | int | The time when the rate limit will reset, reported in | +| | | unix time. |^ +| `version_info` | object | A `Version Info` object containing the update status | +| | | for each configured software updater. |^ +| | | #version-info-desc |+ +{ #update-status-spec } + +| Field | Type | Description | +| --------- | :----: | ---------------------------------------------------------- | +| `system` | object | A [System Status](#system-status-spec) object. | +| | | This field is only available when the `update_manager` is |^ +| | | configured to update system packages. |^ +| _updater_ | object | A `Status Update` object. There are multiple sub-types of | +| | | status objects, the specific type can be determined by the |^ +| | | object's [configured_type](#configured-type-desc) field. |^ +| | | The `version_info` object may have multiple _updater_ |^ +| | | items. The field name for each updater will match the |^ +| | | object's `name`. |^ +{ #version-info-desc } Version Info + +| Type | Description | +| ---------- | ---------------------------------------------------------- | +| `git_repo` | A [Git Repo Status](#git-repo-status-spec) object. | +| | The software is distributed through a git repo on GitHub. |^ +| `web` | A [Net Hosted Status](#net-app-status-spec ) object. | +| | The software is a web application. |^ +| | Updates are GitHub hosted releases packaged in a zip file. |^ +| `zip` | A [Net Hosted Status](#net-app-status-spec ) object. | +| | The software is a local application, optionally installed |^ +| | as a system service. Updates are GitHub hosted releases |^ +| | packaged in a zip file. |^ +| `python` | A [Python Package Status](#python-status-spec) object. | +| | The software is a Python Application installed in its own |^ +| | virtualenv. Updates may be hosted on PyPI or GitHub. |^ +| | Pip is used to deploy updates. |^ +| `system` | A [System Status](#system-status-spec) object. This type | +| | is internally managed and only applicable to the system |^ +| | package manager. |^ +{ #configured-type-desc } Configured Types + +| Field | Type | Description | +| ---------------------- | :------: | -------------------------------------------------------- | +| `name` | string | The name of the software to manage updates for. | +| `configured_type` | string | The [type](#configured-type-desc) of updater configured. | +| `detected_type` | string | **DEPRECATED.** Will always report `git_repo`. | +| `channel` | string | The configured update `channel`. | +| | | #git-repo-channel-desc |+ +| `channel_invalid` | bool | A value of `true` indicates that the current `channel` | +| | | configuration is not supported by the type. Will |^ +| | | always be `false` for `git_repo` types as all channels |^ +| | | are supported. |^ +| `debug_enabled` | bool | Set to `true` when Moonraker's debug features are | +| | | enabled. In this condition updates may proceed when the |^ +| | | repo's HEAD is detached. |^ +| `is_valid` | bool | Set to `true` when repo detection completes and passes | +| | | all validity checks. |^ +| `version` | string | The current detected version. | +| `remote_version` | string | The latest version available on the remote. | +| `rollback_version` | string | The version prior to the last update. This version is | +| | | used during a `rollback` request. |^ +| `full_version_string` | string | The complete version string reported by `git describe`. | +| | | Generally includes an abbreviated hash of the current |^ +| | | commit and tags such as "dirty" when appropriate. |^ +| `remote_hash` | string | The latest available commit hash on the remote. | +| `current_hash` | string | The commit hash the local repo is currently on. | +| `remote_alias` | string | The git alias of the remote. The git default for the | +| | | primary alias is `origin`. |^ +| `remote_url` | string | Full URL of the git remote matching the current | +| | | `remote_alias`. |^ +| `recovery_url` | string | The `origin` git remote URL for this repo. This URL is | +| | | used to perform a `hard recovery` when requested. |^ +| `owner` | string | The owner of the remote repo as detected from the remote | +| | | URL. |^ +| `branch` | string | The name of the current git branch. | +| `repo_name` | string | The name of the remote repo as detected from the remote | +| | | URL. |^ +| `is_dirty` | bool | Set to `true` if the repo is "dirty", ie: if one or | +| | | more files in the repo have been modified. |^ +| `corrupt` | bool | Set to `true` if the repo is corrupt. This indicates | +| | | that the local repo is broken and needs to be recovered. |^ +| `pristine` | bool | Set to `true` when the repo is clean and no untracked | +| | | files exist in the repo. |^ +| `detached` | bool | Set to `true` when the git repo's HEAD is detached. | +| `git_messages` | [string] | An array of strings containing the output from a failed | +| | | `git` command during initialization or an update. This |^ +| | | array will be empty if all `git` commands succeed. |^ +| `anomalies` | [string] | An array of strings that describe anomalies found during | +| | | initialization. An anomaly can be defined as an |^ +| | | unexpected condition that does not result in an |^ +| | | `invalid` repo state. Updates may proceed when |^ +| | | anomalies are detected. An example of an anomaly is the |^ +| | | presence of "untracked files" in the repo. |^ +| `warnings` | [string] | An array of strings that describe warnings detected | +| | | during repo initialization. When a warning is present |^ +| | | the repo is marked invalid and updates are disabled. |^ +| `commits_behind` | [object] | An array of `Commit Info` objects providing commit data | +| | | on upstream commits available for update. This array is |^ +| | | limited to a size of 30 untagged commits. Any tagged |^ +| | | commits within 100 commits behind are included. |^ +| | | #git-commit-info-spec |+ +| `commits_behind_count` | int | The total number of commits the current repo is behind | +| | | the next update. This number may be greater than the |^ +| | | length of the `commits_behind` array. |^ +| `info_tags` | object | An object containing custom tags added to the updater's | +| | | configuration in `moonraker.conf`. The values will |^ +| | | always be strings. Client developers may define what |^ +| | | tags, if any, users will configure. The software can |^ +| | | then choose to display information or perform a |^ +| | | specific action pre/post update if necessary. |^ +{ #git-repo-status-spec } Git Repo Status + +| Channel | Description | +| -------- | --------------------------------------------------------- | +| `stable` | The repo will update to the latest tagged stable release. | +| `beta` | The repo will update to the latest tag. This may include | +| | tags with `beta` and `release candidate` identifiers. |^ +| `dev` | The repo will update to the latest available commit. | +{ #git-repo-channel-desc } + +| Field | Type | Description | +| --------- | :------------: | ----------------------------------------------------- | +| `author` | string | The author of the commit. | +| `date` | string | The date of the commit in unix time. Note that the | +| | | date is extracted from the git log as a string value. |^ +| | | It should be converted to an integer prior to |^ +| | | processing from unix time. |^ +| `sha` | string | The commit hash. | +| `subject` | string | The title of the commit. | +| `message` | string | The content in the body of the commit. | +| `tag` | string \| null | The name of the associated tag if present. Will be | +| | | null if the commit has no tag. |^ +{ #git-commit-info-spec } Commit Info + + +| Field | Type | Description | +| ------------------ | :------: | -------------------------------------------------------- | +| `name` | string | The name of the software to manage updates for. | +| `configured_type` | string | The [type](#configured-type-desc) of updater configured. | +| `channel` | string | The configured update `channel`. | +| | | #net-hosted-channel-desc |+ +| `channel_invalid` | bool | A value of `true` indicates that the current `channel` | +| | | configuration is not supported by the type. |^ +| | | are supported. |^ +| `debug_enabled` | bool | Set to `true` when Moonraker's debug features are | +| | | enabled. |^ +| `owner` | string | The owner of the GitHub repo hosting the software. | +| `repo_name` | string | The name of the GitHub repo hosting the software. | +| `last_error` | string | A message associated with the last error encountered | +| | | after initialization or an update. Will be an empty |^ +| | | string if no errors were detected. |^ +| `version` | string | The current detected version. | +| `remote_version` | string | The version of the latest available release on GitHub. | +| `rollback_version` | string | The version prior to the last update. This version is | +| | | used during a `rollback` request. |^ +| `is_valid` | bool | Set to `true` when the updater has completed | +| | | initialization and all validity checks passed. |^ +| `anomalies` | [string] | An array of strings that describe anomalies found during | +| | | initialization. An anomaly can be defined as an |^ +| | | unexpected condition that does not result in an |^ +| | | `invalid` updater state. Updates may proceed when |^ +| | | anomalies are detected. |^ +| `warnings` | [string] | An array of strings that describe warnings detected | +| | | during initialization. When a warning is present |^ +| | | the updater is marked invalid and updates are disabled. |^ +| `info_tags` | object | An object containing custom tags added to the updater's | +| | | configuration in `moonraker.conf`. The values will |^ +| | | always be strings. Client developers may define what |^ +| | | tags, if any, users will configure. The software can |^ +| | | then choose to display information or perform a |^ +| | | specific action pre/post update if necessary. |^ +{ #net-app-status-spec } Net Hosted Status + +| Channel | Description | +| -------- | --------------------------------------------------------- | +| `stable` | The software will update to the stable release on GitHub. | +| `beta` | The software will update to the latest release on GitHub, | +| | including those marked as "pre-release". |^ +{ #net-hosted-channel-desc } + +| Field | Type | Description | +| --------------------- | :------------: | --------------------------------------------------------- | +| `name` | string | The name of the software to manage updates for. | +| `configured_type` | string | The [type](#configured-type-desc) of updater configured. | +| `channel` | string | The configured update `channel`. | +| | | #python-channel-desc |+ +| `channel_invalid` | bool | A value of `true` indicates that the current `channel` | +| | | configuration is not supported by the type. |^ +| | | are supported. |^ +| `debug_enabled` | bool | Set to `true` when Moonraker's debug features are | +| | | enabled. |^ +| `owner` | string | The owner of the GitHub repo hosting the software. | +| | | Will be a `?` when no repo owner is detected. |^ +| `repo_name` | string | The name of the GitHub repo hosting the software. | +| | | Will be a `?` when no repo name is detected. |^ +| `branch` | string \| null | The name of the branch on the GitHub remote to build | +| | | `dev` updates from. Will be `null` if no primary branch |^ +| | | is configured. |^ +| `version` | string | The current detected version. | +| `remote_version` | string | The version of the latest available release on GitHub. | +| `rollback_version` | string | The version prior to the last update. This version is | +| | | used during a `rollback` request. |^ +| `full_version_string` | string | The complete version string extracted from the python | +| | | package's metadata. |^ +| `current_hash` | string | The hash of the commit used to build the current version | +| | | of the package. A placeholder of `not-specified` is used |^ +| | | when the the current hash is not provided in the package |^ +| | | metadata. |^ +| `remote_hash` | string | The hash of the latest update available. A placeholder of | +| | | `update-available` is used when the remote hash is not |^ +| | | provided by the remote host and updates are available. |^ +| `is_valid` | bool | Set to `true` when the updater has completed | +| | | initialization and all validity checks passed. |^ +| `is_dirty` | bool | Set to `true` if the repo was modified at the time the | +| | | package was built. |^ +| `changelog_url` | string | A URL to the software's changelog. Will be an empty | +| | | string if no changelog URL is detected. |^ +| `anomalies` | [string] | An array of strings that describe anomalies found during | +| | | initialization. An anomaly can be defined as an |^ +| | | unexpected condition that does not result in an |^ +| | | `invalid` updater state. Updates may proceed when |^ +| | | anomalies are detected. |^ +| `warnings` | [string] | An array of strings that describe warnings detected | +| | | during initialization. When a warning is present |^ +| | | the updater is marked invalid and updates are disabled. |^ +| `info_tags` | object | An object containing custom tags added to the updater's | +| | | configuration in `moonraker.conf`. The values will |^ +| | | always be strings. Client developers may define what |^ +| | | tags, if any, users will configure. The software can |^ +| | | then choose to display information or perform a |^ +| | | specific action pre/post update if necessary. |^ +{ #python-status-spec } Python Package Status + +| Channel | Description | +| -------- | -------------------------------------------------------- | +| `stable` | The software will update to the stable release on GitHub | +| | or PyPI. |^ +| `beta` | Only applies to packages installed via GitHub. The | +| | software will update to the latest release, including |^ +| | hose marked as "pre-release". |^ +| `dev` | Only applies to packages installed via GitHub. The | +| | software will update to the latest commit available. |^ +{ #python-channel-desc } + + +| Field | Type | Description | +| ----------------- | :------: | -------------------------------------------------------- | +| `name` | string | The name of the software to manage updates for. Will | +| | | always be `system`. |^ +| `configured_type` | string | The [type](#configured-type-desc) of updater configured. | +| | | Will always be `system`. |^ +| `package_count` | int | The number of system packages that require updating. | +| `package_list` | [string] | An array of package names that require updating. | +{ #system-status-spec } System Update Status + +/// + +## Refresh update status + +Refreshes the internal update state for the requested software. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/refresh +Content-Type: application/json + +{ + "name": "klipper" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.refresh", + "params": { + "name": "klipper" + }, + "id": 4644 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------- | ----------------------------------------------------- | +| `name` | string | `null` | The name of the software to refresh. | +| | | | If omitted all registered software will be refreshed. |^ + +/// + +/// Note +This endpoint will raise 503 error under the following conditions: + + - An update is in progress + - A print is in progress + - The update manager hasn't completed initialization +/// + +For an example response refer to the +[Status Example Response](#status-example-response). + +/// api-response-spec + open: True +The response spec is identical to the +[Status Request Specification](#update-status-spec) +/// + +/// Tip +Applications should use care when calling this method as a refresh +is CPU intensive and may be time consuming. Moonraker can be +configured to refresh state periodically, thus it is recommended +that applications avoid their own procedural implementations. +Instead it is best to call this API only when a user requests a +refresh. +/// + +## Perform an Upgrade + +*Added in API Version 1.5.0* + +Upgrade to the most recent release of the requested software. +If an update is requested while a print is in progress then this +request will return an error. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/upgrade +Content-Type: application/json + +{ + "name": "app_name" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.upgrade", + "params": { + "name": "app_name" + }, + "id": 8546 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------- | ------------------------------------ | +| `name` | string | null | The name of the software to upgrade. | +| | | | If omitted all registered |^ +| | | | software updates will be upgraded. |^ + +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Recover a corrupt repo + +On occasion a git command may fail resulting in a repo in a +dirty or invalid state. This endpoint may be used to attempt +to recover a git repo that is dirty, broken, or corrupt. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/recover +Content-Type: application/json + +{ + "name": "moonraker", + "hard": false +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.recover", + "params": { + "name": "moonraker", + "hard": false + }, + "id": 4564 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------------ | --------------------------------------------------- | +| `name` | string | **REQUIRED** | The name of the software to recover. | +| `hard` | bool | false | Determines the [mode](#git-repo-recovery-mode-desc) | +| | | | used to perform the recovery. |^ + +| Name | Description | +| --------------- | ------------------------------------------------------- | +| `hard == false` | Moonraker will attempt to recover the repo by running | +| | `git reset`. This will generally work for repos that |^ +| | are dirty, but will not correct repos that are corrupt. |^ +| `hard == true` | Moonraker will remove the current repo and re-clone it. | +{ #git-repo-recovery-mode-desc } Recovery Modes + +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Rollback to the previous version + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/rollback +Content-Type: application/json + +{ + "name": "moonraker" +} +``` + +JSON-RPC request: + +```json +{ + "jsonrpc": "2.0", + "method": "machine.update.rollback", + "params": { + "name": "moonraker" + }, + "id": 4564 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Perform a full update + +*Deprecated in API Version 1.5.0, superseded by the* +*[Upgrade](#perform-an-upgrade) endpoint.* + +Attempts to update all registered software. Updates are performed in the +following order: + +- `system` if enabled +- All optional software configured in `moonraker.conf`. +- Klipper +- Moonraker + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/full +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.full", + "id": 4645 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Update Moonraker + +*Deprecated in API Version 1.5.0, superseded by the* +*[Upgrade](#perform-an-upgrade) endpoint.* + +Upgrades to the latest version of Moonraker and restarts +the service. If an update is requested while a print is in progress then +this request will return an error. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/moonraker +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.moonraker", + "id": 4645 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Update Klipper + +*Deprecated in API Version 1.5.0, superseded by the* +*[Upgrade](#perform-an-upgrade) endpoint.* + +Upgrades to the latest version of Klipper and restarts +the service. If an update is requested while a print is in progress +then this request will return an error. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/klipper +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.klipper", + "id": 5745 +} +``` +```{.text .apiresponse title="Response"} +"ok" +``` + +## Update Client + +*Deprecated in API Version 1.5.0, superseded by the* +*[Upgrade](#perform-an-upgrade) endpoint.* + +Update to the most recent release of the requested software. +If an update is requested while a print is in progress then this +request will return an error. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/client +Content-Type: application/json + +{ + "name": "app_name" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.client", + "params": { + "name": "app_name" + }, + "id": 8546 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | ------- | ------------------------------------ | +| `name` | string | null | The name of the software to upgrade. | +| | | | If omitted all registered |^ +| | | | software updates will be upgraded. |^ + +/// + +```{.text .apiresponse title="Response"} +"ok" +``` + +## Update System Packages + +*Deprecated in API Version 1.5.0, superseded by the* +*[Upgrade](#perform-an-upgrade) endpoint.* + +Upgrades system packages. If an update is requested while a print is +in progress then this request will return an error. + +```{.http .apirequest title="HTTP Request"} +POST /machine/update/system +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "machine.update.system", + "id": 4564 +} +``` + +```{.text .apiresponse title="Response"} +"ok" +``` + + diff --git a/docs/external_api/webcams.md b/docs/external_api/webcams.md new file mode 100644 index 0000000..31fd5b8 --- /dev/null +++ b/docs/external_api/webcams.md @@ -0,0 +1,471 @@ +# Webcam Management + +Moonraker maintains webcam configuration in its database so +various applications and front-ends can share this configuration +through a consistent interface. The endpoints in this section +may be used to manage various webcam configurations. + +/// note +Moonraker does not directly manipulate webcams. +External applications, such as +[crowsnest](https://github.com/mainsail-crew/crowsnest), +handle direct webcam functionality. +/// + +## List Webcams + +```{.http .apirequest title="HTTP Request"} +GET /server/webcams/list +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.webcams.list", + "id": 4654 +} +``` + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "webcams": [ + { + "name": "testcam3", + "location": "door", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 20, + "target_fps_idle": 5, + "stream_url": "http://camera.lan/webcam?action=stream", + "snapshot_url": "http://camera.lan/webcam?action=snapshot", + "flip_horizontal": false, + "flip_vertical": true, + "rotation": 90, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "config", + "uid": "55d3801e-fdc1-438d-8728-2fff8b83b909" + }, + { + "name": "tc2", + "location": "printer", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 15, + "target_fps_idle": 5, + "stream_url": "http://printer.lan/webcam?action=stream", + "snapshot_url": "http://printer.lan/webcam?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "database", + "uid": "65e51c8a-6763-41d4-8e76-345bb6e8e7c3" + }, + { + "name": "TestCam", + "location": "printer", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 15, + "target_fps_idle": 5, + "stream_url": "/webcam/?action=stream", + "snapshot_url": "/webcam/?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "database", + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + } + ] +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| --------- | :------: | ------------------------------------------------------- | +| `webcams` | [object] | An array of [Webcam Entry](#webcam-entry-spec) objects. | + +| Field | Type | Description | +| ----------------- | :----: | ------------------------------------------------------------- | +| `name` | string | Friendly name of the webcam. | +| `location` | string | A single word description of where the webcam | +| | | is located or what it is viewing. |^ +| `service` | string | The name of the webcam streaming service used to | +| | | operate the webcam. |^ +| `enabled` | bool | Set to `true` when the webcam is available, `false` | +| | | otherwise. |^ +| `icon` | string | Name of the icon associated with the webcam. | +| `target_fps` | int | Target frames per second when the printer is active. | +| `target_fps_idle` | int | Target frames per second when the printer is idle. | +| `stream_url` | string | The url for the webcam's stream request. This may | +| | | be a complete url, or a url path relative to |^ +| | | Moonraker's host. |^ +| `snapshot_url` | string | The url for the webcam's snapshot request. This may | +| | | be a complete url, or a url path relative to |^ +| | | Moonraker's host. If the webcam does not support |^ +| | | a snapshot url this will be an empty string. |^ +| `flip_horizontal` | bool | A value of `true` indicates that the stream should | +| | | be flipped horizontally. |^ +| `flip_vertical` | bool | A value of `true` indicates that the stream should | +| | | be flipped vertically. |^ +| `rotation` | int | Indicates the amount of clockwise rotation, in | +| | | degrees, that should be applied to the stream. May |^ +| | | be 0, 90, 180, or 270. |^ +| `aspect_ratio` | string | Indicates the aspect ratio of the stream. The format | +| | | should be `W:H`, for example `4:3` or `16:9`. |^ +| `extra_data` | object | An object containing custom configuration added by | +| | | frontends. |^ +| `source` | string | The [configuration source](#webcam-configuration-source-desc) | +| | | of the webcam entry. |^ +| `uid` | string | A unique identifier assigned to the webcam entry. | +{ #webcam-entry-spec } Webcam Entry + +| Source | Description | +| ---------- | -------------------------------------------------------------- | +| `database` | The webcam's configuration is stored in Moonraker's database. | +| | These entries are generally added by front-ends via the webcam |^ +| | API. Front-ends may modify and remove these entries. |^ +| `config` | The webcam's configuration is sourced from `moonraker.conf`. | +| | The webcam endpoints can not modify or remove these entries. |^ +{ #webcam-configuration-source-desc } Configuration Source + +//// note +Moonraker does not provide a specification for the `location`, `service`, +and `icon` fields. These fields can contain any string value, generally +front-ends set these values based on their needs. Developers should +consider using the same values that existing front-ends (such as Mainsail +and Fluidd) currently use to maintain compatibility. +//// + +/// + +## Get Webcam Information + +```{.http .apirequest title="HTTP Request"} +GET /server/webcams/item?uid=341778f9-387f-455b-8b69-ff68442d41d9 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.webcams.get_item", + "params": { + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | -------------- | ----------------------------------------------- | +| `uid` | string | **REQUIRED** | The requested webcam's unique ID. While | +| | | | this parameter is considered required, if |^ +| | | | omitted the request will fall back on looking |^ +| | | | up the camera by `name`. |^ +| `name` | string | **DEPRECATED** | The requested webcam's friendly name. This | +| | | | parameter is deprecated, all future |^ +| | | | implementations should use the `uid` parameter. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "webcam": { + "name": "TestCam", + "location": "printer", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 15, + "target_fps_idle": 5, + "stream_url": "/webcam/?action=stream", + "snapshot_url": "/webcam/?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "database", + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------- | +| `webcam` | object | A [Webcam Entry](#webcam-entry-spec) object for | +| | | the requested webcam. |^ + +/// + +## Add or update a webcam + +Adds a new webcam entry or updates an existing entry. When updating +an entry only the fields provided will be modified. + +/// Note +A webcam configured in `moonraker.conf` cannot be updated or +overwritten using this API. +/// + +```{.http .apirequest title="HTTP Request"} +POST /server/webcams/item +Content-Type: application/json + +{ + "name": "cam_name", + "snapshot_url": "http://printer.lan:8080/webcam?action=snapshot", + "stream_url": "http://printer.lan:8080/webcam?action=stream" +} +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.webcams.post_item", + "params": { + "name": "cam_name", + "snapshot_url": "/webcam?action=snapshot", + "stream_url": "/webcam?action=stream" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +//// note +The default values in the parameter specification below apply to *new* webcam +entries. Existing entries to be updated only require the `uid` parameter, +all other parameters default to their existing value. +//// + +| Name | Type | Default | Description | +| ----------------- | :----: | --------------- | ---------------------------------------------------- | +| `uid` | string | null | The unique ID of an existing Webcam Entry to | +| | | | modify. If omitted the request will attempt |^ +| | | | to create a new Webcam Entry, otherwise the |^ +| | | | existing entry will be updated. |^ +| `name` | string | **REQUIRED** | The friendly name of the webcam. Each webcam | +| | | | entry must have a *unique* name. |^ +| `location` | string | "printer" | A single word description of where the webcam | +| | | | is located or what it is viewing. |^ +| `icon` | string | "mdiWebcam" | Name of the icon associated with the webcam. | +| `enabled` | bool | true | Set to `true` when the webcam is available, `false` | +| | | | otherwise. |^ +| `service` | string | "mjpegstreamer" | The name of the webcam streaming service used to | +| | | | operate the webcam. |^ +| `target_fps` | int | 15 | Target frames per second when the printer is active. | +| `target_fps_idle` | int | 5 | Target frames per second when the printer is idle. | +| `stream_url` | string | **REQUIRED** | The url for the webcam's stream request. This may | +| | | | be a complete url or a url path relative to |^ +| | | | Moonraker's host. |^ +| `snapshot_url` | string | "" | The url for the webcam's snapshot request. This may | +| | | | be a complete url or a url path relative to |^ +| | | | Moonraker's host. |^ +| `flip_horizontal` | bool | false | A value of `true` indicates that the stream should | +| | | | be flipped horizontally. |^ +| `flip_vertical` | bool | false | A value of `true` indicates that the stream should | +| | | | be flipped vertically. |^ +| `rotation` | int | 0 | Indicates the amount of clockwise rotation, in | +| | | | degrees, that should be applied to the stream. May |^ +| | | | be 0, 90, 180, or 270. |^ +| `aspect_ratio` | string | "4:3" | Indicates the aspect ratio of the stream. The format | +| | | | should be `W:H`, for example `4:3` or `16:9`. |^ +| `extra_data` | object | {} | An object containing custom configuration added by | +| | | | frontends. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "webcam": { + "name": "TestCam", + "location": "printer", + "service": "mjpegstreamer", + "enabled": true, + "icon": "mdiWebcam", + "target_fps": 15, + "target_fps_idle": 5, + "stream_url": "/webcam/?action=stream", + "snapshot_url": "/webcam/?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "aspect_ratio": "4:3", + "extra_data": {}, + "source": "database", + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------- | +| `webcam` | object | A [Webcam Entry](#webcam-entry-spec) object for | +| | | the new or updated webcam. |^ + +/// + +## Delete a webcam + +/// Note + A webcam configured via `moonraker.conf` cannot be deleted + using this API. +/// + +```{.http .apirequest title="HTTP Request"} +DELETE /server/webcams/item?uid=341778f9-387f-455b-8b69-ff68442d41d9 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.webcams.delete_item", + "params": { + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | -------------- | ----------------------------------------------- | +| `uid` | string | **REQUIRED** | The requested webcam's unique ID. While | +| | | | this parameter is considered required, if |^ +| | | | omitted the request will fall back on looking |^ +| | | | up the camera by `name`. |^ +| `name` | string | **DEPRECATED** | The requested webcam's friendly name. This | +| | | | parameter is deprecated, all future |^ +| | | | implementations should use the `uid` parameter. |^ + +/// + + +Parameters: + +- `uid`: The webcam's assigned unique ID. This parameter is optional, when + not specified the request will fallback to the `name` parameter. +- `name`: The name of the webcam to delete. If the named webcam is not + available the request will return with an error. This parameter must + be provided when the `uid` is omitted. + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "webcam": { + "name": "TestCam", + "location": "printer", + "service": "mjpegstreamer", + "target_fps": 15, + "stream_url": "/webcam/?action=stream", + "snapshot_url": "/webcam/?action=snapshot", + "flip_horizontal": false, + "flip_vertical": false, + "rotation": 0, + "source": "database", + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + } +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------- | :----: | ----------------------------------------------- | +| `webcam` | object | A [Webcam Entry](#webcam-entry-spec) object for | +| | | the deleted webcam. |^ + +/// + + +## Test a webcam + +Resolves a webcam's stream and snapshot urls. If the snapshot +is served over http, a test is performed to see if the url is +reachable. + +```{.http .apirequest title="HTTP Request"} +POST /server/webcams/test?uid=341778f9-387f-455b-8b69-ff68442d41d9 +``` + +```{.json .apirequest title="JSON-RPC Request"} +{ + "jsonrpc": "2.0", + "method": "server.webcams.test", + "params": { + "uid": "341778f9-387f-455b-8b69-ff68442d41d9" + }, + "id": 4654 +} +``` + +/// api-parameters + open: True + +| Name | Type | Default | Description | +| ------ | :----: | -------------- | ----------------------------------------------- | +| `uid` | string | **REQUIRED** | The requested webcam's unique ID. While | +| | | | this parameter is considered required, if |^ +| | | | omitted the request will fall back on looking |^ +| | | | up the camera by `name`. |^ +| `name` | string | **DEPRECATED** | The requested webcam's friendly name. This | +| | | | parameter is deprecated, all future |^ +| | | | implementations should use the `uid` parameter. |^ + +/// + +/// collapse-code +```{.json .apiresponse title="Example Response"} +{ + "name": "TestCam", + "snapshot_reachable": true, + "snapshot_url": "http://127.0.0.1:80/webcam/?action=snapshot", + "stream_url": "http://127.0.0.1:80/webcam/?action=stream" +} +``` +/// + +/// api-response-spec + open: True + +| Field | Type | Description | +| -------------------- | :----: | ------------------------------------------ | +| `name` | string | The friendly name of the webcam tested. | +| `snapshot_reachable` | bool | Value will be `true` if Moonraker is able | +| | | to successfully resolve and connect to the |^ +| | | snapshot url. |^ +| `snapshot_url` | string | The resolved snapshot url. | +| `stream_url` | string | The resolved stream url. | + +/// diff --git a/docs/index.md b/docs/index.md index b797f16..0e7ae7c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,8 @@ Users should refer to the [Installation](installation.md) and [Configuration](configuration.md) sections for documentation on how to install and configure Moonraker. -Client developers may refer to the [Client API](web_api.md) +Front end and other client developers may refer to the +[External API](./external_api/introduction.md) documentation. Backend developers should refer to the diff --git a/docs/installation.md b/docs/installation.md index da0f8c8..9df0bd1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,4 +1,4 @@ -## Installation +# This document provides a guide on how to install Moonraker on a Debian based Linux Distributions. Other linux distributions may work, however @@ -6,14 +6,14 @@ they may need a custom install script. Moonraker requires Python 3.7 or greater, verify that your distribution's Python 3 packages meet this requirement. -### Installing Klipper +## Installing Klipper Klipper should be installed prior to installing Moonraker. Please see [Klipper's Documentation](https://klipper3d.com/Overview.html) for details. After installing Klipper you should make sure to add Moonraker's [configuration requirements](#klipper-configuration-requirements). -#### Klipper Configuration Requirements +### Klipper Configuration Requirements Moonraker depends on the following Klippy extras for full functionality: @@ -34,7 +34,7 @@ missing one or both, you can simply add the bare sections to `printer.cfg`: path: ~/printer_data/gcodes ``` -#### Enabling Klipper's Unix Domain Socket Server +### Enabling Klipper's Unix Domain Socket Server After Klipper is installed it may be necessary to modify its `defaults` file in order to enable the Unix Domain Socket. Begin by opening the file in your @@ -92,7 +92,7 @@ mkdir ~/printer_data/config mv printer.cfg ~/printer_data/config ``` -### Installing Moonraker +## Installing Moonraker Moonraker provides an install script that can be used to facilitate installation. The type of installation depends on where the install @@ -105,7 +105,7 @@ Prior to installation it is necessary to open a terminal on the host machine, or SSH into it. It is recommended to read this entire section before proceeding with the installation. -#### Installing the Moonraker Python package +### Installing the Moonraker Python package The Python Package version of Moonraker will receive fewer updates, and should generally be more stable. This is intended for users that do @@ -123,7 +123,7 @@ wget https://raw.githubusercontent.com/Arksine/moonraker/master/scripts/install- ./install-moonraker.sh ``` -#### Installing Moonraker from source +### Installing Moonraker from source Moonraker can be run directly from source. This method of installation will clone Moonraker's git repository, and may receive frequent bleeding edge @@ -138,7 +138,7 @@ git clone https://github.com/Arksine/moonraker.git ~/moonraker/scripts/install-moonraker.sh ``` -#### Customizing the installation +### Customizing the installation The install script will attempt to create a basic configuration if `moonraker.conf` does not exist at the expected location, however if you @@ -218,7 +218,7 @@ Now you may wish to install a frontend, such as debian/ubuntu distros). -### Data Folder Structure +## Data Folder Structure As mentioned previously, files and folders used by Moonraker are organized in a primary data folder. The example below illustrates the folder @@ -299,7 +299,7 @@ systemd service unit, ie: when a file change is detected. The action taken depends on the "root" folder, thus it is important that they be distinct. -### The systemd service file +## The systemd service file The default installation will create `/etc/systemd/system/moonraker.service`. Below is a common example of service file, installed on a Raspberry Pi: @@ -340,7 +340,7 @@ Following are some items to take note of: the environment file. -### Command line usage +## Command line usage This section is intended for users that need to write their own installation script. Detailed are the command line arguments @@ -418,7 +418,7 @@ be used to specify options in place of the command line. [The environment file](#the-environment-file) may be used to set Moonraker's command line arguments and/or environment variables. -### The environment file +## The environment file The environment file, `moonraker.env`. is created in the data path during installation. A default installation's environment file will contain the path @@ -454,7 +454,7 @@ MOONRAKER_ARGS="-m moonraker" PYTHONPATH="/home/pi/moonraker" ``` -# Optional Speedups +## Optional Speedups Moonraker supports two optional Python packages that can be used to reduce its CPU load: @@ -480,7 +480,7 @@ environment variables in [moonraker.env](#the-environment-file): - `MOONRAKER_ENABLE_UVLOOP="n"` -### PolicyKit Permissions +## PolicyKit Permissions Some of Moonraker's components require elevated privileges to perform actions. Previously these actions could only be run via commandline programs launched @@ -548,7 +548,7 @@ enable_system_updates: False Previously installed PolicyKit rules can be removed by running `set-policykit-rules.sh -c` -### Completing Privileged Upgrades +## Completing Privileged Upgrades At times an update to Moonraker may require a change to the systemd service file, which requires sudo permission to complete. Moonraker will present @@ -579,7 +579,7 @@ bound to another port the user may specify a custom address and port. The API Key (`-k`) option is only necessary if the localhost is not authorized to access Moonraker's API. -### Retrieving the API Key +## Retrieving the API Key Some clients may require an API Key to connect to Moonraker. After the `[authorization]` component is first configured Moonraker will automatically @@ -617,7 +617,7 @@ sudo systemctl restart moonraker {"result": "8ce6ae5d354a4365812b83140ed62e4b"} -### Database Backup and Restore +## Database Backup and Restore Moonraker stores persistent data using an Sqlite database. By default the database file is located at `/database/moonraker-sql.db`. @@ -632,7 +632,7 @@ existing database file when the Moonraker service has been stopped. Restoration can be performed by stopping the Moonraker service and overwriting the existing database with the backup. -#### LDMB Database (deprecated) +### LDMB Database (deprecated) Previous versions of Moonraker used a [LMDB Database](http://www.lmdb.tech/doc/) for persistent storage of procedurally generated data. LMDB database files are @@ -697,7 +697,7 @@ contain credentials and other sensitive information, so users should treat this file accordingly. It is not recommended to keep backups in any folder served by Moonraker. -### Recovering a broken repo +## Recovering a broken repo Currently Moonraker is deployed using `git`. Without going into the gritty details,`git` is effectively a file system, and as such is subject to @@ -734,7 +734,7 @@ git clone https://github.com/Klipper3d/klipper.git sudo systemctl restart klipper ``` -### Debug options for developers +## Debug options for developers Moonraker accepts several command line arguments that can be used to assist both front end developers and developers interested in extending diff --git a/docs/moonraker.conf b/docs/moonraker.conf index 9951da2..f51b315 100644 --- a/docs/moonraker.conf +++ b/docs/moonraker.conf @@ -18,7 +18,7 @@ cors_domains: http://app.fluidd.xyz # Enable OctoPrint compatibility for Slicer uploads -# Supports Cura, Slic3r, and Slic3r dervivatives +# Supports Cura, Slic3r, and Slic3r derivatives # (PrusaSlicer, SuperSlicer) [octoprint_compat] # Default webcam config values: diff --git a/docs/printer_objects.md b/docs/printer_objects.md index 645594f..147ce6a 100644 --- a/docs/printer_objects.md +++ b/docs/printer_objects.md @@ -1,36 +1,93 @@ -# +# Printer Objects + +/// Note +For the most complete and up to date list of Klipper printer objects +available for query please see +[Klipper's Status Reference](https://www.klipper3d.org/Status_Reference.html). +The objects outlined in this document are subset of all objects available. In +addition it is possible that the object specifications here are out of date +relative to the latest commit pushed to Klipper's GitHub repo. + +The Printer Object Specifications in this document are current as of +Klipper Version `v0.12.0-430-g329fbd01d`. +/// + As mentioned in the API documentation, it is possible to -[query](web_api.md#query-printer-object-status) or -[subscribe](web_api.md#subscribe-to-printer-object-status) +[query](./external_api/printer.md#query-printer-object-status) or +[subscribe](./external_api/printer.md#subscribe-to-printer-object-status-updates) to "Klipper Printer Objects." There are numerous printer objects in Klipper, many of which are optional and only report status if they are -enabled by Klipper's configuration. Client's may retrieve a list of +enabled by Klipper's configuration. Clients may retrieve a list of available printer objects via the -[list objects endpoint](web_api.md#list-available-printer-objects). This -should be done after Klipper reports its state as "ready". +[list objects endpoint](./external_api/printer.md#list-loaded-printer-objects). +This should be done after Klipper reports its state as "ready". -This section will provide an overview of the most useful printer objects. -If a developer is interested in retrieving state for an object not listed here, -look in Klipper's source code for module you wish to query. If the module -contains a "get_status()" method, its return value will contain a dictionary -that reports state which can be queried. +/// Tip +There may be printer objects not documented here or in Klipper's +Status Reference. Developers interested in the state for such +objects will need to view Klippy's Python source to determine the nature +of the values reported. + +Look for a `get_status()` class method. The returned `dict` will indicate +the fields reported by the object. +/// + +/// Tip +Positional coordinates are expressed as 4 element float arrays. +The format is [X, Y, Z, E]. +/// ## webhooks -```json + +```{.json title="Printer Object Example"} { "state": "startup", "state_message": "message" } ``` -The `webhooks` object contains the current printer state and the current -state message. These fields match those returned via the `/printer/info` -endpoint. This is provided as a convience, clients may subscribe to `webhooks` -so they are asynchonously notified of a change to printer state. The `state` -may be `startup`, `ready`, `shutdown`, or `error`. The `state_message` -contains a message specific to the current printers state. + +| Field | Type | Description | +| --------------- | :----: | ---------------------------------------------------------------- | +| `state` | string | The current [state](./external_api/printer.md#klippy-state-desc) | +| | | reported by Klipper. |^ +| `state_message` | string | A message describing current state. | +{ #webhooks-object-spec } Webhooks Object + +## motion_report + +```{.json title="Printer Object Example"} +{ + "live_position": [0, 0, 0, 0], + "live_velocity": 0, + "live_extruder_velocity": 0, + "steppers": [ + "extruder", + "stepper_x", + "stepper_y", + "stepper_z" + ], + "trapq": [ + "extruder", + "toolhead" + ] +} +``` + +| Field | Type | Description | +| ------------------------ | :------: | --------------------------------------------- | +| `live_position` | [float] | The estimated real world position of the tool | +| | | at the time of the query. |^ +| `love_velocity` | float | The estimated real world velocity of the tool | +| | | at the time of the query. |^ +| `live_extruder_velocity` | float | The estimated real world velocity of the | +| | | active extruder at the time of the query. |^ +| `steppers` | [string] | An array of registered stepper names. | +| `trapq` | [string] | An array of registered trapq objects. | +{ #motion-report-object-spec } Motion Report Object ## gcode_move -```json + +```{.json title="Printer Object Example"} { "speed_factor": 1.0, "speed": 100.0, @@ -42,194 +99,747 @@ contains a message specific to the current printers state. "gcode_position": [0.0, 0.0, 0.0, 0.0] } ``` -The `gcode_move` object reports the current gcode state: -- `speed_factor`: AKA "feedrate", this is the current speed multiplier -- `speed`: The current gcode speed in mm/s. -- `extrude_factor`: AKA "extrusion multiplier". -- `absolute_coordinates`: true if the machine axes are moved using - absolute coordinates, false if using relative coordinates. -- `absolute_extrude`: true if the extruder is moved using absolute - coordinates, false if using relative coordinates. -- `homing_origin`: [X, Y, Z, E] - returns the "gcode offset" applied to - each axis. For example, the "Z" axis can be checked to determine how - much offset has been applied via "babystepping". -- `position`: [X, Y, Z, E] - The internal gcode position, including - any offsets (gcode_offset, G92, etc) added to an axis. -- `gcode_position`: [X, Y, Z, E] - The current gcode position - sans any offsets applied. For X, Y, and Z, this should match - the most recent "G1" or "G0" processed assuming the machine is - using absolute coordinates. +| Field | Type | Description | +| ---------------------- | :-----: | --------------------------------------------- | +| `speed_factor` | float | A speed multiplier applied to the move. Also | +| | | known as "feedrate percentage". |^ +| `speed` | float | Speed of the most recently processed gcode | +| | | move command in mm/s. |^ +| `extruder_factor` | float | An extrusion multiplier applied to the move. | +| `absolute_coordinates` | bool | True if the move is in absolute coordinates, | +| | | false if the move is relative. |^ +| `absolute_extrude` | bool | True if the extrusion move is in absolute | +| | | coordinates, false if relative. |^ +| `homing_origin` | [float] | A coordinate representing the amount of gcode | +| | | offset applied to each axis. |^ +| `position` | [float] | The current position after offsets are | +| | | applied. |^ +| `gcode_position` | [float] | The current position without any offsets. | +{ #gcode-move-object-spec } GCode Move Object -!!! Note - The printer's actual movement will lag behind the reported positional - coordinates due to lookahead. +/// Note +The printer's actual movement will lag behind the reported positional +coordinates due to lookahead. +/// ## toolhead -```json +```{.json title="Printer Object Example"} { "homed_axes": "xyz", - "print_time": 0.0, - "estimated_print_time": 0.0, + "axis_minimum": [0, -4, -2, 0], + "axis_maximum": [250, 210, 220, 0], + "print_time": 0.25, + "stalls": 0, + "estimated_print_time": 0, "extruder": "extruder", - "position": [0.0, 0.0, 0.0, 0.0], - "max_velocity": 500.0, - "max_accel": 3000.0, - "max_accel_to_decel": 1500.0, - "square_corner_velocity": 5.0 + "position": [0, 0, 0, 0], + "max_velocity": 300, + "max_accel": 1500, + "minimum_cruise_ratio": 0.5, + "square_corner_velocity": 5 } ``` -The `toolhead` object reports state of the current tool: -- `homed_axes`: a string containing the axes that are homed. If no axes - are homed, returns a null string. -- `print_time`: internal value, not generally useful for clients -- `estimated_print_time`: internal value, not generally useful for clients. -- `extruder`: the name of the currently selected extruder, ie "extruder" - or "extruder1". -- `position`: [X, Y, Z, E] - This the the last position toward which the tool - was commanded to move. It includes any offsets applied via gcode as well - as any transforms made by modules such as "bed_mesh", "bed_tilt", or - "skew_correction". -- `max_velocity`: The currently set maximum velocity of the tool (mm/s^2). -- `max_accel`: The currently set maximum acceleration of the tool (mm/s^2). -- `max_accel_to_decel`: The currently set maximum accel to decel of the tool. - This value is the maximum rate at which the tool can transition from - acceleration to deceleration (mm/s^2). -- `square_corner_velocity`: The currently set square corner velocity. This - is the maximum velocity at which the tool may travel a 90 degree corner. +| Field | Type | Description | +| ------------------------ | :-----: | ------------------------------------------------ | +| `homed_axes` | string | The current homed axes. Will be an empty string | +| | | if no axes are homed. |^ +| `axis_minimum` | [float] | A coordinate indicating the minimum valid move | +| | | location. |^ +| `axis_maximum` | [float] | A coordinate indicating the maximum valid move | +| | | location. |^ +| `cone_start_z` | float | Available for Delta printers only. The value is | +| | | the maximum z height at the maximum radius. |^ +| `print_time` | float | An internal value Klipper uses for scheduling | +| | | commands. |^ +| `estimated_print_time` | float | An internal value Klipper uses for scheduling | +| | | commands. |^ +| `extruder` | string | The name of the currently selected extruder. | +| `position` | [float] | A coordinate indicating the commanded position | +| | | of the toolhead. |^ +| `max_velocity` | float | The current maximum velocity limit. | +| `max_accel` | float | The current maximum acceleration limit. | +| `minimum_cruise_ratio` | float | The current minimum cruise ratio. This ratio | +| | | enforces the minimum portion of a move that |^ +| | | must occur at cruising speed. |^ +| `square_corner_velocity` | float | The current square corner velocity. This is the | +| | | maximum velocity the tool may travel a 90 |^ +| | | degree corner. |^ +{ #toolhead-object-spec } Toolhead Object -!!! tip - `max_velocity`, `max_accel`, `max_accel_to_decel`, and - `square_corner_velocity` can be changed by the `SET_VELOCITY_LIMIT` gcode. - `M204` can also change `max_accel`. +/// tip +The `max_velocity`, `max_accel`, `minimum_cruise_ratio`, and +`square_corner_velocity` can be changed by the `SET_VELOCITY_LIMIT` +gcode command. Their default values may be configured the `[printer]` +section of Klipper's `printer.cfg`. +/// ## configfile -```json + +/// collapse-code +```{.json title="Printer Object Example"} { - "config": {}, - "settings": {}, - "save_config_pending": false + "config": { + "mcu": { + "serial": "/dev/serial/by-id/usb" + }, + "exclude_object": {}, + "printer": { + "kinematics": "cartesian", + "max_velocity": "300", + "max_accel": "1500", + "max_z_velocity": "15", + "max_z_accel": "200" + }, + "stepper_x": { + "microsteps": "16", + "step_pin": "PC0", + "dir_pin": "!PL0", + "enable_pin": "!PA7", + "rotation_distance": "32", + "endstop_pin": "!PK2", + "position_endstop": "0", + "position_min": "0", + "position_max": "250", + "homing_speed": "50", + "homing_retract_dist": "0" + }, + "stepper_y": { + "microsteps": "16", + "step_pin": "PC1", + "dir_pin": "PL1", + "enable_pin": "!PA6", + "rotation_distance": "32", + "endstop_pin": "!PK7", + "position_endstop": "-4", + "position_max": "210", + "position_min": "-4", + "homing_speed": "50", + "homing_retract_dist": "0" + }, + "stepper_z": { + "microsteps": "16", + "step_pin": "PC2", + "dir_pin": "!PL2", + "enable_pin": "!PA5", + "rotation_distance": "8", + "endstop_pin": "probe:z_virtual_endstop", + "position_max": "220", + "position_min": "-2", + "homing_speed": "13.333" + }, + "extruder": { + "microsteps": "8", + "step_pin": "PC3", + "dir_pin": "PL6", + "enable_pin": "!PA4", + "rotation_distance": "6.53061216", + "full_steps_per_rotation": "400", + "nozzle_diameter": "0.4", + "filament_diameter": "1.750", + "max_extrude_cross_section": "50.0", + "max_extrude_only_distance": "500.0", + "max_extrude_only_velocity": "120.0", + "max_extrude_only_accel": "1250.0", + "heater_pin": "PE5", + "sensor_type": "ATC Semitec 104GT-2", + "sensor_pin": "PF0", + "control": "pid", + "pid_kp": "16.13", + "pid_ki": "1.1625", + "pid_kd": "56.23", + "min_temp": "0", + "max_temp": "305" + }, + "heater_bed": { + "heater_pin": "PG5", + "sensor_type": "EPCOS 100K B57560G104F", + "sensor_pin": "PF2", + "control": "pid", + "pid_kp": "126.13", + "pid_ki": "4.3", + "pid_kd": "924.76", + "min_temp": "0", + "max_temp": "125" + }, + "verify_heater heater_bed": { + "max_error": "240", + "check_gain_time": "120" + }, + "heater_fan nozzle_cooling_fan": { + "pin": "PH5", + "heater": "extruder", + "heater_temp": "50.0" + }, + "fan": { + "pin": "PH3" + }, + "display": { + "lcd_type": "hd44780", + "rs_pin": "PD5", + "e_pin": "PF7", + "d4_pin": "PF5", + "d5_pin": "PG4", + "d6_pin": "PH7", + "d7_pin": "PG3", + "encoder_pins": "^PJ1,^PJ2", + "click_pin": "^!PH6" + }, + "pause_resume": {}, + "virtual_sdcard": { + "path": "~/printer_data/gcodes" + }, + "respond": { + "default_type": "command" + }, + "probe": { + "pin": "PB4", + "x_offset": "23", + "y_offset": "5", + "z_offset": "0.8", + "speed": "12.0" + }, + "bed_mesh": { + "speed": "140", + "horizontal_move_z": "2", + "mesh_min": "24, 6", + "mesh_max": "238, 210", + "probe_count": "7", + "mesh_pps": "2", + "fade_start": "1", + "fade_end": "10", + "fade_target": "0", + "move_check_distance": "15", + "algorithm": "bicubic", + "bicubic_tension": ".2", + "zero_reference_position": "154.0, 113.0", + "faulty_region_1_min": "116.75, 41.81", + "faulty_region_1_max": "133.25, 78.81", + "faulty_region_2_min": "156.5, 99.31", + "faulty_region_2_max": "193.5, 115.81", + "faulty_region_3_min": "116.75, 136.21", + "faulty_region_3_max": "133.25, 173.31" + }, + "homing_override": { + "gcode": "\nG1 Z3 F600\nG28 X0 Y0\nG1 X131 Y108 F5000\nG28 Z0", + "axes": "Z", + "set_position_x": "0", + "set_position_y": "0", + "set_position_z": "0" + }, + "output_pin BEEPER_pin": { + "pin": "PH2", + "pwm": "True", + "value": "0", + "shutdown_value": "0", + "cycle_time": "0.001", + "scale": "1000" + }, + "force_move": { + "enable_force_move": "True" + }, + "idle_timeout": { + "gcode": "\nM104 S0\nM84" + }, + "gcode_macro PAUSE": { + "rename_existing": "BASE_PAUSE", + "gcode": "\n{% if not printer.pause_resume.is_paused %}\nM600\n{% endif %}" + }, + "gcode_macro M600": { + "variable_extr_temp": "0", + "gcode": "\n{% set X = params.X|default(100) %}\n{% set Y = params.Y|default(100) %}\n{% set Z = params.Z|default(100) %}\nBASE_PAUSE\nSET_GCODE_VARIABLE MACRO=M600 VARIABLE=extr_temp VALUE={printer.extruder.target}\nG91\n{% if printer.extruder.temperature|float > 180 %}\nG1 E-.8 F2700\n{% endif %}\nG1 Z{Z}\nG90\nG1 X{X} Y{Y} F3000" + }, + "gcode_macro RESUME": { + "rename_existing": "BASE_RESUME", + "gcode": "\n{% if printer.pause_resume.is_paused %}\n{% if printer[\"gcode_macro M600\"].extr_temp %}\nM109 S{printer[\"gcode_macro M600\"].extr_temp}\n{% endif %}\nBASE_RESUME\n{% endif %}" + } + }, + "warnings": [], + "save_config_pending": false, + "save_config_pending_items": {}, + "settings": { + "mcu": { + "serial": "/dev/serial/by-id/usb", + "baud": 250000, + "max_stepper_error": 0.000025 + }, + "heater_bed": { + "sensor_type": "EPCOS 100K B57560G104F", + "pullup_resistor": 4700, + "inline_resistor": 0, + "sensor_pin": "PF2", + "min_temp": 0, + "max_temp": 125, + "min_extrude_temp": 170, + "max_power": 1, + "smooth_time": 1, + "control": "pid", + "pid_kp": 126.13, + "pid_ki": 4.3, + "pid_kd": 924.76, + "heater_pin": "PG5", + "pwm_cycle_time": 0.1 + }, + "verify_heater heater_bed": { + "hysteresis": 5, + "max_error": 240, + "heating_gain": 2, + "check_gain_time": 120 + }, + "heater_fan nozzle_cooling_fan": { + "heater": [ + "extruder" + ], + "heater_temp": 50, + "max_power": 1, + "kick_start_time": 0.1, + "off_below": 0, + "cycle_time": 0.01, + "hardware_pwm": false, + "shutdown_speed": 1, + "pin": "PH5", + "fan_speed": 1 + }, + "fan": { + "max_power": 1, + "kick_start_time": 0.1, + "off_below": 0, + "cycle_time": 0.01, + "hardware_pwm": false, + "shutdown_speed": 0, + "pin": "PH3" + }, + "display": { + "lcd_type": "hd44780", + "rs_pin": "PD5", + "e_pin": "PF7", + "d4_pin": "PF5", + "d5_pin": "PG4", + "d6_pin": "PH7", + "d7_pin": "PG3", + "hd44780_protocol_init": true, + "line_length": 20, + "menu_root": "__main", + "menu_timeout": 0, + "menu_reverse_navigation": false, + "encoder_pins": "^PJ1,^PJ2", + "encoder_steps_per_detent": 4, + "encoder_fast_rate": 0.03, + "click_pin": "^!PH6", + "display_group": "_default_20x4" + }, + "pause_resume": { + "recover_velocity": 50 + }, + "virtual_sdcard": { + "path": "~/printer_data/gcodes", + "on_error_gcode": "\n{% if 'heaters' in printer %}\n TURN_OFF_HEATERS\n{% endif %}\n" + }, + "respond": { + "default_type": "command", + "default_prefix": "//" + }, + "probe": { + "z_offset": 0.8, + "deactivate_on_each_sample": true, + "activate_gcode": "", + "deactivate_gcode": "", + "pin": "PB4", + "x_offset": 23, + "y_offset": 5, + "speed": 12, + "lift_speed": 12, + "samples": 1, + "sample_retract_dist": 2, + "samples_result": "average", + "samples_tolerance": 0.1, + "samples_tolerance_retries": 0 + }, + "bed_mesh": { + "adaptive_margin": 0, + "probe_count": [ + 7 + ], + "mesh_min": [ + 24, + 6 + ], + "mesh_max": [ + 238, + 210 + ], + "mesh_pps": [ + 2 + ], + "algorithm": "bicubic", + "bicubic_tension": 0.2, + "scan_overshoot": 0, + "zero_reference_position": [ + 154, + 113 + ], + "horizontal_move_z": 2, + "speed": 140, + "faulty_region_1_min": [ + 116.75, + 41.81 + ], + "faulty_region_1_max": [ + 133.25, + 78.81 + ], + "faulty_region_2_min": [ + 156.5, + 99.31 + ], + "faulty_region_2_max": [ + 193.5, + 115.81 + ], + "faulty_region_3_min": [ + 116.75, + 136.21 + ], + "faulty_region_3_max": [ + 133.25, + 173.31 + ], + "fade_start": 1, + "fade_end": 10, + "fade_target": 0, + "split_delta_z": 0.025, + "move_check_distance": 15 + }, + "homing_override": { + "set_position_x": 0, + "set_position_y": 0, + "set_position_z": 0, + "axes": "Z", + "gcode": "\nG1 Z3 F600\nG28 X0 Y0\nG1 X131 Y108 F5000\nG28 Z0" + }, + "output_pin beeper_pin": { + "pwm": true, + "pin": "PH2", + "cycle_time": 0.001, + "hardware_pwm": false, + "scale": 1000, + "value": 0, + "shutdown_value": 0 + }, + "force_move": { + "enable_force_move": true + }, + "idle_timeout": { + "timeout": 600, + "gcode": "\nM104 S0\nM84" + }, + "gcode_macro pause": { + "gcode": "\n{% if not printer.pause_resume.is_paused %}\nM600\n{% endif %}", + "rename_existing": "BASE_PAUSE", + "description": "G-Code macro" + }, + "gcode_macro m600": { + "gcode": "\n{% set X = params.X|default(100) %}\n{% set Y = params.Y|default(100) %}\n{% set Z = params.Z|default(100) %}\nBASE_PAUSE\nSET_GCODE_VARIABLE MACRO=M600 VARIABLE=extr_temp VALUE={printer.extruder.target}\nG91\n{% if printer.extruder.temperature|float > 180 %}\nG1 E-.8 F2700\n{% endif %}\nG1 Z{Z}\nG90\nG1 X{X} Y{Y} F3000", + "description": "G-Code macro", + "variable_extr_temp": "0" + }, + "gcode_macro resume": { + "gcode": "\n{% if printer.pause_resume.is_paused %}\n{% if printer[\"gcode_macro M600\"].extr_temp %}\nM109 S{printer[\"gcode_macro M600\"].extr_temp}\n{% endif %}\nBASE_RESUME\n{% endif %}", + "rename_existing": "BASE_RESUME", + "description": "G-Code macro" + }, + "printer": { + "max_velocity": 300, + "max_accel": 1500, + "minimum_cruise_ratio": 0.5, + "square_corner_velocity": 5, + "kinematics": "cartesian", + "max_z_velocity": 15, + "max_z_accel": 200 + }, + "stepper_x": { + "step_pin": "PC0", + "dir_pin": "!PL0", + "rotation_distance": 32, + "microsteps": 16, + "full_steps_per_rotation": 200, + "gear_ratio": [], + "enable_pin": "!PA7", + "endstop_pin": "!PK2", + "position_endstop": 0, + "position_min": 0, + "position_max": 250, + "homing_speed": 50, + "second_homing_speed": 25, + "homing_retract_speed": 50, + "homing_retract_dist": 0, + "homing_positive_dir": false + }, + "stepper_y": { + "step_pin": "PC1", + "dir_pin": "PL1", + "rotation_distance": 32, + "microsteps": 16, + "full_steps_per_rotation": 200, + "gear_ratio": [], + "enable_pin": "!PA6", + "endstop_pin": "!PK7", + "position_endstop": -4, + "position_min": -4, + "position_max": 210, + "homing_speed": 50, + "second_homing_speed": 25, + "homing_retract_speed": 50, + "homing_retract_dist": 0, + "homing_positive_dir": false + }, + "stepper_z": { + "step_pin": "PC2", + "dir_pin": "!PL2", + "rotation_distance": 8, + "microsteps": 16, + "full_steps_per_rotation": 200, + "gear_ratio": [], + "enable_pin": "!PA5", + "endstop_pin": "probe:z_virtual_endstop", + "position_min": -2, + "position_max": 220, + "homing_speed": 13.333, + "second_homing_speed": 6.6665, + "homing_retract_speed": 13.333, + "homing_retract_dist": 5, + "homing_positive_dir": false + }, + "extruder": { + "sensor_type": "ATC Semitec 104GT-2", + "pullup_resistor": 4700, + "inline_resistor": 0, + "sensor_pin": "PF0", + "min_temp": 0, + "max_temp": 305, + "min_extrude_temp": 170, + "max_power": 1, + "smooth_time": 1, + "control": "pid", + "pid_kp": 16.13, + "pid_ki": 1.1625, + "pid_kd": 56.23, + "heater_pin": "PE5", + "pwm_cycle_time": 0.1, + "nozzle_diameter": 0.4, + "filament_diameter": 1.75, + "max_extrude_cross_section": 50, + "max_extrude_only_velocity": 120, + "max_extrude_only_accel": 1250, + "max_extrude_only_distance": 500, + "instantaneous_corner_velocity": 1, + "step_pin": "PC3", + "pressure_advance": 0, + "pressure_advance_smooth_time": 0.04, + "dir_pin": "PL6", + "rotation_distance": 6.53061216, + "microsteps": 8, + "full_steps_per_rotation": 400, + "gear_ratio": [], + "enable_pin": "!PA4" + }, + "verify_heater extruder": { + "hysteresis": 5, + "max_error": 120, + "heating_gain": 2, + "check_gain_time": 20 + } + } } ``` -The `configfile` object reports printer configuration state: +/// -- `config`: This is an object containing the configuration as read from - printer.cfg. Each config section will be an object containing the - configured options. Values will ALWAYS be reported as - strings. Note that default values are not reported, only options - configured in printer.cfg are present. -- `settings`: Similar to `config`, however this object includes default - values that may not have been included in `printer.cfg`. It is possible - for a value to be a string, integer, boolean, or float. -- `save_config_pending`: True if the printer has taken an action which - has updated the internal configuration (ie: PID calibration, probe - calibration, bed mesh calibration). This allows clients to present - the user with the option to execute a SAVE_CONFIG gcode which will - save the configuration to printer.cfg and restart the Klippy Host. +| Field | Type | Description | +| --------------------------- | :------: | -------------------------------------------------- | +| `config` | object | An object containing the raw config as parsed | +| | | from Klipper's config file. The keys are |^ +| | | `section` names, the value for each section is |^ +| | | an object containing `option: value` pairs. |^ +| | | The values for each option will always be |^ +| | | strings. |^ +| `settings` | object | An object containing the parsed configuration | +| | | for all loaded Klipper objects. Each key |^ +| | | is a `Klipper object` name, the values are |^ +| | | objects containing `setting: value` pairs. |^ +| | | The values will be converted to the type requested |^ +| | | during parsing. Settings with default values |^ +| | | may be present without a corresponding `option` |^ +| | | in the `config`. It is also possible |^ +| | | for an entire `Klipper object` to exist without |^ +| | | a corresponding `section` in the `config`. |^ +| `save_config_pending` | bool | A value of `true` indicates that a `save_config` | +| | | action is pending a restart before writing the |^ +| | | updated options to the config file. |^ +| `save_config_pending_items` | object | An object containing the items pending for write | +| | | when `save_config_pending` is `true.` |^ +| `warnings` | [string] | An array of strings describing issues encountered | +| | | when the configuration file was parsed. |^ +{ # configfile-object-spec} Configfile Object + +/// warning +The `configfile` object has the potential to be very large. Client software +running on devices with limited memory (such as embedded devices) may have issues +querying this object. +/// ## extruder -*Enabled when `[extruder]` is included in printer.cfg* -!!! note - If multiple extruders are configured, extruder 0 is available as - `extruder`, extruder 1 as `extruder1` and so on. -```json + +*Enabled when `[extruder]` is included in `printer.cfg`* + +/// note +If multiple extruders are configured, extruder 0 is available as +`extruder`, extruder 1 as `extruder1` and so on. +/// + +```{.json title="Printer Object Example"} { - "temperature": 0.0, - "target": 0.0, - "power": 0.0, - "pressure_advance": 0.0, - "smooth_time": 0.0 + "temperature": 0, + "target": 0, + "power": 0, + "can_extrude": true, + "pressure_advance": 0, + "smooth_time": 0.04, + "motion_queue": null } ``` -The `extruder` object reports state of an extruder: -- `temperature`: The extruder's current temperature (in C). -- `target`: The extruder's target temperature (in C). -- `power`: The current pwm value applied to the heater. This value is - expressed as a percentage from 0.0 to 1.0. -- `pressure_advance`: The extruder's current pressure advance value. -- `smooth_time`: The currently set time range to use when calculating the - average extruder velocity for pressure advance. +| Field | Type | Description | +| ------------------ | :------------: | ------------------------------------------------- | +| `temperature` | float | The extruder's current temperature in C. | +| `target` | float | The extruder's requested target temperature in C. | +| `power` | float | The current pwm value applied to the extruder's | +| | | heater. This value should be in a range from 0.0 |^ +| | | to 1.0. |^ +| `can_extrude` | bool | A value of `true` indicates that the current | +| | | temperature is above the minimum extrusion temp. |^ +| `pressure_advance` | float | The extruder's current pressure advance value. | +| `smooth_time` | float | The currently set time range to use when | +| | | calculating the average extruder velocity for |^ +| | | pressure advance. |^ +| `motion_queue` | string \| null | The name of the extruder the stepper is | +| | | synchronized to. Will be null if the stepper is |^ +| | | not synced with another extruder. |^ +{ #extruder-object-spec } Extruder Object ## heater_bed -*Enabled when `[heater_bed]` is included in printer.cfg* -```json + +*Enabled when `[heater_bed]` is included in `printer.cfg`* + +```{.json title="Printer Object Example"} { "temperature": 0.0, "target": 0.0, "power": 0.0, } ``` -The `heater_bed` object reports state of the heated bed: -- `temperature`: The bed's current temperature -- `target`: The bed's target temperature -- `power`: The current pwm value applied to the heater. This value is - expressed as a percentage from 0.0 to 1.0. +| Field | Type | Description | +| ------------- | :---: | ------------------------------------------------ | +| `temperature` | float | The current temperature of the bed. | +| `target` | float | The target temperature of the bed. | +| `power` | float | The current pwm value applied to the heater. The | +| | | value should be in the range from 0.0 to 1.0. |^ +{ #heater-bed-object-spec } Heater Bed Object ## fan -*Enabled when `[fan]` is included in printer.cfg* -```json + +*Enabled when `[fan]` is included in `printer.cfg`* + +```{.json title="Printer Object Example"} { "speed": 0.0, "rpm": 4000 } ``` -The `fan` object returns state of the part cooling fan: -- `speed`: The current fan speed. This is reported as a - percentage of maximum speed in the range of 0.0 - 1.0. -- `rpm`: The fan's revolutions per minute if the tachometer - pin has been configured. Will report `null` if no tach - has been configured. +| Field | Type | Description | +| ------- | :---------: | -------------------------------------------------------- | +| `speed` | float | The current fan speed. This is reported as a percentage | +| | | with a range from 0.0 to 1.0 |^ +| `rpm` | int \| null | The fan's revolutions per minute if the tachometer pin | +| | | has been configured. Will report `null` when the tach |^ +| | | pin is not configured. |^ +{ #fan-object-spec } Fan Object ## idle_timeout -```json + +```{.json title="Printer Object Example"} { "state": "Idle", "printing_time": 0.0 } ``` -The `idle_timeout` object reports the idle state of the printer: +| Field | Type | Description | +| --------------- | :----: | --------------------------------------------------------- | +| `state` | string | The current [state](#idle-timeout-state-desc) as reported | +| | | by the idle timeout module. |^ +| `printing_time` | float | The amount of time, in seconds, that idle timeout has | +| | | reported a `Printing` state. Will be reset to 0 when |^ +| | | the state transitions from `Printing` to `Ready`. |^ +{ #idle-timeout-object-spec } Idle Timeout Object -- `state`: Can be `Idle`, `Ready`, or `Printing`. The printer will - transition to the `Printing` state whenever a gcode is issued that - commands the tool, this includes manual commands. Thus this should - not be used to determine if a gcode file print is in progress. It can - however be used to determine if the printer is busy. -- `printing_time`: The amount of time the printer has been in the - `Printing` state. This is reset to 0 whenever the printer transitions - from `Printing` to `Ready`. +| State | Description | +| ---------- | ------------------------------------------------------------- | +| `Printing` | The printer is busy. This indicates that some action has | +| | been scheduled, such as a move command. |^ +| `Ready` | The printer is no longer active and waiting for more activity | +| | or for the idle timeout to expire. |^ +| `Idle` | The printer has been inactive for a period of time longer | +| | than the configured idle timeout. |^ +{ #idle-timeout-state-desc} Idle Timeout State + +/// Tip +The `idle_timeout` state field should not be used to determine if Klipper +is "printing" a file, as the state will report `Printing` when executing +manual commands. +/// ## virtual_sdcard -*Enabled when `[virtual_sdcard]` is included in printer.cfg* -```json + +*Enabled when `[virtual_sdcard]` is included in `printer.cfg`* + +```{.json title="Printer Object Example"} { - "progress": 0.0, + "file_path": null, + "progress": 0, "is_active": false, - "file_position": 0 + "file_position": 0, + "file_size": 0 } ``` -The `virtual_sdcard` object reports the state of the virtual sdcard: -- `progress`: The print progress reported as a percentage of the file - read, in the range of 0.0 - 1.0. -- `is_active`: Returns true if the virtual sdcard is currently processing - a file. Note that this will return false if a virtual sdcard print is - paused. -- `file_position`: The current file position in bytes. This will always - be an integer value +| Field | Type | Description | +| --------------- | :------------: | ---------------------------------------------- | +| `file_path` | string \| null | The full absolute path of the currently loaded | +| | | file. Will be `null` if no file is loaded. |^ +| `progress` | float | The current file progress reported as a | +| | | percentage. Valid range is 0.0 to 1.0. |^ +| `is_active` | bool | When `true` the virtual sdcard is actively | +| | | processing a file. |^ +| `file_position` | int | The current file position in bytes. | +| `file_size` | int | The file size of the currently loaded | +| | | file in bytes. |^ +{ #virtual-sdcard-object-spec } Virtual SDCard Object -!!! Note - `progress` and `file_position` will persist after a print has - paused, completed, or errored. They are cleared when the user issues - a SDCARD_RESET_FILE gcode or when a new print has started. + +/// Note +The value for most fields will persist after a print has +paused, completed, or errored. They are cleared when the user issues +a `SDCARD_RESET_FILE` gcode or when a new print has started. +/// ## print_stats -*Enabled when `[virtual_sdcard]` is included in printer.cfg* -```json + +*Enabled when `[virtual_sdcard]` is included in `printer.cfg`* + +```{.json title="Printer Object Example"} { "filename": "", "total_duration": 0.0, @@ -243,158 +853,1471 @@ The `virtual_sdcard` object reports the state of the virtual sdcard: } } ``` -The `print_stats` object reports `virtual_sdcard` print state: -- `filename`: The name of the current file loaded. This will be a null - string if no file is loaded. Note that name is a path relative to the - gcode folder, thus if the file is located in a subdirectory it would - be reported as "my_sub_dir/myprint.gcode". -- `total_duration`: The total time (in seconds) elapsed since a print - has started. This includes time spent paused. -- `print_duration`: The total time spent printing (in seconds). This is - equivalent to `total_duration` - time paused. -- `filament_used`: The amount of filament used during the current print - (in mm). Any extrusion during a pause is excluded. -- `state`: Current print state. Can be one of the following values: - - `"standby"` - - `"printing"` - - `"paused"` - - `"complete"` - - `"cancelled"` - - `"error"` - Note that if an error is detected the print will abort -- `message`: If an error is detected, this field contains the error - message generated. Otherwise it will be a null string. -- `info`: This is a dict containing information about the print provided by the - slicer. Currently this is limited to the `total_layer` and `current_layer` values. - Note that these values are set by the +| Field | Type | Description | +| ---------------- | :----: | -------------------------------------------------- | +| `filename` | string | The path to the currently loaded file relative to | +| | | the configured gcode folder. Will be an empty |^ +| | | string if no file is loaded. |^ +| `total_duration` | float | Total job duration in seconds. | +| `print_duration` | float | Time spent printing the current job in seconds. | +| | | Does not include time paused. |^ +| `filament_used` | float | Amount of filament used for the current job in mm. | +| `state` | string | The current job [state](#print-stats-state-desc). | +| `message` | string | A status message set by Klipper. Will be an empty | +| | | string if no message is set. |^ +| `info` | object | A `Print Stats Supplemental Info` object. | +| | | #print-stats-supplemental-info-spec |+ +{ #print-stats-object-spec } Print Stats Object + +| Field | Type | Description | +| --------------- | :---------: | -------------------------------------------- | +| `total_layer` | int \| null | The total layer count of the current | +| | | job. Will be null if the total layer |^ +| | | count is not set. |^ +| `current_layer` | int \| null | The index of the layer the job is currently | +| | | printing. Will be null of the current layer |^ +| | | is not set. |^ +{#print-stats-supplemental-info-spec} Print Stats Supplemental Info + +| State | Description | +| ----------- | ---------------------------------------------- | +| `standby` | The printer is standing by for a job to begin. | +| `printing` | A job is currently printing. | +| `paused` | The current print job is paused. | +| `complete` | The last print job successfully finished. | +| `error` | The last print job exited with an error. | +| `cancelled` | THe last print job was cancelled by the user. | +{ #print-stats-state-desc } Print Stats State + + /// tip + The `total_layer` and `current_layer` values in the `info` field are set by the [SET_PRINT_STATS_INFO](https://www.klipper3d.org/G-Codes.html#set_print_stats_info) gcode command. It is necessary to configure the slicer to include this command - in the print. `SET_PRINT_STATS_INFO TOTAL_LAYER=total_layer_count` should - be called in the slicer's "start gcode" to initalize the total layer count. - `SET_PRINT_STATS_INFO CURRENT_LAYER=current_layer` should be called in the + in the print. `SET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count}` should + be called in the slicer's "start gcode" to initialize the total layer count. + `SET_PRINT_STATS_INFO CURRENT_LAYER={current_layer}` should be called in the slicer's "on layer change" gcode. The user must substitute the `total_layer_count` and `current_layer` with the appropriate "placeholder syntax" for the slicer. + /// -!!! Note - After a print has started all of the values above will persist until - the user issues a SDCARD_RESET_FILE gcode or when a new print has started. +/// note +After a print has started all of the values reported by `print_stats will +persist until the user issues a `SDCARD_RESET_FILE` gcode command or a new print +has started. +/// ## display_status -*Enabled when `[display]` or `[display_status]` is included in printer.cfg* -```json + +*Enabled when `[display]` or `[display_status]` is included in `printer.cfg`* + +```{.json title="Printer Object Example"} { "message": "", "progress": 0.0 } ``` -The `display_status` object contains state typically used to update displays: -- `message`: The message set by a M117 gcode. If no message is set this will - be a null string. -- `progress`: The percentage of print progress, as reported by M73. This - will be in the range of 0.0 - 1.0. If no M73 has been issued this value - will fallback to the eqivalent of `virtual_sdcard.progress`. Note that - progress updated via M73 has a timeout. If no M73 is received after 5 - seconds, `progress` will be set to the fallback value. +| Field | Type | Description | +| ---------- | :----: | --------------------------------------------------- | +| `message` | string | The message set by a M117 gcode. If no message | +| | | is set this will be an empty string. |^ +| `progress` | float | Current print progress as reported by the M73 | +| | | gcode command. If M73 has not been issued this |^ +| | | value will fall back on `virtual_sdcard.progress`. |^ +| | | This value is expressed as a percentage of progress |^ +| | | complete with a range from 0.0 to 1.0. |^ +{ #display-status-object-spec } Display Status Object -## temperature_sensor sensor_name -*Enabled when `[temperature_sensor sensor_name]` is included in printer.cfg. -It is possible for multiple temperature sensors to be configured.* -```json +/// note +Progress updates via M73 commands must be received at an interval of +no more than 5 seconds. M73 progress tracking has a 5 second timeout +after which the `progress` field will fall back to the value reported +by `virtual_sdcard.progress`. +/// + +## temperature_sensor + +*Enabled when `[temperature_sensor sensor_name]` is included in `printer.cfg`.* +*It is possible for multiple temperature sensors to be configured.* + +```{.json title="Printer Object Example"} { "temperature": 0.0, "measured_min_temp": 0.0, "measured_max_temp": 0.0 } ``` -A `temperature_sensor` reports the following state: -- `temperature`: Sensor's current reported temperature -- `measured_min_temp`: The mimimum temperature read from the sensor -- `measured_max_temp`: The maximum temperature read from the sensor +| Field | Type | Description | +| ------------------- | :---: | -------------------------------------------------- | +| `temperature` | float | The sensor's current temperature reading in C. | +| `measured_min_temp` | float | The minimum sensor reading since the host started. | +| `measured_max_temp` | float | The maximum sensor reading since the host started. | +{ #temp-sensor-object-spec } Temperature Sensor Object -## temperature_fan fan_name -*Enabled when `[temperature_fan fan_name]` is included in printer.cfg. It is -possible for multiple temperature fans to be configured.* -```json +## temperature_fan + +*Enabled when `[temperature_fan fan_name]` is included in `printer.cfg`.* +*It is possible for multiple temperature fans to be configured.* + +```{.json title="Printer Object Example"} { "speed": 0.0, + "rpm": null, "temperature": 0.0, "target": 0.0 } ``` -A `temperature_fan` reports the following state: -- `speed`: Current fan speed as a percentage of maximum speed, reported - in the range of 0.0 - 1.0 -- `temperature`: Currently reported temperature of the sensor associated - with the fan -- `target`: The current target temperature for the `temperature_fan`. +| Field | Type | Description | +| ------------- | :---------: | ---------------------------------------------------------- | +| `speed` | float | The current fan speed. This is reported as a percentage | +| | | with a range from 0.0 to 1.0 |^ +| `rpm` | int \| null | The fan's revolutions per minute if the tachometer pin | +| | | has been configured. Will report `null` when the tach |^ +| | | pin is not configured. |^ +| `temperature` | float | Current temperature of the sensor associated with the fan. | +| `target` | float | Target temperature for the fan to enable. | +{ #temp-fan-object-spec } Temperature Fan Object -## filament_switch_sensor sensor_name -*Enabled when `[filament_switch_sensor sensor_name]` is included in -printer.cfg. It is possible for multiple filament sensors to be configured.* -```json +## filament_switch_sensor + +*Enabled when `[filament_switch_sensor sensor_name]` is included in* +*`printer.cfg`. It is possible for multiple filament switch sensors* +*to be configured.* + +```{.json title="Printer Object Example"} { "filament_detected": false, "enabled": true } ``` -A `filament_switch_sensor` reports the following state: -- `filament_detected`: Set to true if the switch detects filament, otherwise - false -- `enabled`: Set to true if the sensor is currently enabled, otherwise false +| Field | Type | Description | +| ------------------- | :--: | ------------------------------------------------------- | +| `filament_detected` | bool | Reports `true` when filament is detected by the sensor. | +| `enabled` | bool | Reports `true` when the filament sensor is enabled. | +{ #filament-switch-sensor-object-spec } -## output_pin pin_name -*Enabled when `[output_pin pin_name]` is included in printer.cfg. -It is possible for multiple output pins to be configured.* -```json +## output_pin + +*Enabled when `[output_pin pin_name]` is included in `printer.cfg`.* +*It is possible for multiple output pins to be configured.* + +```{.json title="Printer Object Example"} { "value": 0.0 } ``` -An `output_pin` reports the following state: -- `value`: The currently set value of the pin, in the range of 0.0 - 1.0. - A digital pin will always be 0 or 1, whereas a pwm pin may report a value - across the entire range. +| Field | Type | Description | +| ------- | :----------: | ------------------------------------------- | +| `value` | float \| int | The current value of the pin. Digital pins | +| | | will be 0 (off) or 1 (on). PWM pins will |^ +| | | report a range from 0.0 to 1.0. |^ +{ #output-pin-object-spec } Output Pin Object ## bed_mesh -*Enabled when `[bed_mesh]` is included in printer.cfg.* -```json + +*Enabled when `[bed_mesh]` is included in `printer.cfg`.* + +/// collapse-code +```{.json title="Printer Object Example"} { - "profile_name": "", - "mesh_min": [0.0, 0.0], - "mesh_max": [0.0, 0.0], - "probed_matrix": [[]], - "mesh_matrix": [[]] + "profile_name": "default", + "mesh_min": [ + 24, + 6 + ], + "mesh_max": [ + 237.96, + 210 + ], + "probed_matrix": [ + [ + -0.128919, + -0.136419, + -0.111419, + -0.131419, + -0.166419, + -0.211419, + -0.268919 + ], + [ + -0.006419, + 0.021081, + 0.003581, + -0.056419, + -0.068919, + -0.086419, + -0.173919 + ], + [ + 0.001081, + 0.048581, + 0.023581, + -0.014544, + -0.016419, + -0.063919, + -0.108919 + ], + [ + 0.013581, + 0.041081, + 0.033581, + 0.006081, + -0.008919, + -0.036419, + -0.076419 + ], + [ + 0.031081, + 0.076081, + 0.071081, + 0.022331, + 0.021081, + 0.013581, + -0.036419 + ], + [ + 0.006081, + 0.053581, + 0.068581, + 0.016081, + 0.041081, + 0.023581, + -0.001419 + ], + [ + -0.041419, + -0.021419, + 0.061081, + 0.068581, + 0.021081, + 0.036081, + 0.033581 + ] + ], + "mesh_matrix": [ + [ + -0.128919, + -0.132067, + -0.136049, + -0.136419, + -0.128826, + -0.117623, + -0.111419, + -0.114197, + -0.121975, + -0.131419, + -0.141604, + -0.153456, + -0.166419, + -0.180215, + -0.195123, + -0.211419, + -0.231789, + -0.253549, + -0.268919 + ], + [ + -0.0929, + -0.092739, + -0.092341, + -0.090771, + -0.085645, + -0.079346, + -0.078086, + -0.08576, + -0.098472, + -0.110748, + -0.120355, + -0.129525, + -0.139475, + -0.149675, + -0.160655, + -0.175215, + -0.198, + -0.224365, + -0.243178 + ], + [ + -0.043271, + -0.038473, + -0.031937, + -0.027623, + -0.02591, + -0.026419, + -0.031975, + -0.046304, + -0.06568, + -0.081743, + -0.090474, + -0.095893, + -0.101697, + -0.107088, + -0.112864, + -0.125123, + -0.151083, + -0.183525, + -0.206882 + ], + [ + -0.006419, + 0.002377, + 0.014229, + 0.021081, + 0.020155, + 0.014229, + 0.003581, + -0.01503, + -0.038363, + -0.056419, + -0.063919, + -0.066141, + -0.068919, + -0.071789, + -0.075215, + -0.086419, + -0.113641, + -0.148641, + -0.173919 + ], + [ + 0.004414, + 0.016177, + 0.032024, + 0.041174, + 0.039035, + 0.030198, + 0.017655, + -0.000672, + -0.022706, + -0.03922, + -0.044637, + -0.044534, + -0.046419, + -0.051524, + -0.058616, + -0.071512, + -0.096278, + -0.126848, + -0.148826 + ], + [ + 0.00247, + 0.016561, + 0.03558, + 0.046822, + 0.044244, + 0.033888, + 0.021174, + 0.005504, + -0.012524, + -0.025701, + -0.02868, + -0.026807, + -0.028919, + -0.038547, + -0.052159, + -0.067993, + -0.088391, + -0.111011, + -0.127067 + ], + [ + 0.001081, + 0.016081, + 0.036359, + 0.048581, + 0.046104, + 0.035572, + 0.023581, + 0.010502, + -0.004035, + -0.014544, + -0.016164, + -0.013757, + -0.016419, + -0.028965, + -0.046581, + -0.063919, + -0.080771, + -0.097345, + -0.108919 + ], + [ + 0.004692, + 0.018015, + 0.03605, + 0.0471, + 0.045384, + 0.036683, + 0.026637, + 0.015674, + 0.003366, + -0.005933, + -0.008321, + -0.007701, + -0.011419, + -0.02354, + -0.04, + -0.055956, + -0.071107, + -0.085754, + -0.095956 + ], + [ + 0.008859, + 0.019085, + 0.032964, + 0.041729, + 0.041315, + 0.035788, + 0.028581, + 0.019667, + 0.009074, + 0.000317, + -0.00392, + -0.00632, + -0.011419, + -0.02151, + -0.034299, + -0.047438, + -0.06171, + -0.076333, + -0.086604 + ], + [ + 0.013581, + 0.022007, + 0.033488, + 0.041081, + 0.041914, + 0.038859, + 0.033581, + 0.025433, + 0.015062, + 0.006081, + 0.000618, + -0.003456, + -0.008919, + -0.016697, + -0.025863, + -0.036419, + -0.050308, + -0.065586, + -0.076419 + ], + [ + 0.020618, + 0.030066, + 0.042971, + 0.051729, + 0.053413, + 0.050951, + 0.045525, + 0.035803, + 0.023118, + 0.012655, + 0.007137, + 0.003842, + -0.000215, + -0.0054, + -0.011347, + -0.019938, + -0.034293, + -0.051292, + -0.063456 + ], + [ + 0.028211, + 0.039977, + 0.056064, + 0.0671, + 0.06958, + 0.067009, + 0.060525, + 0.047946, + 0.031454, + 0.018743, + 0.013793, + 0.012623, + 0.010988, + 0.008679, + 0.005904, + -0.000956, + -0.016272, + -0.035671, + -0.04966 + ], + [ + 0.031081, + 0.0446, + 0.063118, + 0.076081, + 0.079738, + 0.07784, + 0.071081, + 0.056312, + 0.036683, + 0.022331, + 0.018627, + 0.020201, + 0.021081, + 0.020618, + 0.019461, + 0.013581, + -0.001789, + -0.021882, + -0.036419 + ], + [ + 0.026729, + 0.041019, + 0.060649, + 0.074785, + 0.07992, + 0.079562, + 0.073396, + 0.057015, + 0.034828, + 0.019738, + 0.019173, + 0.025705, + 0.02997, + 0.029346, + 0.026453, + 0.019785, + 0.005944, + -0.011673, + -0.024382 + ], + [ + 0.017655, + 0.032151, + 0.052141, + 0.0671, + 0.074094, + 0.076057, + 0.071266, + 0.053943, + 0.029865, + 0.014646, + 0.017897, + 0.030006, + 0.037748, + 0.035934, + 0.029752, + 0.021544, + 0.010138, + -0.003295, + -0.0129 + ], + [ + 0.006081, + 0.0196, + 0.038396, + 0.053581, + 0.063488, + 0.069785, + 0.068581, + 0.053211, + 0.03034, + 0.016081, + 0.020248, + 0.033025, + 0.041081, + 0.038674, + 0.031544, + 0.023581, + 0.014877, + 0.00534, + -0.001419 + ], + [ + -0.009845, + 0.000519, + 0.015275, + 0.029692, + 0.044726, + 0.059421, + 0.066174, + 0.058303, + 0.04249, + 0.031174, + 0.030519, + 0.034359, + 0.036637, + 0.0351, + 0.032, + 0.028025, + 0.022504, + 0.016108, + 0.011544 + ], + [ + -0.028271, + -0.022245, + -0.013082, + -0.00003, + 0.021186, + 0.04629, + 0.063211, + 0.065736, + 0.060078, + 0.052794, + 0.044416, + 0.034413, + 0.027748, + 0.027679, + 0.030949, + 0.032748, + 0.030676, + 0.027134, + 0.024507 + ], + [ + -0.041419, + -0.038549, + -0.033456, + -0.021419, + 0.004229, + 0.036822, + 0.061081, + 0.071174, + 0.072933, + 0.068581, + 0.054507, + 0.034322, + 0.021081, + 0.0221, + 0.030062, + 0.036081, + 0.036451, + 0.034877, + 0.033581 + ] + ], + "profiles": { + "default": { + "points": [ + [ + -0.128919, + -0.136419, + -0.111419, + -0.131419, + -0.166419, + -0.211419, + -0.268919 + ], + [ + -0.006419, + 0.021081, + 0.003581, + -0.056419, + -0.068919, + -0.086419, + -0.173919 + ], + [ + 0.001081, + 0.048581, + 0.023581, + -0.014544, + -0.016419, + -0.063919, + -0.108919 + ], + [ + 0.013581, + 0.041081, + 0.033581, + 0.006081, + -0.008919, + -0.036419, + -0.076419 + ], + [ + 0.031081, + 0.076081, + 0.071081, + 0.022331, + 0.021081, + 0.013581, + -0.036419 + ], + [ + 0.006081, + 0.053581, + 0.068581, + 0.016081, + 0.041081, + 0.023581, + -0.001419 + ], + [ + -0.041419, + -0.021419, + 0.061081, + 0.068581, + 0.021081, + 0.036081, + 0.033581 + ] + ], + "mesh_params": { + "min_x": 24, + "max_x": 237.96, + "min_y": 6, + "max_y": 210, + "x_count": 7, + "y_count": 7, + "mesh_x_pps": 2, + "mesh_y_pps": 2, + "algo": "bicubic", + "tension": 0.5 + } + } + } } ``` -The `bed_mesh` printer object reports the following state: +/// -- `profile_name`: The name of the currently loaded profile. If no profile is - loaded then this will report a null string. If the user is not using - bed_mesh profile management then this will report `default` after mesh - calibration completes. -- `mesh_min`: [X, Y] - The minimum x and y coordinates of the mesh. -- `mesh_max`: [X, Y] - The maximum x and y coordinates of the mesh. -- `probed_matrix`: A 2 dimensional array representing the matrix of probed - values. If the matrix has not been probed the the result is `[[]]`. -- `mesh_matrix`: A 2 dimension array representing the interpolated mesh. If - no matrix has been generated the result is `[[]]`. +| Field | Type | Description | +| --------------- | :-------: | ----------------------------------------------------- | +| `profile_name` | string | The name of the currently loaded profile. Will be an | +| | | empty string if no profile is loaded. |^ +| `mesh_min` | [float] | A coordinate (X,Y) indicating the minimum location | +| | | of the mesh. |^ +| `mesh_max` | [float] | A coordinate (X,Y) indicating the maximum location | +| | | of the mesh. |^ +| `probed_matrix` | [[float]] | A 2D array of Z values sampled by the probe. | +| `mesh_matrix` | [[float]] | A 2D array of Z values representing the interpolated | +| | | mesh. |^ +| `profiles` | object | An object, where the keys are profile names and the | +| | | values are `Bed Mesh Profile` objects. |^ +| | | #bed-mesh-profile-spec |+ +{ #bed-mesh-object-spec } Bed Mesh Object -!!! tip - See [web_api.md](web_api.md##bed-mesh-coordinates) for an example - of how to use this information to generate (X,Y,Z) coordinates. +| Field | Type | Description | +| ------------- | :-------: | -------------------------------------------- | +| `mesh_params` | object | A `Mesh Parameters` object. | +| | | #profile-mesh-params-spec |+ +| `points` | [[float]] | A 2D array of Z values sampled by the probe. | +{ #bed-mesh-profile-spec } Bed Mesh Profile -## gcode_macro macro_name -*Enabled when `[gcode_macro macro_name]` is included in printer.cfg. -It is possible for multiple gcode macros to be configured.* +| Field | Type | Description | +| ------------ | :----: | --------------------------------------------------- | +| `min_x` | float | The minimum X coordinate probed. | +| `max_x` | float | The maximum X coordinate probed. | +| `min_y` | float | The minimum Y coordinate probed. | +| `max_y` | float | The maximum Y coordinate probed. | +| `x_count` | int | The number of probe samples taken along the X axis. | +| `y_count` | int | The number of probe samples taken along the Y axis. | +| `mesh_x_pps` | int | The number of values to interpolate between probe | +| | | samples on the X axis. |^ +| `mesh_y_pps` | int | The number of values to interpolate between probe | +| | | samples on the Y axis. |^ +| `algo` | string | The interpolation algorithm. Can be `lagrange` or | +| | | `bicubic`. |^ +| `tension` | float | The `tension` parameter used for the `bicubic` | +| | | interpolation algorithm. |^ +{ #profile-mesh-params-spec } Mesh Parameters -Gcode macros will report the state of configured `variables`. -While user defined macros likely won't report state that is useful -for a client, it is possible for client developers to recommend or -request a specific gcode_macro configuration, then have the client -take action based on the variables reported by the macro. +/// tip +See [the tutorials](./external_api/introduction.md#bed-mesh-coordinates) +for an example of how to use this information to generate (X,Y,Z) +coordinates. +/// + +## exclude_object + +*Available when `[exclude_object]` is configured in `printer.cfg`.* + +/// collapse-code +```{.json title="Printer Object Example"} +{ + "objects": [ + { + "name": "HELPER_DISK_STL_(INSTANCE_1)", + "center": [ + 136.426, + 116.347 + ], + "polygon": [ + [ + 136.614, + 108.849 + ], + [ + 138.112, + 109.039 + ], + [ + 139.541, + 109.524 + ], + [ + 140.844, + 110.286 + ], + [ + 141.968, + 111.294 + ], + [ + 142.868, + 112.506 + ], + [ + 143.506, + 113.874 + ], + [ + 143.858, + 115.342 + ], + [ + 143.921, + 116.599 + ], + [ + 143.719, + 118.094 + ], + [ + 143.324, + 119.289 + ], + [ + 142.595, + 120.611 + ], + [ + 141.616, + 121.76 + ], + [ + 140.427, + 122.69 + ], + [ + 139.076, + 123.363 + ], + [ + 137.617, + 123.751 + ], + [ + 136.362, + 123.846 + ], + [ + 134.862, + 123.682 + ], + [ + 133.425, + 123.22 + ], + [ + 132.109, + 122.48 + ], + [ + 130.969, + 121.492 + ], + [ + 130.185, + 120.507 + ], + [ + 129.478, + 119.173 + ], + [ + 129.053, + 117.725 + ], + [ + 128.927, + 116.221 + ], + [ + 129.104, + 114.722 + ], + [ + 129.577, + 113.289 + ], + [ + 130.328, + 111.979 + ], + [ + 131.326, + 110.847 + ], + [ + 132.318, + 110.072 + ], + [ + 133.658, + 109.376 + ], + [ + 135.109, + 108.963 + ] + ] + }, + { + "name": "HELPER_DISK_STL_(INSTANCE_2)", + "center": [ + 115.426, + 116.347 + ], + "polygon": [ + [ + 115.615, + 108.849 + ], + [ + 117.113, + 109.039 + ], + [ + 118.542, + 109.524 + ], + [ + 119.639, + 110.141 + ], + [ + 120.796, + 111.111 + ], + [ + 121.736, + 112.292 + ], + [ + 122.42, + 113.637 + ], + [ + 122.821, + 115.093 + ], + [ + 122.922, + 116.599 + ], + [ + 122.774, + 117.848 + ], + [ + 122.325, + 119.289 + ], + [ + 121.596, + 120.611 + ], + [ + 120.617, + 121.76 + ], + [ + 119.428, + 122.69 + ], + [ + 118.077, + 123.363 + ], + [ + 116.618, + 123.751 + ], + [ + 115.363, + 123.846 + ], + [ + 113.863, + 123.682 + ], + [ + 112.426, + 123.22 + ], + [ + 111.11, + 122.48 + ], + [ + 109.969, + 121.492 + ], + [ + 109.186, + 120.507 + ], + [ + 108.479, + 119.173 + ], + [ + 108.054, + 117.725 + ], + [ + 107.927, + 116.221 + ], + [ + 108.104, + 114.722 + ], + [ + 108.578, + 113.289 + ], + [ + 109.329, + 111.979 + ], + [ + 110.327, + 110.847 + ], + [ + 111.318, + 110.072 + ], + [ + 112.658, + 109.376 + ], + [ + 114.11, + 108.963 + ] + ] + }, + { + "name": "HELPER_DISK_STL_(INSTANCE_3)", + "center": [ + 136.426, + 95.347 + ], + "polygon": [ + [ + 136.614, + 87.849 + ], + [ + 138.112, + 88.039 + ], + [ + 139.541, + 88.524 + ], + [ + 140.844, + 89.286 + ], + [ + 141.968, + 90.294 + ], + [ + 142.868, + 91.506 + ], + [ + 143.506, + 92.874 + ], + [ + 143.858, + 94.342 + ], + [ + 143.921, + 95.599 + ], + [ + 143.719, + 97.094 + ], + [ + 143.324, + 98.289 + ], + [ + 142.595, + 99.611 + ], + [ + 141.616, + 100.76 + ], + [ + 140.427, + 101.69 + ], + [ + 139.076, + 102.363 + ], + [ + 137.617, + 102.751 + ], + [ + 136.362, + 102.846 + ], + [ + 134.862, + 102.682 + ], + [ + 133.425, + 102.22 + ], + [ + 132.109, + 101.48 + ], + [ + 130.969, + 100.492 + ], + [ + 130.185, + 99.507 + ], + [ + 129.478, + 98.173 + ], + [ + 129.053, + 96.725 + ], + [ + 128.927, + 95.221 + ], + [ + 129.104, + 93.722 + ], + [ + 129.577, + 92.288 + ], + [ + 130.328, + 90.979 + ], + [ + 131.326, + 89.847 + ], + [ + 132.531, + 88.937 + ], + [ + 133.658, + 88.376 + ], + [ + 135.109, + 87.963 + ] + ] + }, + { + "name": "M3_HEX_NUT_STL_(INSTANCE_1)", + "center": [ + 120.324, + 100.421 + ], + "polygon": [ + [ + 120.517, + 97.357 + ], + [ + 123.074, + 98.833 + ], + [ + 123.074, + 102.008 + ], + [ + 120.324, + 103.596 + ], + [ + 117.574, + 102.008 + ], + [ + 117.574, + 98.833 + ], + [ + 120.324, + 97.245 + ] + ] + }, + { + "name": "M3_HEX_NUT_STL_(INSTANCE_2)", + "center": [ + 108.824, + 100.421 + ], + "polygon": [ + [ + 109.017, + 97.357 + ], + [ + 111.574, + 98.833 + ], + [ + 111.574, + 102.008 + ], + [ + 108.824, + 103.596 + ], + [ + 106.074, + 102.008 + ], + [ + 106.074, + 98.833 + ], + [ + 108.824, + 97.245 + ] + ] + }, + { + "name": "M3_HEX_NUT_STL_(INSTANCE_3)", + "center": [ + 115.732, + 89.793 + ], + "polygon": [ + [ + 115.925, + 86.729 + ], + [ + 118.482, + 88.205 + ], + [ + 118.482, + 91.38 + ], + [ + 115.732, + 92.968 + ], + [ + 112.982, + 91.38 + ], + [ + 112.982, + 88.205 + ], + [ + 115.732, + 86.617 + ] + ] + } + ], + "excluded_objects": [], + "current_object": "M3_HEX_NUT_STL_(INSTANCE_3)" +} + +``` +/// + +| Field | Type | Description | +| ------------------ | :------------: | --------------------------------------------- | +| `objects` | [object] | An array of `Object Definitions`. | +| | | #object-definition-spec |+ +| `excluded_objects` | [string] | An array of object names currently excluded. | +| `current_object` | string \| null | The name of the object currently being | +| | | printed. Will be `null` if no defined object |^ +| | | is printing. |^ +{ #exclude-object-spec } Exclude Object + +| Field | Type | Description | +| --------- | :-------: | --------------------------------------------------- | +| `name` | string | The name of the defined object. | +| `polygon` | [[float]] | A 2D array indicating the (X,Y) coordinates | +| | | that form the boundary of the object's location. |^ +| | | This field is only available if the `polygon` |^ +| | | is included when the objected is defined by the |^ +| | | `EXCLUDE_OBJECT_DEFINE` gcode command. |^ +| `center` | [float] | An (X,Y) coordinate indicating the center point | +| | | of the object. This field is only available if |^ +| | | the `center` is included when the object is defined |^ +| | | by the `EXCLUDE_OBJECT_DEFINE` gcode command. |^ +{ #object-definition-spec } Object Definition + +## gcode_macro + +*Available when `[gcode_macro macro_name]` is included in `printer.cfg`.* +*It is possible for multiple gcode macros to be configured.* + +```{.json title="Printer Object Example"} +{ + "var_name": "value" +} +``` + +| Field | Type | Description | +| ---------- | :--: | ---------------------------------------- | +| *var_name* | any | Zero or more `variables` present in the | +| | | gcode_macro's configuration. The type |^ +| | | is coerced from the value set. If no |^ +| | | variables are configured the object will |^ +| | | be empty. |^ +{ #gcode-macro-object-spec } GCode Macro Object + +## mcu + +*The primary `mcu` object should always be available. It is possible* +*for additional MCU objects to be present when one or more* +*[mcu mcu_name] sections are included in `printer.cfg`.* + +```{.json title="Printer Object Example"} +{ + "mcu_version": "v0.12.0-272-g13c75ea87", + "mcu_build_versions": "gcc: (GCC) 5.4.0 binutils: (GNU Binutils) 2.26.20160125", + "mcu_constants": { + "ADC_MAX": 1023, + "BUS_PINS_spi": "PB3,PB2,PB1", + "BUS_PINS_twi": "PD0,PD1", + "CLOCK_FREQ": 16000000, + "MCU": "atmega2560", + "PWM_MAX": 255, + "RECEIVE_WINDOW": 192, + "RESERVE_PINS_serial": "PE0,PE1", + "SERIAL_BAUD": 250000, + "STATS_SUMSQ_BASE": 256 + } +} +``` + +| Field | Type | Description | +| -------------------- | :----: | --------------------------------------------- | +| `mcu_version` | string | The version of Klipper at the time the MCU | +| | | firmware was built. |^ +| `mcu_build_versions` | string | Version information about the tools used | +| | | to build the MCU firmware. |^ +| `mcu_constants` | object | An object containing compile time constants | +| | | reported by the MCU. The constants available |^ +| | | may differ between MCUs as they depend on the |^ +| | | micro-controller's underlying architecture. |^ +{ #mcu-object-spec } MCU Object + +## stepper_enable + +*Available when one or more steppers are configured in `printer.cfg`.* + +```{.json title="Printer Object Example"} +{ + "steppers": { + "stepper_z": false, + "stepper_y": false, + "stepper_x": false, + "extruder": false + } +} +``` + +| Field | Type | Description | +| ---------- | :----: | -------------------------------------------- | +| `steppers` | object | An object containing the enabled state for | +| | | all registered steppers. The keys are |^ +| | | stepper names, the values are boolean's |^ +| | | reflecting the stepper driver enabled state. |^ +{ #stepper-enable-object-spec } Stepper Enable Object + +## TMC Drivers + +*Available when one or more `[tmcXXXX driver_name]` sections are included* +*in `printer.cfg`.* + +```{.json title="Printer Object Example"} +{ + "phase_offset_position": 0.01, + "temperature": null, + "drv_status": { + "sg_result": 211, + "cs_actual": 8, + "stallguard": 1, + "stst": 1 + }, + "mcu_phase_offset": 1, + "run_current": 0.28173785812901503, + "hold_current": 0.28173785812901503 +} +``` + +| Field | Type | Description | +| ----------------------- | :------------: | -------------------------------------------- | +| `phase_offset_position` | float \| null | The commanded position corresponding | +| | | with the driver's "zero phase". Will be |^ +| | | `null` if the phase offset is unknown. |^ +| `temperature` | float \| null | The temperature reported by the driver. | +| | | Will be `null` if temperature reporting |^ +| | | is not available. |^ +| `drv_status` | object \| null | An object containing results from the most | +| | | recent driver status query. Will be `null` |^ +| | | if the driver is disabled. See the driver's |^ +| | | datasheet for explanations on the fields |^ +| | | present in this object. |^ +| `mcu_phase_offset` | int \| null | The MCU stepper position corresponding | +| | | with the driver's "zero phase". Will be |^ +| | | `null` if the phase offset is unknown. |^ +| `run_current` | float | The presently set run current in amps. | +| `hold_current` | float | The presently set hold current in amps. | +{ #tmc-driver-object-spec } TMC Driver Object + +/// note +Klipper will omit fields from the `drv_status` object whose value +evaluates to false. For example, the `stallguard` field is only +present when its value is non-zero. +/// diff --git a/docs/src/css/extra-950ac449d4.css b/docs/src/css/extra-950ac449d4.css new file mode 100644 index 0000000..a718304 --- /dev/null +++ b/docs/src/css/extra-950ac449d4.css @@ -0,0 +1,2 @@ +:root>*{--md-code-link-bg-color:hsla(0, 0%, 96%, 1);--md-code-link-accent-bg-color:var(--md-code-link-bg-color);--md-default-bg-color--trans:rgb(100%, 100%, 100%, 0);--md-code-title-bg-color:var(--md-code-bg-color);--md-code-inline-bg-color:var(--md-code-bg-color);--md-code-special-bg-color:#e8e8e8;--md-code-alternate-bg-color:var(--md-code-bg-color);--md-code-hl-punctuation-color:var(--md-code-fg-color);--md-code-hl-namespace-color:var(--md-code-fg-color);--md-code-hl-entity-color:var(--md-code-hl-keyword-color);--md-code-hl-tag-color:var(--md-code-hl-keyword-color);--md-code-hl-builtin-color:var(--md-code-hl-constant-color);--md-code-hl-class-color:var(--md-code-hl-function-color);--md-typeset-a-color:#00bcd4;--md-progress-stripe:var(--md-default-bg-color--lighter);--md-progress-100:#00e676;--md-progress-80:#00e676;--md-progress-60:#fbc02d;--md-progress-40:#ff9100;--md-progress-20:#ff5252;--md-progress-0:#ff1744;--md-typeset-kbd-color:#ebebeb;--md-typeset-kbd-border-color:#b8b8b8;--md-typeset-kbd-accent-color:hsla(0, 100%, 100%, 1)}:root>[data-md-color-scheme=slate]{--md-code-link-bg-color:hsla(232, 15%, 15%, 1);--md-code-link-accent-bg-color:var(--md-code-link-bg-color);--md-code-special-bg-color:#2b2d3b;--md-default-bg-color--trans:hsla(232,15%,15%, 0);--md-default-bg-color--ultra-dark:#111217;--md-code-title-bg-color:var(--md-primary-fg-color--dark);--md-shadow-z2:0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),0 0 0.05rem hsla(0, 0%, 0%, 0.2);--md-typeset-kbd-color:var(--md-default-fg-color--lightest);--md-typeset-kbd-border-color:#1a1c24;--md-typeset-kbd-accent-color:var(--md-default-fg-color--lighter)}:root>[data-md-color-scheme=dracula]{--md-default-fg-color:rgba(248, 248, 242, 0.87);--md-default-fg-color--light:rgba(248, 248, 242, 0.54);--md-default-fg-color--lighter:rgba(248, 248, 242, 0.16);--md-default-fg-color--lightest:rgba(248, 248, 242, 0.07);--md-default-autocomplete-fg-color:rgba(248, 248, 242, 0.4);--md-shadow-z2:0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),0 0 0.05rem hsla(0, 0%, 0%, 0.2);--md-default-bg-color:var(--md-default-bg-color--darkest);--md-default-bg-color--light:rgba(50, 52, 67, 0.7);--md-default-bg-color--lighter:rgba(50, 52, 67, 0.3);--md-default-bg-color--lightest:rgba(50, 52, 67, 0.12);--md-default-bg-color--trans:rgba(50, 52, 67, 0);--md-default-bg-color--dark:#2b2e3b;--md-default-bg-color--darker:#252732;--md-default-bg-color--darkest:#1e2029;--md-default-bg-color--ultra-dark:#111217;--md-text-color:var(--md-default-fg-color);--md-typeset-color:var(--md-default-fg-color);--md-admonition-fg-color:var(--md-default-fg-color);--md-code-fg-color:hsl(60, 30%, 96%);--md-code-bg-color:hsl(231, 15%, 18%);--md-code-title-bg-color:var(--md-default-bg-color--ultra-dark);--md-code-inline-bg-color:#323443;--md-code-hl-operator-color:hsl(326, 100%, 74%);--md-code-hl-punctuation-color:hsl(60, 30%, 96%);--md-code-hl-string-color:hsl(65, 92%, 76%);--md-code-hl-special-color:hsl(265, 89%, 78%);--md-code-hl-number-color:hsl(265, 89%, 78%);--md-code-hl-keyword-color:hsl(326, 100%, 74%);--md-code-hl-name-color:hsl(60, 30%, 96%);--md-code-hl-constant-color:hsl(265, 89%, 78%);--md-code-hl-function-color:hsl(135, 94%, 65%);--md-code-hl-comment-color:hsl(225, 27%, 51%);--md-code-hl-variable-color:hsl(31, 100%, 71%);--md-code-hl-generic-color:hsl(225, 27%, 51%);--md-code-hl-color:hsl(231, 25%, 25%);--md-code-hl-entity-color:hsl(135, 94%, 65%);--md-code-hl-tag-color:hsl(326, 100%, 74%);--md-code-hl-namespace-color:hsl(60, 30%, 96%);--md-code-hl-builtin-color:hsl(191, 97%, 77%);--md-code-hl-class-color:hsl(191, 97%, 77%);--md-code-special-bg-color:#1c1e26;--md-code-alternate-bg-color:#3d3e49;--md-code-link-bg-color:#364653;--md-typeset-a-color:hsl(191, 97%, 77%);--md-typeset-mark-color:#6e7252;--md-typeset-del-color:#734568;--md-typeset-ins-color:#36724e;--md-progress-stripe:var(--md-default-bg-color--lightest);--md-progress-100:hsl(135, 94%, 65%);--md-progress-80:hsl(135, 92%, 79%);--md-progress-60:hsl(65, 92%, 76%);--md-progress-40:hsl(31, 100%, 71%);--md-progress-20:hsl(326, 100%, 74%);--md-progress-0:hsl(0, 100%, 67%);--md-typeset-kbd-color:var(--md-default-fg-color--lightest);--md-typeset-kbd-border-color:var(--md-default-bg-color--ultra-dark);--md-typeset-kbd-accent-color:var(--md-default-fg-color--lighter)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=red],[data-md-color-scheme=dracula][data-md-color-primary=red]{--md-primary-code-bg-color:#47303a;--md-primary-fg-color:hsla(0deg, 100%, 67%, 1);--md-primary-fg-color--transparent:hsla(0deg, 100%, 67%, 0.1);--md-primary-fg-color--light:hsla(0deg, 100%, 72%, 1);--md-primary-fg-color--dark:hsla(0deg, 100%, 62%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=pink],[data-md-color-scheme=dracula][data-md-color-primary=pink]{--md-primary-code-bg-color:#47354b;--md-primary-fg-color:hsla(326deg, 100%, 74%, 1);--md-primary-fg-color--transparent:hsla(326deg, 100%, 74%, 0.1);--md-primary-fg-color--light:hsla(326deg, 100%, 79%, 1);--md-primary-fg-color--dark:hsla(326deg, 100%, 69%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=purple],[data-md-color-scheme=dracula][data-md-color-primary=purple]{--md-primary-code-bg-color:#3e3952;--md-primary-fg-color:hsla(265deg, 89%, 78%, 1);--md-primary-fg-color--transparent:hsla(265deg, 89%, 78%, 0.1);--md-primary-fg-color--light:hsla(265deg, 89%, 83%, 1);--md-primary-fg-color--dark:hsla(265deg, 89%, 73%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-purple],[data-md-color-scheme=dracula][data-md-color-primary=deep-purple]{--md-primary-code-bg-color:#3e3952;--md-primary-fg-color:hsla(265deg, 89%, 78%, 1);--md-primary-fg-color--transparent:hsla(265deg, 89%, 78%, 0.1);--md-primary-fg-color--light:hsla(265deg, 89%, 83%, 1);--md-primary-fg-color--dark:hsla(265deg, 89%, 73%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=blue],[data-md-color-scheme=dracula][data-md-color-primary=blue]{--md-primary-code-bg-color:#303446;--md-primary-fg-color:hsla(225deg, 27%, 51%, 1);--md-primary-fg-color--transparent:hsla(225deg, 27%, 51%, 0.1);--md-primary-fg-color--light:hsla(225deg, 27%, 56%, 1);--md-primary-fg-color--dark:hsla(225deg, 27%, 46%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=indigo],[data-md-color-scheme=dracula][data-md-color-primary=indigo]{--md-primary-code-bg-color:#303446;--md-primary-fg-color:hsla(225deg, 27%, 51%, 1);--md-primary-fg-color--transparent:hsla(225deg, 27%, 51%, 0.1);--md-primary-fg-color--light:hsla(225deg, 27%, 56%, 1);--md-primary-fg-color--dark:hsla(225deg, 27%, 46%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-blue],[data-md-color-scheme=dracula][data-md-color-primary=light-blue]{--md-primary-code-bg-color:#303446;--md-primary-fg-color:hsla(225deg, 27%, 51%, 1);--md-primary-fg-color--transparent:hsla(225deg, 27%, 51%, 0.1);--md-primary-fg-color--light:hsla(225deg, 27%, 56%, 1);--md-primary-fg-color--dark:hsla(225deg, 27%, 46%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=cyan],[data-md-color-scheme=dracula][data-md-color-primary=cyan]{--md-primary-code-bg-color:#364653;--md-primary-fg-color:hsla(191deg, 97%, 77%, 1);--md-primary-fg-color--transparent:hsla(191deg, 97%, 77%, 0.1);--md-primary-fg-color--light:hsla(191deg, 97%, 82%, 1);--md-primary-fg-color--dark:hsla(191deg, 97%, 72%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=teal],[data-md-color-scheme=dracula][data-md-color-primary=teal]{--md-primary-code-bg-color:#364653;--md-primary-fg-color:hsla(191deg, 97%, 77%, 1);--md-primary-fg-color--transparent:hsla(191deg, 97%, 77%, 0.1);--md-primary-fg-color--light:hsla(191deg, 97%, 82%, 1);--md-primary-fg-color--dark:hsla(191deg, 97%, 72%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=green],[data-md-color-scheme=dracula][data-md-color-primary=green]{--md-primary-code-bg-color:#2d4840;--md-primary-fg-color:hsla(135deg, 94%, 65%, 1);--md-primary-fg-color--transparent:hsla(135deg, 94%, 65%, 0.1);--md-primary-fg-color--light:hsla(135deg, 94%, 70%, 1);--md-primary-fg-color--dark:hsla(135deg, 94%, 60%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-green],[data-md-color-scheme=dracula][data-md-color-primary=light-green]{--md-primary-code-bg-color:#2d4840;--md-primary-fg-color:hsla(135deg, 94%, 65%, 1);--md-primary-fg-color--transparent:hsla(135deg, 94%, 65%, 0.1);--md-primary-fg-color--light:hsla(135deg, 94%, 70%, 1);--md-primary-fg-color--dark:hsla(135deg, 94%, 60%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=lime],[data-md-color-scheme=dracula][data-md-color-primary=lime]{--md-primary-code-bg-color:#2d4840;--md-primary-fg-color:hsla(135deg, 94%, 65%, 1);--md-primary-fg-color--transparent:hsla(135deg, 94%, 65%, 0.1);--md-primary-fg-color--light:hsla(135deg, 94%, 70%, 1);--md-primary-fg-color--dark:hsla(135deg, 94%, 60%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=yellow],[data-md-color-scheme=dracula][data-md-color-primary=yellow]{--md-primary-code-bg-color:#454842;--md-primary-fg-color:hsla(65deg, 92%, 76%, 1);--md-primary-fg-color--transparent:hsla(65deg, 92%, 76%, 0.1);--md-primary-fg-color--light:hsla(65deg, 92%, 81%, 1);--md-primary-fg-color--dark:hsla(65deg, 92%, 71%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=amber],[data-md-color-scheme=dracula][data-md-color-primary=amber]{--md-primary-code-bg-color:#454842;--md-primary-fg-color:hsla(65deg, 92%, 76%, 1);--md-primary-fg-color--transparent:hsla(65deg, 92%, 76%, 0.1);--md-primary-fg-color--light:hsla(65deg, 92%, 81%, 1);--md-primary-fg-color--dark:hsla(65deg, 92%, 71%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=orange],[data-md-color-scheme=dracula][data-md-color-primary=orange]{--md-primary-code-bg-color:#473e3d;--md-primary-fg-color:hsla(31deg, 100%, 71%, 1);--md-primary-fg-color--transparent:hsla(31deg, 100%, 71%, 0.1);--md-primary-fg-color--light:hsla(31deg, 100%, 76%, 1);--md-primary-fg-color--dark:hsla(31deg, 100%, 66%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-orange],[data-md-color-scheme=dracula][data-md-color-primary=deep-orange]{--md-primary-code-bg-color:#473e3d;--md-primary-fg-color:hsla(31deg, 100%, 71%, 1);--md-primary-fg-color--transparent:hsla(31deg, 100%, 71%, 0.1);--md-primary-fg-color--light:hsla(31deg, 100%, 76%, 1);--md-primary-fg-color--dark:hsla(31deg, 100%, 66%, 1);--md-primary-bg-color:var(--md-default-bg-color);--md-primary-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=red],[data-md-color-scheme=dracula][data-md-color-accent=red]{--md-code-link-accent-bg-color:#472c36;--md-accent-fg-color:hsla(0deg, 100%, 62%, 1);--md-accent-fg-color--transparent:hsla(0deg, 100%, 62%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=pink],[data-md-color-scheme=dracula][data-md-color-accent=pink]{--md-code-link-accent-bg-color:#473149;--md-accent-fg-color:hsla(326deg, 100%, 69%, 1);--md-accent-fg-color--transparent:hsla(326deg, 100%, 69%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=purple],[data-md-color-scheme=dracula][data-md-color-accent=purple]{--md-code-link-accent-bg-color:#3c3652;--md-accent-fg-color:hsla(265deg, 89%, 73%, 1);--md-accent-fg-color--transparent:hsla(265deg, 89%, 73%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-purple],[data-md-color-scheme=dracula][data-md-color-accent=deep-purple]{--md-code-link-accent-bg-color:#3c3652;--md-accent-fg-color:hsla(265deg, 89%, 73%, 1);--md-accent-fg-color--transparent:hsla(265deg, 89%, 73%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=blue],[data-md-color-scheme=dracula][data-md-color-accent=blue]{--md-code-link-accent-bg-color:#2e3243;--md-accent-fg-color:hsla(225deg, 27%, 46%, 1);--md-accent-fg-color--transparent:hsla(225deg, 27%, 46%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=indigo],[data-md-color-scheme=dracula][data-md-color-accent=indigo]{--md-code-link-accent-bg-color:#2e3243;--md-accent-fg-color:hsla(225deg, 27%, 46%, 1);--md-accent-fg-color--transparent:hsla(225deg, 27%, 46%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-blue],[data-md-color-scheme=dracula][data-md-color-accent=light-blue]{--md-code-link-accent-bg-color:#2e3243;--md-accent-fg-color:hsla(225deg, 27%, 46%, 1);--md-accent-fg-color--transparent:hsla(225deg, 27%, 46%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=cyan],[data-md-color-scheme=dracula][data-md-color-accent=cyan]{--md-code-link-accent-bg-color:#324553;--md-accent-fg-color:hsla(191deg, 97%, 72%, 1);--md-accent-fg-color--transparent:hsla(191deg, 97%, 72%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=teal],[data-md-color-scheme=dracula][data-md-color-accent=teal]{--md-code-link-accent-bg-color:#324553;--md-accent-fg-color:hsla(191deg, 97%, 72%, 1);--md-accent-fg-color--transparent:hsla(191deg, 97%, 72%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=green],[data-md-color-scheme=dracula][data-md-color-accent=green]{--md-code-link-accent-bg-color:#2a483d;--md-accent-fg-color:hsla(135deg, 94%, 60%, 1);--md-accent-fg-color--transparent:hsla(135deg, 94%, 60%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-green],[data-md-color-scheme=dracula][data-md-color-accent=light-green]{--md-code-link-accent-bg-color:#2a483d;--md-accent-fg-color:hsla(135deg, 94%, 60%, 1);--md-accent-fg-color--transparent:hsla(135deg, 94%, 60%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=lime],[data-md-color-scheme=dracula][data-md-color-accent=lime]{--md-code-link-accent-bg-color:#2a483d;--md-accent-fg-color:hsla(135deg, 94%, 60%, 1);--md-accent-fg-color--transparent:hsla(135deg, 94%, 60%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=yellow],[data-md-color-scheme=dracula][data-md-color-accent=yellow]{--md-code-link-accent-bg-color:#45483e;--md-accent-fg-color:hsla(65deg, 92%, 71%, 1);--md-accent-fg-color--transparent:hsla(65deg, 92%, 71%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=amber],[data-md-color-scheme=dracula][data-md-color-accent=amber]{--md-code-link-accent-bg-color:#45483e;--md-accent-fg-color:hsla(65deg, 92%, 71%, 1);--md-accent-fg-color--transparent:hsla(65deg, 92%, 71%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=orange],[data-md-color-scheme=dracula][data-md-color-accent=orange]{--md-code-link-accent-bg-color:#473d39;--md-accent-fg-color:hsla(31deg, 100%, 66%, 1);--md-accent-fg-color--transparent:hsla(31deg, 100%, 66%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-orange],[data-md-color-scheme=dracula][data-md-color-accent=deep-orange]{--md-code-link-accent-bg-color:#473d39;--md-accent-fg-color:hsla(31deg, 100%, 66%, 1);--md-accent-fg-color--transparent:hsla(31deg, 100%, 66%, 0.1);--md-accent-bg-color:var(--md-default-bg-color);--md-accent-bg-color--light:var(--md-default-bg-color--light)}.highlight .kc{color:var(--md-code-hl-constant-color)}.highlight .nc,.highlight .ne{color:var(--md-code-hl-class-color)}.highlight .mb{color:var(--md-code-hl-number-color)}.highlight .bp,.highlight .nb{color:var(--md-code-hl-builtin-color)}.highlight .nn{color:var(--md-code-hl-namespace-color)}.highlight .na,.highlight .nd,.highlight .ni{color:var(--md-code-hl-entity-color)}.highlight .nl,.highlight .nt{color:var(--md-code-hl-tag-color)}.md-typeset :not(pre)>code{margin:0;padding:0 .2941176471em;color:var(--md-code-fg-color);background-color:var(--md-code-inline-bg-color);border-radius:.1rem;box-shadow:none}.md-typeset a>code{color:inherit!important;background-color:var(--md-code-link-bg-color)!important;transition:color 125ms;transition:background-color 125ms}.md-typeset a>code *{color:var(--md-typeset-a-color)!important}.md-typeset a>code:hover{background-color:var(--md-code-link-accent-bg-color)!important}.md-typeset a>code:hover *{color:var(--md-accent-fg-color)!important}.md-typeset pre>code{outline:0}.md-typeset td code{word-break:normal}.md-typeset .highlight{-moz-tab-size:8;-o-tab-size:8;tab-size:8}.md-typeset .highlight+.result{border-width:.1rem}.md-typeset .highlight [data-linenos].special::before{background-color:var(--md-code-special-bg-color)}.md-typeset .highlighttable .linenodiv .special{margin-right:-.5882352941em;margin-left:-1.1764705882em;padding-right:.5882352941em;padding-left:1.1764705882em;background-color:var(--md-code-special-bg-color)}.md-typeset .highlight span.filename{position:relative;display:block;margin-top:1em;padding:.5em 1.1764705882em .5em 2.9411764706em;font-weight:700;font-size:.68rem;background-color:var(--md-code-title-bg-color);border-top-left-radius:.1rem;border-top-right-radius:.1rem}.md-typeset .highlight span.filename+pre{margin-top:0}.md-typeset .highlight span.filename+pre code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .highlight span.filename::before{position:absolute;left:.8823529412em;width:1.4705882353em;height:1.4705882353em;background-color:var(--md-default-fg-color);-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;content:""}.md-typeset .apirequest span.filename::before{-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .apiresponse span.filename::before{-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .collapse-code{position:relative;margin-top:1em;margin-bottom:1em}.md-typeset .collapse-code pre{margin-top:0;margin-bottom:0}.md-typeset .collapse-code input{display:none}.md-typeset .collapse-code input~.code-footer{width:100%;margin:0;padding:.25em .5em .25em 0}.md-typeset .collapse-code input~.code-footer label{position:relative;margin:.05em;padding:.15em .8em;color:var(--md-primary-bg-color);font-size:90%;background-color:var(--md-primary-fg-color);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;border-radius:.1rem;cursor:pointer;content:""}.md-typeset .collapse-code input~.code-footer label:hover{background-color:var(--md-accent-fg-color)}.md-typeset .collapse-code input~.code-footer label::before{position:absolute;top:.15em;left:.15em;display:block;box-sizing:border-box;width:1.25em;height:1.25em;background-color:var(--md-primary-bg-color);background-size:1.25em;content:""}.md-typeset .collapse-code input~.code-footer label.expand{display:none}.md-typeset .collapse-code input~.code-footer label.expand::before{-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .collapse-code input~.code-footer label.collapse::before{-webkit-mask-image:url('data:image/svg+xml;charset=utf-8,');mask-image:url('data:image/svg+xml;charset=utf-8,')}.md-typeset .collapse-code input:checked~.code-footer label.expand{display:inline}.md-typeset .collapse-code input:checked~.code-footer label.collapse{display:none}.md-typeset .collapse-code input:checked+div.highlight code{max-height:9.375em;overflow:hidden}.md-typeset .collapse-code input:checked~.code-footer{position:absolute;bottom:0;left:0;padding:2em .5em .5em .8rem;background-image:linear-gradient(to bottom,transparent,var(--md-default-bg-color) 80% 100%)}[data-md-color-scheme=slate] .md-typeset table:not([class]){box-shadow:var(--md-shadow-z2)}[data-md-color-scheme=slate] .md-typeset table:not([class]) tr:hover{background-color:rgba(0,0,0,.08)}[data-md-color-scheme=slate] .md-typeset table:not([class]) th{color:var(--md-text-color);background-color:var(--md-primary-fg-color);border-bottom:.05rem solid var(--md-accent-fg-color)}[data-md-color-scheme=slate] .md-typeset table:not([class]) td{border-top:.05rem solid var(--md-default-fg-color--lighter)}.md-typeset table:not([class]) tr.table-container{visibility:visible}.md-typeset table:not([class]) tr.table-container:not([open]){visibility:collapse}.md-typeset table:not([class]) tr.table-container-ctrl{cursor:pointer}.md-typeset table:not([class]) tr.table-container-ctrl>td:first-child::after{background-color:var(--md-accent-fg-color);content:"";height:1rem;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;position:absolute;transform:rotate(0);transition:transform .25s;width:1rem}.md-typeset table:not([class]) tr.table-container-ctrl:hover{box-shadow:inset 0 0 .3rem var(--md-accent-fg-color)}.md-typeset table:not([class]) tr.table-container-ctrl[open]>td:first-child::after{transform:rotate(90deg)}.md-typeset table:not([class]) caption{padding-top:.3rem;padding-bottom:.3rem;font-weight:700}.md-typeset .table-container table:not([class]){box-shadow:none;margin:0}@media screen and (max-width:44.9375em){.md-typeset>diagram-div{margin-right:-.8rem;margin-left:-.8rem}.md-typeset>.collapse-code{margin-right:-.8rem;margin-left:-.8rem}.md-typeset>.collapse-code label.collapse{left:.8rem}} +/*# sourceMappingURL=extra-950ac449d4.css.map */ diff --git a/docs/src/css/extra-950ac449d4.css.map b/docs/src/css/extra-950ac449d4.css.map new file mode 100644 index 0000000..8720cc2 --- /dev/null +++ b/docs/src/css/extra-950ac449d4.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["palette/_colors.scss","extra.css","extensions/_highlight.scss","extensions/_tables.scss","utilities/_break.scss"],"names":[],"mappings":"AAEA,QAEE,wBAAA,oBACA,+BAAA,6BACA,6BAAA,yBACA,yBAAA,wBACA,0BAAA,wBAEA,2BAAA,QACA,6BAAA,wBAEA,+BAAA,wBACA,6BAAA,wBAEA,0BAAA,gCACA,uBAAA,gCACA,2BAAA,iCACA,yBAAA,iCAGA,qBAAA,QAGA,qBAAA,oCACA,kBAAA,QACA,iBAAA,QACA,iBAAA,QACA,iBAAA,QACA,iBAAA,QACA,gBAAA,QAGA,uBAAA,QACA,8BAAA,QACA,8BAAA,uBAEA,mCAEE,wBAAA,uBACA,+BAAA,6BACA,2BAAA,QACA,6BAAA,qBAEA,kCAAA,QAGA,yBAAA,iCAGA,eAAA,EAAA,OAAA,OAAA,oBAAA,CCjBmC,EAAE,EAAE,QAAQ,qBDqB/C,uBAAA,qCACA,8BAAA,QACA,8BAAA,oCAIF,qCAEE,sBAAA,0BACA,6BAAA,0BACA,+BAAA,0BACA,gCAAA,0BACA,mCAAA,yBACA,eAAA,EAAA,OAAA,OAAA,oBAAA,CCtBmC,EAAE,EAAE,QAAQ,qBDyB/C,sBAAA,oCACA,6BAAA,sBACA,+BAAA,sBACA,gCAAA,uBACA,6BAAA,oBAGA,4BAAA,QACA,8BAAA,QACA,+BAAA,QACA,kCAAA,QAGA,gBAAA,2BACA,mBAAA,2BAGA,yBAAA,2BAGA,mBAAA,kBACA,mBAAA,mBACA,yBAAA,uCACA,0BAAA,QACA,4BAAA,oBACA,+BAAA,kBACA,0BAAA,kBACA,2BAAA,mBACA,0BAAA,mBACA,2BAAA,oBACA,wBAAA,kBACA,4BAAA,mBACA,4BAAA,mBACA,2BAAA,mBACA,4BAAA,mBACA,2BAAA,mBACA,mBAAA,mBAGA,0BAAA,mBACA,uBAAA,oBACA,6BAAA,kBACA,2BAAA,mBACA,yBAAA,mBAEA,2BAAA,QACA,6BAAA,QACA,wBAAA,QAGA,qBAAA,mBACA,wBAAA,QACA,uBAAA,QACA,uBAAA,QAGA,qBAAA,qCACA,kBAAA,mBACA,iBAAA,mBACA,iBAAA,kBACA,iBAAA,mBACA,iBAAA,oBACA,gBAAA,kBAGA,uBAAA,qCACA,8BAAA,uCACA,8BAAA,oCCrCJ,uFDiEE,0DAIE,2BAAA,QACA,sBAAA,yBACA,mCAAA,2BACA,6BAAA,yBACA,4BAAA,yBACA,sBAAA,2BACA,6BAAA,kCChEJ,wFDsDE,2DAIE,2BAAA,QACA,sBAAA,2BACA,mCAAA,6BACA,6BAAA,2BACA,4BAAA,2BACA,sBAAA,2BACA,6BAAA,kCCrDJ,0FD2CE,6DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCC1CJ,+FDgCE,kEAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCC/BJ,wFDqBE,2DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCpBJ,0FDUE,6DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCTJ,8FDDE,iEAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCEJ,wFDZE,2DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCaJ,wFDvBE,2DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCwBJ,yFDlCE,4DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCmCJ,+FD7CE,kEAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCC8CJ,wFDxDE,2DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCyDJ,0FDnEE,6DAIE,2BAAA,QACA,sBAAA,yBACA,mCAAA,2BACA,6BAAA,yBACA,4BAAA,yBACA,sBAAA,2BACA,6BAAA,kCCoEJ,yFD9EE,4DAIE,2BAAA,QACA,sBAAA,yBACA,mCAAA,2BACA,6BAAA,yBACA,4BAAA,yBACA,sBAAA,2BACA,6BAAA,kCC+EJ,0FDzFE,6DAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCC0FJ,+FDpGE,kEAIE,2BAAA,QACA,sBAAA,0BACA,mCAAA,4BACA,6BAAA,0BACA,4BAAA,0BACA,sBAAA,2BACA,6BAAA,kCCqGJ,uFDzEE,yDAEE,+BAAA,QACA,qBAAA,yBACA,kCAAA,2BACA,qBAAA,2BACA,4BAAA,kCC4EJ,wFDlFE,0DAEE,+BAAA,QACA,qBAAA,2BACA,kCAAA,6BACA,qBAAA,2BACA,4BAAA,kCCqFJ,0FD3FE,4DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCC8FJ,+FDpGE,iEAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCCuGJ,wFD7GE,0DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCCgHJ,0FDtHE,4DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCCyHJ,8FD/HE,gEAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCCkIJ,wFDxIE,0DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCC2IJ,wFDjJE,0DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCCoJJ,yFD1JE,2DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCC6JJ,+FDnKE,iEAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCCsKJ,wFD5KE,0DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCC+KJ,0FDrLE,4DAEE,+BAAA,QACA,qBAAA,yBACA,kCAAA,2BACA,qBAAA,2BACA,4BAAA,kCCwLJ,yFD9LE,2DAEE,+BAAA,QACA,qBAAA,yBACA,kCAAA,2BACA,qBAAA,2BACA,4BAAA,kCCiMJ,0FDvME,4DAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCC0MJ,+FDhNE,iEAEE,+BAAA,QACA,qBAAA,0BACA,kCAAA,4BACA,qBAAA,2BACA,4BAAA,kCEjNF,eACE,MAAA,iCAGF,eDmaF,eChaI,MAAA,8BAGF,eACE,MAAA,+BDmaJ,eChaE,eAEE,MAAA,gCAGF,eACE,MAAA,kCAGF,eD8ZF,eACA,eC5ZI,MAAA,+BAEF,eD8ZF,eC5ZI,MAAA,4BAOF,2BACI,OAAA,EACA,QAAA,EAAA,cACA,MAAA,wBACA,iBAAA,+BACA,cAAA,MACA,WAAA,KAKF,mBACE,MAAA,kBACA,iBAAA,uCACA,WAAA,MAAA,MACA,WAAA,iBAAA,MAGA,qBACE,MAAA,oCAGF,yBACE,iBAAA,8CAEA,2BACE,MAAA,oCAMR,qBACE,QAAA,EAIF,oBACE,WAAA,OAGF,uBAGE,cAAA,EAAA,YAAA,EAAA,SAAA,EAEA,+BACE,aAAA,MAOA,sDACE,iBAAA,gCAQF,gDACE,aAAA,eACA,YAAA,gBACA,cAAA,cACA,aAAA,eACA,iBAAA,gCAMN,qCAaE,SAAA,SACA,QAAA,MACA,WAAA,IACA,QAAA,KAAA,eAAA,KAAA,eACA,YAAA,IACA,UAAA,OACA,iBAAA,8BACA,uBAAA,MACA,wBAAA,MAlBA,yCACE,WAAA,EAEA,8CACE,uBAAA,EACA,wBAAA,EAgBJ,6CACE,SAAA,SACA,KAAA,cACA,MAAA,eACA,OAAA,eACA,iBAAA,2BACA,mBAAA,uRAAA,WAAA,uRACA,oBAAA,UAAA,YAAA,UACA,kBAAA,QAAA,UAAA,QACA,QAAA,GAIJ,8CACE,mBAAA,qoBAAA,WAAA,qoBAGF,+CACE,mBAAA,kRAAA,WAAA,kRAGF,2BACE,SAAA,SACA,WAAA,IACA,cAAA,IAEA,+BACE,WAAA,EACA,cAAA,EAGF,iCACE,QAAA,KAEA,8CACE,MAAA,KACA,OAAA,EACA,QAAA,MAAA,KAAA,MAAA,EAEA,oDACE,SAAA,SACA,OAAA,MACA,QAAA,MAAA,KACA,MAAA,2BACA,UAAA,IACA,iBAAA,2BACA,oBAAA,UAAA,YAAA,UACA,kBAAA,QAAA,UAAA,QACA,cAAA,MACA,OAAA,QACA,QAAA,GAEA,0DACE,iBAAA,0BAGF,4DACE,SAAA,SACA,IAAA,MACA,KAAA,MACA,QAAA,MACA,WAAA,WACA,MAAA,OACA,OAAA,OACA,iBAAA,2BACA,gBAAA,OACA,QAAA,GAGF,2DACE,QAAA,KAEA,mEACE,mBAAA,oNAAA,WAAA,oNAGJ,qEACE,mBAAA,mNAAA,WAAA,mNAMJ,mEACE,QAAA,OAGF,qEACE,QAAA,KAGF,4DACE,WAAA,QACA,SAAA,OAGF,sDACE,SAAA,SACA,OAAA,EACA,KAAA,EACA,QAAA,IAAA,KAAA,KAAA,MACA,iBAAA,2EC3OR,4DACE,WAAA,oBAEA,qEACE,iBAAA,gBAGF,+DACE,MAAA,qBACA,iBAAA,2BACA,cAAA,OAAA,MAAA,0BAGF,+DACE,WAAA,OAAA,MAAA,oCAMJ,kDACE,WAAA,QAGF,8DACE,WAAA,SAGF,uDACE,OAAA,QAEI,6EACE,iBAAA,0BACA,QAAA,GACA,OAAA,KACA,mBAAA,uBACA,WAAA,uBACA,sBAAA,OACA,cAAA,OACA,oBAAA,UACA,YAAA,UACA,kBAAA,QACA,UAAA,QACA,SAAA,SAEA,UAAA,UACA,WAAA,UAAA,KACA,MAAA,KAIN,6DACE,WAAA,MAAA,EAAA,EAAA,MAAA,0BAGF,mFACE,UAAA,cAIJ,uCACE,YAAA,MACA,eAAA,MACA,YAAA,IAIJ,gDACE,WAAA,KACA,OAAA,ECsIE,wCF4CE,wBACE,aAAA,OACA,YAAA,OAEF,2BACE,aAAA,OACA,YAAA,OAEA,0CACE,KAAA","file":"extra-950ac449d4.css","sourcesContent":["@import \"./dracula\";\n\n:root > * {\n // Custom code colors\n --md-code-link-bg-color: hsla(0, 0%, 96%, 1);\n --md-code-link-accent-bg-color: var(--md-code-link-bg-color);\n --md-default-bg-color--trans: rgb(100%, 100%, 100%, 0);\n --md-code-title-bg-color: var(--md-code-bg-color);\n --md-code-inline-bg-color: var(--md-code-bg-color);\n\n --md-code-special-bg-color: #{darken(hsl(0, 0%, 96%), 5%)};\n --md-code-alternate-bg-color: var(--md-code-bg-color);\n\n --md-code-hl-punctuation-color: var(--md-code-fg-color);\n --md-code-hl-namespace-color: var(--md-code-fg-color);\n\n --md-code-hl-entity-color: var(--md-code-hl-keyword-color);\n --md-code-hl-tag-color: var(--md-code-hl-keyword-color);\n --md-code-hl-builtin-color: var(--md-code-hl-constant-color);\n --md-code-hl-class-color: var(--md-code-hl-function-color);\n\n // Various Material related color variables\n --md-typeset-a-color: #{$clr-cyan-500};\n\n // Progressbar colors\n --md-progress-stripe: var(--md-default-bg-color--lighter);\n --md-progress-100: #{$clr-green-a400};\n --md-progress-80: #{$clr-green-a400};\n --md-progress-60: #{$clr-yellow-700};\n --md-progress-40: #{$clr-orange-a400};\n --md-progress-20: #{$clr-red-a200};\n --md-progress-0: #{$clr-red-a400};\n\n // Keys colors\n --md-typeset-kbd-color: #{shade(hsla(0, 100%, 100%, 1), 8%)};\n --md-typeset-kbd-border-color: #{shade(hsla(0, 100%, 100%, 1), 28%)};\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n\n &[data-md-color-scheme=\"slate\"] {\n // Custom code colors\n --md-code-link-bg-color: hsla(232, 15%, 15%, 1);\n --md-code-link-accent-bg-color: var(--md-code-link-bg-color);\n --md-code-special-bg-color: #{lighten(hsl(232, 15%, 15%), 5%)};\n --md-default-bg-color--trans: hsla(232,15%,15%, 0);\n\n --md-default-bg-color--ultra-dark: #{darken($drac-default-bg, 15%)};\n //--md-code-fg-color: #{$drac-fg};\n //--md-code-bg-color: #{$drac-bg};\n --md-code-title-bg-color: var(--md-primary-fg-color--dark);\n //--md-code-inline-bg-color: #{lighten($drac-bg, 5%)};\n\n --md-shadow-z2: 0 #{px2rem(4px)} #{px2rem(10px)} hsla(0, 0%, 0%, 0.3),\n 0 0 #{px2rem(1px)} hsla(0, 0%, 0%, 0.2);\n\n // Keys colors\n --md-typeset-kbd-color: var(--md-default-fg-color--lightest);\n --md-typeset-kbd-border-color: #{darken(rgb(46, 48, 62), 9%)};\n --md-typeset-kbd-accent-color: var(--md-default-fg-color--lighter);\n }\n\n // Dark mode color changes\n &[data-md-color-scheme=\"dracula\"] {\n // Default color shades\n --md-default-fg-color: #{transparentize($drac-fg, 0.13)};\n --md-default-fg-color--light: #{transparentize($drac-fg, 0.46)};\n --md-default-fg-color--lighter: #{transparentize($drac-fg, 0.84)};\n --md-default-fg-color--lightest: #{transparentize($drac-fg, 0.93)};\n --md-default-autocomplete-fg-color: #{transparentize($drac-fg, 0.60)};\n --md-shadow-z2: 0 #{px2rem(4px)} #{px2rem(10px)} hsla(0, 0%, 0%, 0.3),\n 0 0 #{px2rem(1px)} hsla(0, 0%, 0%, 0.2);\n\n --md-default-bg-color: var(--md-default-bg-color--darkest);\n --md-default-bg-color--light: #{transparentize($drac-default-bg, 0.3)};\n --md-default-bg-color--lighter: #{transparentize($drac-default-bg, 0.7)};\n --md-default-bg-color--lightest: #{transparentize($drac-default-bg, 0.88)};\n --md-default-bg-color--trans: #{transparentize($drac-default-bg, 1)};\n\n // Dark specific colors\n --md-default-bg-color--dark: #{darken($drac-default-bg, 3%)};\n --md-default-bg-color--darker: #{darken($drac-default-bg, 6%)};\n --md-default-bg-color--darkest: #{darken($drac-default-bg, 9%)};\n --md-default-bg-color--ultra-dark: #{darken($drac-default-bg, 15%)};\n\n // General text\n --md-text-color: var(--md-default-fg-color);\n --md-typeset-color: var(--md-default-fg-color);\n\n // Admonition colors\n --md-admonition-fg-color: var(--md-default-fg-color);\n\n // Code colors\n --md-code-fg-color: #{$drac-fg};\n --md-code-bg-color: #{$drac-bg};\n --md-code-title-bg-color: var(--md-default-bg-color--ultra-dark);\n --md-code-inline-bg-color: #{lighten($drac-bg, 5%)};\n --md-code-hl-operator-color: #{$drac-pink};\n --md-code-hl-punctuation-color: #{$drac-fg};\n --md-code-hl-string-color: #{$drac-yellow};\n --md-code-hl-special-color: #{$drac-purple};\n --md-code-hl-number-color: #{$drac-purple};\n --md-code-hl-keyword-color: #{$drac-pink};\n --md-code-hl-name-color: #{$drac-fg};\n --md-code-hl-constant-color: #{$drac-purple};\n --md-code-hl-function-color: #{$drac-green};\n --md-code-hl-comment-color: #{$drac-blue};\n --md-code-hl-variable-color: #{$drac-orange};\n --md-code-hl-generic-color: #{$drac-blue};\n --md-code-hl-color: #{$drac-selection};\n\n // Custom code colors\n --md-code-hl-entity-color: #{$drac-green};\n --md-code-hl-tag-color: #{$drac-pink};\n --md-code-hl-namespace-color: #{$drac-fg};\n --md-code-hl-builtin-color: #{$drac-cyan};\n --md-code-hl-class-color: #{$drac-cyan};\n\n --md-code-special-bg-color: #{darken($drac-bg, 5%)};\n --md-code-alternate-bg-color: #{tint($drac-bg, 10%)};\n --md-code-link-bg-color: #{mix($drac-cyan, $drac-bg, 15%)};\n\n // Various Material related color variables\n --md-typeset-a-color: #{$drac-cyan};\n --md-typeset-mark-color: #{mix($drac-yellow, $drac-bg, 35%)};\n --md-typeset-del-color: #{mix($drac-pink, $drac-bg, 35%)};\n --md-typeset-ins-color: #{mix($drac-green, $drac-bg, 35%)};\n\n // Progressbar colors\n --md-progress-stripe: var(--md-default-bg-color--lightest);\n --md-progress-100: #{$drac-green};\n --md-progress-80: #{$drac-light-green};\n --md-progress-60: #{$drac-yellow};\n --md-progress-40: #{$drac-orange};\n --md-progress-20: #{$drac-pink};\n --md-progress-0: #{$drac-red};\n\n // Keys colors\n --md-typeset-kbd-color: var(--md-default-fg-color--lightest);\n --md-typeset-kbd-border-color: var(--md-default-bg-color--ultra-dark);\n --md-typeset-kbd-accent-color: var(--md-default-fg-color--lighter);\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: primary colors\n// ----------------------------------------------------------------------------\n\n@each $name, $colors in (\n \"red\": $drac-red lighten($drac-red, 5%) darken($drac-red, 5%),\n \"pink\": $drac-pink lighten($drac-pink, 5%) darken($drac-pink, 5%),\n \"purple\": $drac-purple lighten($drac-purple, 5%) darken($drac-purple, 5%),\n \"deep-purple\": $drac-purple lighten($drac-purple, 5%) darken($drac-purple, 5%),\n \"blue\": $drac-blue lighten($drac-blue, 5%) darken($drac-blue, 5%),\n \"indigo\": $drac-blue lighten($drac-blue, 5%) darken($drac-blue, 5%),\n \"light-blue\": $drac-blue lighten($drac-blue, 5%) darken($drac-blue, 5%),\n \"cyan\": $drac-cyan lighten($drac-cyan, 5%) darken($drac-cyan, 5%),\n \"teal\": $drac-cyan lighten($drac-cyan, 5%) darken($drac-cyan, 5%),\n \"green\": $drac-green lighten($drac-green, 5%) darken($drac-green, 5%),\n \"light-green\": $drac-green lighten($drac-green, 5%) darken($drac-green, 5%),\n \"lime\": $drac-green lighten($drac-green, 5%) darken($drac-green, 5%),\n \"yellow\": $drac-yellow lighten($drac-yellow, 5%) darken($drac-yellow, 5%),\n \"amber\": $drac-yellow lighten($drac-yellow, 5%) darken($drac-yellow, 5%),\n \"orange\": $drac-orange lighten($drac-orange, 5%) darken($drac-orange, 5%),\n \"deep-orange\": $drac-orange lighten($drac-orange, 5%) darken($drac-orange, 5%)\n) {\n\n // Color palette\n [data-md-color-scheme=\"dracula\"][data-md-color-primary=\"#{$name}\"],\n [data-md-color-scheme=\"dracula\"] :not([data-md-color-scheme])[data-md-color-primary=\"#{$name}\"]\n {\n\n --md-primary-code-bg-color: #{mix($drac-bg, nth($colors, 1), 85%)};\n --md-primary-fg-color: hsla(#{hex2hsl(nth($colors, 1))}, 1);\n --md-primary-fg-color--transparent: hsla(#{hex2hsl(nth($colors, 1))}, 0.1);\n --md-primary-fg-color--light: hsla(#{hex2hsl(nth($colors, 2))}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl(nth($colors, 3))}, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: accent colors\n// ----------------------------------------------------------------------------\n\n@each $name, $color in (\n \"red\": darken($drac-red, 5%),\n \"pink\": darken($drac-pink, 5%),\n \"purple\": darken($drac-purple, 5%),\n \"deep-purple\": darken($drac-purple, 5%),\n \"blue\": darken($drac-blue, 5%),\n \"indigo\": darken($drac-blue, 5%),\n \"light-blue\": darken($drac-blue, 5%),\n \"cyan\": darken($drac-cyan, 5%),\n \"teal\": darken($drac-cyan, 5%),\n \"green\": darken($drac-green, 5%),\n \"light-green\": darken($drac-green, 5%),\n \"lime\": darken($drac-green, 5%),\n \"yellow\": darken($drac-yellow, 5%),\n \"amber\": darken($drac-yellow, 5%),\n \"orange\": darken($drac-orange, 5%),\n \"deep-orange\": darken($drac-orange, 5%)\n) {\n\n // Color palette\n [data-md-color-scheme=\"dracula\"][data-md-color-accent=\"#{$name}\"],\n [data-md-color-scheme=\"dracula\"] :not([data-md-color-scheme])[data-md-color-primary=\"#{$name}\"] {\n --md-code-link-accent-bg-color: #{mix($color, $drac-bg, 15%)};\n --md-accent-fg-color: hsla(#{hex2hsl($color)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($color)}, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n }\n}\n",":root > * {\n --md-code-link-bg-color: hsla(0, 0%, 96%, 1);\n --md-code-link-accent-bg-color: var(--md-code-link-bg-color);\n --md-default-bg-color--trans: rgb(100%, 100%, 100%, 0);\n --md-code-title-bg-color: var(--md-code-bg-color);\n --md-code-inline-bg-color: var(--md-code-bg-color);\n --md-code-special-bg-color: #e8e8e8;\n --md-code-alternate-bg-color: var(--md-code-bg-color);\n --md-code-hl-punctuation-color: var(--md-code-fg-color);\n --md-code-hl-namespace-color: var(--md-code-fg-color);\n --md-code-hl-entity-color: var(--md-code-hl-keyword-color);\n --md-code-hl-tag-color: var(--md-code-hl-keyword-color);\n --md-code-hl-builtin-color: var(--md-code-hl-constant-color);\n --md-code-hl-class-color: var(--md-code-hl-function-color);\n --md-typeset-a-color: #00bcd4;\n --md-progress-stripe: var(--md-default-bg-color--lighter);\n --md-progress-100: #00e676;\n --md-progress-80: #00e676;\n --md-progress-60: #fbc02d;\n --md-progress-40: #ff9100;\n --md-progress-20: #ff5252;\n --md-progress-0: #ff1744;\n --md-typeset-kbd-color: #ebebeb;\n --md-typeset-kbd-border-color: #b8b8b8;\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n}\n:root > *[data-md-color-scheme=slate] {\n --md-code-link-bg-color: hsla(232, 15%, 15%, 1);\n --md-code-link-accent-bg-color: var(--md-code-link-bg-color);\n --md-code-special-bg-color: #2b2d3b;\n --md-default-bg-color--trans: hsla(232,15%,15%, 0);\n --md-default-bg-color--ultra-dark: #111217;\n --md-code-title-bg-color: var(--md-primary-fg-color--dark);\n --md-shadow-z2: 0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),\n 0 0 0.05rem hsla(0, 0%, 0%, 0.2);\n --md-typeset-kbd-color: var(--md-default-fg-color--lightest);\n --md-typeset-kbd-border-color: #1a1c24;\n --md-typeset-kbd-accent-color: var(--md-default-fg-color--lighter);\n}\n:root > *[data-md-color-scheme=dracula] {\n --md-default-fg-color: rgba(248, 248, 242, 0.87);\n --md-default-fg-color--light: rgba(248, 248, 242, 0.54);\n --md-default-fg-color--lighter: rgba(248, 248, 242, 0.16);\n --md-default-fg-color--lightest: rgba(248, 248, 242, 0.07);\n --md-default-autocomplete-fg-color: rgba(248, 248, 242, 0.4);\n --md-shadow-z2: 0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),\n 0 0 0.05rem hsla(0, 0%, 0%, 0.2);\n --md-default-bg-color: var(--md-default-bg-color--darkest);\n --md-default-bg-color--light: rgba(50, 52, 67, 0.7);\n --md-default-bg-color--lighter: rgba(50, 52, 67, 0.3);\n --md-default-bg-color--lightest: rgba(50, 52, 67, 0.12);\n --md-default-bg-color--trans: rgba(50, 52, 67, 0);\n --md-default-bg-color--dark: #2b2e3b;\n --md-default-bg-color--darker: #252732;\n --md-default-bg-color--darkest: #1e2029;\n --md-default-bg-color--ultra-dark: #111217;\n --md-text-color: var(--md-default-fg-color);\n --md-typeset-color: var(--md-default-fg-color);\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-code-fg-color: hsl(60, 30%, 96%);\n --md-code-bg-color: hsl(231, 15%, 18%);\n --md-code-title-bg-color: var(--md-default-bg-color--ultra-dark);\n --md-code-inline-bg-color: #323443;\n --md-code-hl-operator-color: hsl(326, 100%, 74%);\n --md-code-hl-punctuation-color: hsl(60, 30%, 96%);\n --md-code-hl-string-color: hsl(65, 92%, 76%);\n --md-code-hl-special-color: hsl(265, 89%, 78%);\n --md-code-hl-number-color: hsl(265, 89%, 78%);\n --md-code-hl-keyword-color: hsl(326, 100%, 74%);\n --md-code-hl-name-color: hsl(60, 30%, 96%);\n --md-code-hl-constant-color: hsl(265, 89%, 78%);\n --md-code-hl-function-color: hsl(135, 94%, 65%);\n --md-code-hl-comment-color: hsl(225, 27%, 51%);\n --md-code-hl-variable-color: hsl(31, 100%, 71%);\n --md-code-hl-generic-color: hsl(225, 27%, 51%);\n --md-code-hl-color: hsl(231, 25%, 25%);\n --md-code-hl-entity-color: hsl(135, 94%, 65%);\n --md-code-hl-tag-color: hsl(326, 100%, 74%);\n --md-code-hl-namespace-color: hsl(60, 30%, 96%);\n --md-code-hl-builtin-color: hsl(191, 97%, 77%);\n --md-code-hl-class-color: hsl(191, 97%, 77%);\n --md-code-special-bg-color: #1c1e26;\n --md-code-alternate-bg-color: #3d3e49;\n --md-code-link-bg-color: #364653;\n --md-typeset-a-color: hsl(191, 97%, 77%);\n --md-typeset-mark-color: #6e7252;\n --md-typeset-del-color: #734568;\n --md-typeset-ins-color: #36724e;\n --md-progress-stripe: var(--md-default-bg-color--lightest);\n --md-progress-100: hsl(135, 94%, 65%);\n --md-progress-80: hsl(135, 92%, 79%);\n --md-progress-60: hsl(65, 92%, 76%);\n --md-progress-40: hsl(31, 100%, 71%);\n --md-progress-20: hsl(326, 100%, 74%);\n --md-progress-0: hsl(0, 100%, 67%);\n --md-typeset-kbd-color: var(--md-default-fg-color--lightest);\n --md-typeset-kbd-border-color: var(--md-default-bg-color--ultra-dark);\n --md-typeset-kbd-accent-color: var(--md-default-fg-color--lighter);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=red],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=red] {\n --md-primary-code-bg-color: #47303a;\n --md-primary-fg-color: hsla(0deg, 100%, 67%, 1);\n --md-primary-fg-color--transparent: hsla(0deg, 100%, 67%, 0.1);\n --md-primary-fg-color--light: hsla(0deg, 100%, 72%, 1);\n --md-primary-fg-color--dark: hsla(0deg, 100%, 62%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=pink],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=pink] {\n --md-primary-code-bg-color: #47354b;\n --md-primary-fg-color: hsla(326deg, 100%, 74%, 1);\n --md-primary-fg-color--transparent: hsla(326deg, 100%, 74%, 0.1);\n --md-primary-fg-color--light: hsla(326deg, 100%, 79%, 1);\n --md-primary-fg-color--dark: hsla(326deg, 100%, 69%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=purple],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=purple] {\n --md-primary-code-bg-color: #3e3952;\n --md-primary-fg-color: hsla(265deg, 89%, 78%, 1);\n --md-primary-fg-color--transparent: hsla(265deg, 89%, 78%, 0.1);\n --md-primary-fg-color--light: hsla(265deg, 89%, 83%, 1);\n --md-primary-fg-color--dark: hsla(265deg, 89%, 73%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=deep-purple],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-purple] {\n --md-primary-code-bg-color: #3e3952;\n --md-primary-fg-color: hsla(265deg, 89%, 78%, 1);\n --md-primary-fg-color--transparent: hsla(265deg, 89%, 78%, 0.1);\n --md-primary-fg-color--light: hsla(265deg, 89%, 83%, 1);\n --md-primary-fg-color--dark: hsla(265deg, 89%, 73%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=blue],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=blue] {\n --md-primary-code-bg-color: #303446;\n --md-primary-fg-color: hsla(225deg, 27%, 51%, 1);\n --md-primary-fg-color--transparent: hsla(225deg, 27%, 51%, 0.1);\n --md-primary-fg-color--light: hsla(225deg, 27%, 56%, 1);\n --md-primary-fg-color--dark: hsla(225deg, 27%, 46%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=indigo],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=indigo] {\n --md-primary-code-bg-color: #303446;\n --md-primary-fg-color: hsla(225deg, 27%, 51%, 1);\n --md-primary-fg-color--transparent: hsla(225deg, 27%, 51%, 0.1);\n --md-primary-fg-color--light: hsla(225deg, 27%, 56%, 1);\n --md-primary-fg-color--dark: hsla(225deg, 27%, 46%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=light-blue],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-blue] {\n --md-primary-code-bg-color: #303446;\n --md-primary-fg-color: hsla(225deg, 27%, 51%, 1);\n --md-primary-fg-color--transparent: hsla(225deg, 27%, 51%, 0.1);\n --md-primary-fg-color--light: hsla(225deg, 27%, 56%, 1);\n --md-primary-fg-color--dark: hsla(225deg, 27%, 46%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=cyan],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=cyan] {\n --md-primary-code-bg-color: #364653;\n --md-primary-fg-color: hsla(191deg, 97%, 77%, 1);\n --md-primary-fg-color--transparent: hsla(191deg, 97%, 77%, 0.1);\n --md-primary-fg-color--light: hsla(191deg, 97%, 82%, 1);\n --md-primary-fg-color--dark: hsla(191deg, 97%, 72%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=teal],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=teal] {\n --md-primary-code-bg-color: #364653;\n --md-primary-fg-color: hsla(191deg, 97%, 77%, 1);\n --md-primary-fg-color--transparent: hsla(191deg, 97%, 77%, 0.1);\n --md-primary-fg-color--light: hsla(191deg, 97%, 82%, 1);\n --md-primary-fg-color--dark: hsla(191deg, 97%, 72%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=green],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=green] {\n --md-primary-code-bg-color: #2d4840;\n --md-primary-fg-color: hsla(135deg, 94%, 65%, 1);\n --md-primary-fg-color--transparent: hsla(135deg, 94%, 65%, 0.1);\n --md-primary-fg-color--light: hsla(135deg, 94%, 70%, 1);\n --md-primary-fg-color--dark: hsla(135deg, 94%, 60%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=light-green],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-green] {\n --md-primary-code-bg-color: #2d4840;\n --md-primary-fg-color: hsla(135deg, 94%, 65%, 1);\n --md-primary-fg-color--transparent: hsla(135deg, 94%, 65%, 0.1);\n --md-primary-fg-color--light: hsla(135deg, 94%, 70%, 1);\n --md-primary-fg-color--dark: hsla(135deg, 94%, 60%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=lime],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=lime] {\n --md-primary-code-bg-color: #2d4840;\n --md-primary-fg-color: hsla(135deg, 94%, 65%, 1);\n --md-primary-fg-color--transparent: hsla(135deg, 94%, 65%, 0.1);\n --md-primary-fg-color--light: hsla(135deg, 94%, 70%, 1);\n --md-primary-fg-color--dark: hsla(135deg, 94%, 60%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=yellow],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=yellow] {\n --md-primary-code-bg-color: #454842;\n --md-primary-fg-color: hsla(65deg, 92%, 76%, 1);\n --md-primary-fg-color--transparent: hsla(65deg, 92%, 76%, 0.1);\n --md-primary-fg-color--light: hsla(65deg, 92%, 81%, 1);\n --md-primary-fg-color--dark: hsla(65deg, 92%, 71%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=amber],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=amber] {\n --md-primary-code-bg-color: #454842;\n --md-primary-fg-color: hsla(65deg, 92%, 76%, 1);\n --md-primary-fg-color--transparent: hsla(65deg, 92%, 76%, 0.1);\n --md-primary-fg-color--light: hsla(65deg, 92%, 81%, 1);\n --md-primary-fg-color--dark: hsla(65deg, 92%, 71%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=orange],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=orange] {\n --md-primary-code-bg-color: #473e3d;\n --md-primary-fg-color: hsla(31deg, 100%, 71%, 1);\n --md-primary-fg-color--transparent: hsla(31deg, 100%, 71%, 0.1);\n --md-primary-fg-color--light: hsla(31deg, 100%, 76%, 1);\n --md-primary-fg-color--dark: hsla(31deg, 100%, 66%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-primary=deep-orange],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-orange] {\n --md-primary-code-bg-color: #473e3d;\n --md-primary-fg-color: hsla(31deg, 100%, 71%, 1);\n --md-primary-fg-color--transparent: hsla(31deg, 100%, 71%, 0.1);\n --md-primary-fg-color--light: hsla(31deg, 100%, 76%, 1);\n --md-primary-fg-color--dark: hsla(31deg, 100%, 66%, 1);\n --md-primary-bg-color: var(--md-default-bg-color);\n --md-primary-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=red],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=red] {\n --md-code-link-accent-bg-color: #472c36;\n --md-accent-fg-color: hsla(0deg, 100%, 62%, 1);\n --md-accent-fg-color--transparent: hsla(0deg, 100%, 62%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=pink],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=pink] {\n --md-code-link-accent-bg-color: #473149;\n --md-accent-fg-color: hsla(326deg, 100%, 69%, 1);\n --md-accent-fg-color--transparent: hsla(326deg, 100%, 69%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=purple],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=purple] {\n --md-code-link-accent-bg-color: #3c3652;\n --md-accent-fg-color: hsla(265deg, 89%, 73%, 1);\n --md-accent-fg-color--transparent: hsla(265deg, 89%, 73%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=deep-purple],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-purple] {\n --md-code-link-accent-bg-color: #3c3652;\n --md-accent-fg-color: hsla(265deg, 89%, 73%, 1);\n --md-accent-fg-color--transparent: hsla(265deg, 89%, 73%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=blue],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=blue] {\n --md-code-link-accent-bg-color: #2e3243;\n --md-accent-fg-color: hsla(225deg, 27%, 46%, 1);\n --md-accent-fg-color--transparent: hsla(225deg, 27%, 46%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=indigo],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=indigo] {\n --md-code-link-accent-bg-color: #2e3243;\n --md-accent-fg-color: hsla(225deg, 27%, 46%, 1);\n --md-accent-fg-color--transparent: hsla(225deg, 27%, 46%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=light-blue],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-blue] {\n --md-code-link-accent-bg-color: #2e3243;\n --md-accent-fg-color: hsla(225deg, 27%, 46%, 1);\n --md-accent-fg-color--transparent: hsla(225deg, 27%, 46%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=cyan],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=cyan] {\n --md-code-link-accent-bg-color: #324553;\n --md-accent-fg-color: hsla(191deg, 97%, 72%, 1);\n --md-accent-fg-color--transparent: hsla(191deg, 97%, 72%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=teal],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=teal] {\n --md-code-link-accent-bg-color: #324553;\n --md-accent-fg-color: hsla(191deg, 97%, 72%, 1);\n --md-accent-fg-color--transparent: hsla(191deg, 97%, 72%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=green],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=green] {\n --md-code-link-accent-bg-color: #2a483d;\n --md-accent-fg-color: hsla(135deg, 94%, 60%, 1);\n --md-accent-fg-color--transparent: hsla(135deg, 94%, 60%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=light-green],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-green] {\n --md-code-link-accent-bg-color: #2a483d;\n --md-accent-fg-color: hsla(135deg, 94%, 60%, 1);\n --md-accent-fg-color--transparent: hsla(135deg, 94%, 60%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=lime],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=lime] {\n --md-code-link-accent-bg-color: #2a483d;\n --md-accent-fg-color: hsla(135deg, 94%, 60%, 1);\n --md-accent-fg-color--transparent: hsla(135deg, 94%, 60%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=yellow],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=yellow] {\n --md-code-link-accent-bg-color: #45483e;\n --md-accent-fg-color: hsla(65deg, 92%, 71%, 1);\n --md-accent-fg-color--transparent: hsla(65deg, 92%, 71%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=amber],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=amber] {\n --md-code-link-accent-bg-color: #45483e;\n --md-accent-fg-color: hsla(65deg, 92%, 71%, 1);\n --md-accent-fg-color--transparent: hsla(65deg, 92%, 71%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=orange],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=orange] {\n --md-code-link-accent-bg-color: #473d39;\n --md-accent-fg-color: hsla(31deg, 100%, 66%, 1);\n --md-accent-fg-color--transparent: hsla(31deg, 100%, 66%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n[data-md-color-scheme=dracula][data-md-color-accent=deep-orange],\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-orange] {\n --md-code-link-accent-bg-color: #473d39;\n --md-accent-fg-color: hsla(31deg, 100%, 66%, 1);\n --md-accent-fg-color--transparent: hsla(31deg, 100%, 66%, 0.1);\n --md-accent-bg-color: var(--md-default-bg-color);\n --md-accent-bg-color--light: var(--md-default-bg-color--light);\n}\n\n.highlight .kc {\n color: var(--md-code-hl-constant-color);\n}\n.highlight .nc,\n.highlight .ne {\n color: var(--md-code-hl-class-color);\n}\n.highlight .mb {\n color: var(--md-code-hl-number-color);\n}\n.highlight .nb,\n.highlight .bp {\n color: var(--md-code-hl-builtin-color);\n}\n.highlight .nn {\n color: var(--md-code-hl-namespace-color);\n}\n.highlight .na,\n.highlight .nd,\n.highlight .ni {\n color: var(--md-code-hl-entity-color);\n}\n.highlight .nl,\n.highlight .nt {\n color: var(--md-code-hl-tag-color);\n}\n\n.md-typeset {\n /* Allow code to look like code everywhere despite Material's current preference */\n /* Code that is also a link */\n /* Don't always like code breaking in table cells */\n /* Special line number coloring for tables */\n}\n.md-typeset *:not(pre) > code {\n margin: 0;\n padding: 0 0.2941176471em;\n color: var(--md-code-fg-color);\n background-color: var(--md-code-inline-bg-color);\n border-radius: 0.1rem;\n box-shadow: none;\n}\n.md-typeset a > code {\n color: inherit !important;\n background-color: var(--md-code-link-bg-color) !important;\n transition: color 125ms;\n transition: background-color 125ms;\n /* If we are linking highlighted, inline code, force it to just look like a code link */\n}\n.md-typeset a > code * {\n color: var(--md-typeset-a-color) !important;\n}\n.md-typeset a > code:hover {\n background-color: var(--md-code-link-accent-bg-color) !important;\n}\n.md-typeset a > code:hover * {\n color: var(--md-accent-fg-color) !important;\n}\n.md-typeset pre > code {\n outline: none;\n}\n.md-typeset td code {\n word-break: normal;\n}\n.md-typeset .highlight {\n /* Needed for tab preserving mode */\n tab-size: 8;\n /* `pymdownx-inline` mode */\n}\n.md-typeset .highlight + .result {\n border-width: 0.1rem;\n}\n.md-typeset .highlight [data-linenos] {\n /* Special line mode coloring */\n}\n.md-typeset .highlight [data-linenos].special::before {\n background-color: var(--md-code-special-bg-color);\n}\n.md-typeset .highlighttable .linenodiv .special {\n margin-right: -0.5882352941em;\n margin-left: -1.1764705882em;\n padding-right: 0.5882352941em;\n padding-left: 1.1764705882em;\n background-color: var(--md-code-special-bg-color);\n}\n.md-typeset .highlight span.filename {\n position: relative;\n display: block;\n margin-top: 1em;\n padding: 0.5em 1.1764705882em 0.5em 2.9411764706em;\n font-weight: 700;\n font-size: 0.68rem;\n background-color: var(--md-code-title-bg-color);\n border-top-left-radius: 0.1rem;\n border-top-right-radius: 0.1rem;\n}\n.md-typeset .highlight span.filename + pre {\n margin-top: 0;\n}\n.md-typeset .highlight span.filename + pre code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.md-typeset .highlight span.filename::before {\n position: absolute;\n left: 0.8823529412em;\n width: 1.4705882353em;\n height: 1.4705882353em;\n background-color: var(--md-default-fg-color);\n mask-image: svg-load(\"@mdi/svg/svg/console.svg\");\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n}\n.md-typeset .apirequest span.filename::before {\n mask-image: svg-load(\"@mdi/svg/svg/access-point.svg\");\n}\n.md-typeset .apiresponse span.filename::before {\n mask-image: svg-load(\"@mdi/svg/svg/application-import.svg\");\n}\n.md-typeset .collapse-code {\n position: relative;\n margin-top: 1em;\n margin-bottom: 1em;\n}\n.md-typeset .collapse-code pre {\n margin-top: 0;\n margin-bottom: 0;\n}\n.md-typeset .collapse-code input {\n display: none;\n}\n.md-typeset .collapse-code input ~ .code-footer {\n width: 100%;\n margin: 0;\n padding: 0.25em 0.5em 0.25em 0em;\n}\n.md-typeset .collapse-code input ~ .code-footer label {\n position: relative;\n margin: 0.05em;\n padding: 0.15em 0.8em;\n color: var(--md-primary-bg-color);\n font-size: 90%;\n background-color: var(--md-primary-fg-color);\n mask-repeat: no-repeat;\n mask-size: contain;\n border-radius: 0.1rem;\n cursor: pointer;\n content: \"\";\n}\n.md-typeset .collapse-code input ~ .code-footer label:hover {\n background-color: var(--md-accent-fg-color);\n}\n.md-typeset .collapse-code input ~ .code-footer label::before {\n position: absolute;\n top: 0.15em;\n left: 0.15em;\n display: block;\n box-sizing: border-box;\n width: 1.25em;\n height: 1.25em;\n background-color: var(--md-primary-bg-color);\n background-size: 1.25em;\n content: \"\";\n}\n.md-typeset .collapse-code input ~ .code-footer label.expand {\n display: none;\n}\n.md-typeset .collapse-code input ~ .code-footer label.expand::before {\n mask-image: svg-load(\"@mdi/svg/svg/arrow-expand.svg\");\n}\n.md-typeset .collapse-code input ~ .code-footer label.collapse::before {\n mask-image: svg-load(\"@mdi/svg/svg/arrow-collapse.svg\");\n}\n.md-typeset .collapse-code input:checked ~ .code-footer label.expand {\n display: inline;\n}\n.md-typeset .collapse-code input:checked ~ .code-footer label.collapse {\n display: none;\n}\n.md-typeset .collapse-code input:checked + div.highlight code {\n max-height: 9.375em;\n overflow: hidden;\n}\n.md-typeset .collapse-code input:checked ~ .code-footer {\n position: absolute;\n bottom: 0;\n left: 0;\n padding: 2em 0.5em 0.5em 0.8rem;\n background-image: linear-gradient(to bottom, transparent, var(--md-default-bg-color) 80% 100%);\n}\n@media screen and (max-width: 44.9375em) {\n .md-typeset > diagram-div {\n margin-right: -0.8rem;\n margin-left: -0.8rem;\n }\n .md-typeset > .collapse-code {\n margin-right: -0.8rem;\n margin-left: -0.8rem;\n }\n .md-typeset > .collapse-code label.collapse {\n left: 0.8rem;\n }\n}\n\n/* Dark mode changes */\n[data-md-color-scheme=slate] .md-typeset table:not([class]) {\n box-shadow: var(--md-shadow-z2);\n}\n[data-md-color-scheme=slate] .md-typeset table:not([class]) tr:hover {\n background-color: rgba(0, 0, 0, 0.08);\n}\n[data-md-color-scheme=slate] .md-typeset table:not([class]) th {\n color: var(--md-text-color);\n background-color: var(--md-primary-fg-color);\n border-bottom: 0.05rem solid var(--md-accent-fg-color);\n}\n[data-md-color-scheme=slate] .md-typeset table:not([class]) td {\n border-top: 0.05rem solid var(--md-default-fg-color--lighter);\n}\n\n.md-typeset table:not([class]) tr.table-container {\n visibility: visible;\n}\n.md-typeset table:not([class]) tr.table-container:not([open]) {\n visibility: collapse;\n}\n.md-typeset table:not([class]) tr.table-container-ctrl {\n cursor: pointer;\n}\n.md-typeset table:not([class]) tr.table-container-ctrl > td:first-child::after {\n background-color: var(--md-accent-fg-color);\n content: \"\";\n height: 1rem;\n -webkit-mask-image: var(--md-details-icon);\n mask-image: var(--md-details-icon);\n -webkit-mask-position: center;\n mask-position: center;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: contain;\n mask-size: contain;\n position: absolute;\n transform: rotate(0deg);\n transition: transform 0.25s;\n width: 1rem;\n}\n.md-typeset table:not([class]) tr.table-container-ctrl:hover {\n box-shadow: inset 0 0 0.3rem var(--md-accent-fg-color);\n}\n.md-typeset table:not([class]) tr.table-container-ctrl[open] > td:first-child::after {\n transform: rotate(90deg);\n}\n.md-typeset table:not([class]) caption {\n padding-top: 0.3rem;\n padding-bottom: 0.3rem;\n font-weight: bold;\n}\n\n.md-typeset .table-container table:not([class]) {\n box-shadow: none;\n margin: 0;\n}",".highlight {\n .kc { // Keyword constant\n color: var(--md-code-hl-constant-color);\n }\n\n .nc, // Name, class\n .ne, // Name, exception\n {\n color: var(--md-code-hl-class-color);\n }\n\n .mb {\n color: var(--md-code-hl-number-color);\n }\n\n .nb, // Name, builtin\n .bp { // Name, builtin pseudo\n color: var(--md-code-hl-builtin-color);\n }\n\n .nn { // Name, namespace Name, namespace Name, namespace Name, namespace\n color: var(--md-code-hl-namespace-color);\n }\n\n .na, // Name, attribute\n .nd, // Name, decorator\n .ni { // Name, entity\n color: var(--md-code-hl-entity-color);\n }\n .nl, // Name, label\n .nt { // Name, tag\n color: var(--md-code-hl-tag-color);\n }\n}\n\n.md-typeset {\n\n /* Allow code to look like code everywhere despite Material's current preference */\n *:not(pre) > code {\n margin: 0;\n padding: 0 px2em( 4px, 13.6px);\n color: var(--md-code-fg-color);\n background-color: var(--md-code-inline-bg-color);\n border-radius: px2rem(2px);\n box-shadow: none;\n }\n\n /* Code that is also a link */\n a {\n > code {\n color: inherit !important;\n background-color: var(--md-code-link-bg-color) !important;\n transition: color 125ms;\n transition: background-color 125ms;\n\n /* If we are linking highlighted, inline code, force it to just look like a code link */\n * {\n color: var(--md-typeset-a-color) !important;\n }\n\n &:hover {\n background-color: var(--md-code-link-accent-bg-color) !important;\n\n * {\n color: var(--md-accent-fg-color) !important;\n }\n }\n }\n }\n\n pre > code {\n outline: none;\n }\n\n /* Don't always like code breaking in table cells */\n td code {\n word-break: normal;\n }\n\n .highlight {\n\n /* Needed for tab preserving mode */\n tab-size: 8;\n\n + .result {\n border-width: px2rem(2px);\n }\n\n /* `pymdownx-inline` mode */\n [data-linenos] {\n\n /* Special line mode coloring */\n &.special::before {\n background-color: var(--md-code-special-bg-color);\n }\n }\n }\n\n /* Special line number coloring for tables */\n .highlighttable {\n .linenodiv {\n .special {\n margin-right: px2em(-8px, 13.6px);\n margin-left: px2em(-16px, 13.6px);\n padding-right: px2em(8px, 13.6px);\n padding-left: px2em(16px, 13.6px);\n background-color: var(--md-code-special-bg-color);\n }\n }\n }\n\n // Filename span\n .highlight span.filename {\n\n // Adjust margins and and general container look of code block\n + pre {\n margin-top: 0;\n\n code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Style the filename banner\n position: relative;\n display: block;\n margin-top: 1em;\n padding: px2em(8px) px2em(16px, 13.6px) px2em(8px) px2em(40px, 13.6px);\n font-weight: 700;\n font-size: px2rem(13.6px);\n background-color: var(--md-code-title-bg-color);\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n\n // Add code icon\n &::before {\n position: absolute;\n left: px2em(12px, 13.6px);\n width: px2em(20px, 13.6px);\n height: px2em(20px, 13.6px);\n background-color: var(--md-default-fg-color);\n mask-image: svg-load(\"@mdi/svg/svg/console.svg\");\n mask-repeat: no-repeat;\n mask-size: contain;\n content: \"\";\n }\n }\n\n .apirequest span.filename::before {\n mask-image: svg-load(\"@mdi/svg/svg/access-point.svg\");\n }\n\n .apiresponse span.filename::before {\n mask-image: svg-load(\"@mdi/svg/svg/application-import.svg\");\n }\n\n .collapse-code {\n position: relative;\n margin-top: 1em;\n margin-bottom: 1em;\n\n pre {\n margin-top: 0;\n margin-bottom: 0;\n }\n\n input{\n display: none;\n\n ~ .code-footer {\n width: 100%;\n margin: 0;\n padding: px2em(4px) px2em(8px) px2em(4px) px2em(0px);\n\n label {\n position: relative;\n margin: 0.05em;\n padding: 0.15em 0.8em;\n color: var(--md-primary-bg-color);\n font-size: 90%;\n background-color: var(--md-primary-fg-color);\n mask-repeat: no-repeat;\n mask-size: contain;\n border-radius: px2rem(2px);\n cursor: pointer;\n content: \"\";\n\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n\n &::before {\n position: absolute;\n top: 0.15em;\n left: 0.15em;\n display: block;\n box-sizing: border-box;\n width: 1.25em;\n height: 1.25em;\n background-color: var(--md-primary-bg-color);\n background-size: 1.25em;\n content: \"\";\n }\n\n &.expand {\n display: none;\n\n &::before {\n mask-image: svg-load(\"@mdi/svg/svg/arrow-expand.svg\");\n }\n }\n &.collapse::before {\n mask-image: svg-load(\"@mdi/svg/svg/arrow-collapse.svg\");\n }\n }\n }\n\n &:checked {\n ~ .code-footer label.expand {\n display: inline;\n }\n\n ~ .code-footer label.collapse {\n display: none;\n }\n\n + div.highlight code {\n max-height: px2em(150px);\n overflow: hidden;\n }\n\n ~ .code-footer {\n position: absolute;\n bottom: 0;\n left: 0;\n padding: px2em(32px) px2em(8px) px2em(8px) px2rem(16px);\n background-image: linear-gradient(to bottom,\n transparent,\n var(--md-default-bg-color) 80%\n 100%);\n }\n }\n }\n }\n\n @include break-to-device(mobile) {\n\n > {\n diagram-div {\n margin-right: px2rem(-16px);\n margin-left: px2rem(-16px);\n }\n .collapse-code {\n margin-right: px2rem(-16px);\n margin-left: px2rem(-16px);\n\n label.collapse {\n left: px2rem(16px);\n }\n }\n }\n }\n}\n","/* Dark mode changes */\n[data-md-color-scheme=\"slate\"] {\n .md-typeset table:not([class]) {\n box-shadow: var(--md-shadow-z2);\n\n tr:hover {\n background-color: rgba(0,0,0,.08);\n }\n\n th {\n color: var(--md-text-color);\n background-color: var(--md-primary-fg-color);\n border-bottom: px2rem(1px) solid var(--md-accent-fg-color);\n }\n\n td {\n border-top: px2rem(1px) solid var(--md-default-fg-color--lighter);\n }\n }\n}\n\n.md-typeset table:not([class]) {\n tr.table-container {\n visibility: visible;\n }\n\n tr.table-container:not([open]) {\n visibility: collapse;\n }\n\n tr.table-container-ctrl {\n cursor: pointer;\n > td:first-child {\n &::after {\n background-color: var(--md-accent-fg-color);\n content: \"\";\n height: 1rem;\n -webkit-mask-image: var(--md-details-icon);\n mask-image: var(--md-details-icon);\n -webkit-mask-position: center;\n mask-position: center;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: contain;\n mask-size: contain;\n position: absolute;\n //top: .625em;\n transform: rotate(0deg);\n transition: transform .25s;\n width: 1rem;\n }\n }\n\n &:hover {\n box-shadow: inset 0 0 px2rem(6px) var(--md-accent-fg-color);\n }\n\n &[open] > td:first-child::after {\n transform: rotate(90deg);\n }\n }\n\n & caption {\n padding-top: px2rem(6px);\n padding-bottom: px2rem(6px);\n font-weight: bold;\n }\n}\n\n.md-typeset .table-container table:not([class]) {\n box-shadow: none;\n margin: 0;\n}","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max != null {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) != null {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n"]} \ No newline at end of file diff --git a/docs/src/css/extras.css b/docs/src/css/extras.css deleted file mode 100644 index 1dbffb7..0000000 --- a/docs/src/css/extras.css +++ /dev/null @@ -1,7 +0,0 @@ -[data-md-color-scheme="slate"] { - --md-table-color: rgb(20, 20, 20); -} - -thead th { - background-color: var(--md-table-color) -} diff --git a/docs/src/js/compact-tables-qqTQvuZ9.js b/docs/src/js/compact-tables-qqTQvuZ9.js new file mode 100644 index 0000000..45a66db --- /dev/null +++ b/docs/src/js/compact-tables-qqTQvuZ9.js @@ -0,0 +1,2 @@ +!function(){"use strict";var t;t=function(){for(var t=document.querySelectorAll("tbody"),e=0;e {\n const main = () => {\n // Iterate through all tables looking for containers. This\n // works without assigning IDs to everything.\n const tbody_list = document.querySelectorAll(\"tbody\")\n for (let i = 0; i < tbody_list.length; i++) {\n const tbody = tbody_list[i]\n const row_list = tbody.children\n for (let j = 1; j < row_list.length; j++) {\n const cur_row = row_list[j]\n if (cur_row.hasAttribute(\"compact-container\")) {\n const control_row = row_list[j-1]\n cur_row.classList.add(\"table-container\")\n control_row.classList.add(\"table-container-ctrl\")\n control_row.addEventListener(\"click\", (event) => {\n if (control_row.hasAttribute(\"open\")) {\n cur_row.removeAttribute(\"open\")\n control_row.removeAttribute(\"open\")\n } else {\n cur_row.setAttribute(\"open\", \"\")\n control_row.setAttribute(\"open\", \"\")\n }\n })\n }\n }\n }\n }\n\n if (window.document$) {\n // Material specific hook\n window.document$.subscribe(main)\n } else {\n // Normal non-Material specific hook\n document.addEventListener(\"DOMContentLoaded\", main)\n }\n\n})()"],"names":["main","tbody_list","document","querySelectorAll","i","length","row_list","children","_loop","cur_row","j","hasAttribute","control_row","classList","add","addEventListener","event","removeAttribute","setAttribute","window","document$","subscribe"],"mappings":"yBAAA,IACUA,IAAO,WAIT,IADA,IAAMC,EAAaC,SAASC,iBAAiB,SACpCC,EAAI,EAAGA,EAAIH,EAAWI,OAAQD,IAGnC,IAFA,IACME,EADQL,EAAWG,GACFG,SAAQC,EAAAA,WAE3B,IAAMC,EAAUH,EAASI,GACzB,GAAID,EAAQE,aAAa,qBAAsB,CAC3C,IAAMC,EAAcN,EAASI,EAAE,GAC/BD,EAAQI,UAAUC,IAAI,mBACtBF,EAAYC,UAAUC,IAAI,wBAC1BF,EAAYG,iBAAiB,SAAS,SAACC,GAC/BJ,EAAYD,aAAa,SACzBF,EAAQQ,gBAAgB,QACxBL,EAAYK,gBAAgB,UAE5BR,EAAQS,aAAa,OAAQ,IAC7BN,EAAYM,aAAa,OAAQ,IAEzC,GACJ,GAfKR,EAAI,EAAGA,EAAIJ,EAASD,OAAQK,IAAGF,KAoB5CW,OAAOC,UAEPD,OAAOC,UAAUC,UAAUrB,GAG3BE,SAASa,iBAAiB,mBAAoBf"} \ No newline at end of file diff --git a/docs/user_changes.md b/docs/user_changes.md index 058fe9d..112f428 100644 --- a/docs/user_changes.md +++ b/docs/user_changes.md @@ -1,6 +1,6 @@ ## This file tracks configuration changes and deprecations. Additionally -changest to Moonraker that require user intervention will be tracked +changes to Moonraker that require user intervention will be tracked here. ### December 24th 2023 @@ -36,7 +36,7 @@ here. - Configuration options for `[spoolman]` have been added - Configuration options for `[sensor]` have been added -### Februrary 8th 2023 +### February 8th 2023 - The `provider` option in the `[machine]` section no longer accepts `supervisord` as an option. It has been renamed to `supervisord_cli`. @@ -72,7 +72,7 @@ here. ### July 27th 2022 - The behavior of `[include]` directives has changed. Included files are now parsed as they are encountered. If sections are duplicated - options in the last section parsed take precendence. If you are + options in the last section parsed take precedence. If you are using include directives to override configuration in `moonraker.conf` the directives should be moved to the bottom of the file. - Configuration files now support inline comments. @@ -83,7 +83,7 @@ here. warning will be generated. It is crucially important to move configuration to the correct section as in the future it will be a hard requirement. -### Feburary 22nd 2022 +### February 22nd 2022 - The `on_when_upload_queued` option for [power] devices has been deprecated in favor of `on_when_job_queued`. As the new option name implies, this option will power on the device when any new @@ -141,7 +141,7 @@ here. to retrieve the API Key. ### March 10th 2021 -- The `cors_domain` option in the `[authoriztion]` section is now +- The `cors_domain` option in the `[authorization]` section is now checked for dangerous entries. If a domain entry contains a wildcard in the top level domain (ie: `http://www.*`) then it will be rejected, as malicious website can easily reproduce @@ -192,10 +192,10 @@ here. ~/moonraker/scripts/install-moonraker.sh -r - The power plugin configuration has changed. See the - [install guide](installation.md#power-control-plugin) for + [the configuration documentation](./configuration.md#power) for details on the new configuration. - Users transitioning from the previous version of the power plugin will need - to unexport any curently used pins. For example, the following command + to unexport any currently used pins. For example, the following command may be used to unexport pin 19: echo 19 > /sys/class/gpio/unexport diff --git a/docs/web_api.md b/docs/web_api.md deleted file mode 100644 index fff5b98..0000000 --- a/docs/web_api.md +++ /dev/null @@ -1,8703 +0,0 @@ -# - -Most API methods are supported over the Websocket, HTTP, and MQTT -(if configured) transports. File Transfer requests (upload and download) -exclusive to HTTP. The Websocket is required to receive server generated -events such as gcode responses. For information on how to set up the -Websocket, please see the Appendix at the end of this document. - -### HTTP API Overview - -Moonraker's HTTP API could best be described as "RESTish". Attempts are -made to conform to REST standards, however the dynamic nature of -Moonraker's API registration along with the desire to keep consistency -between multiple API protocols results in an HTTP API that does not -completely adhere to the standard. - -Moonraker is capable of parsing request arguments from the both the body -(either JSON or form-data depending on the `Content-Type` header) and from -the query string. All arguments are grouped together in one data structure, -with body arguments taking precedence over query arguments. Thus -if the same argument is supplied both in the body and in the -query string the body argument would be used. It is left up to the client -developer to decide exactly how they want to provide arguments, however -future API documentation will make recommendations. As of March 1st 2021 -this document exclusively illustrates arguments via the query string. - -All successful HTTP requests will return a json encoded object in the form of: - -``` -{result: } -``` - -Response data is generally an object itself, however for some requests this -may simply be an "ok" string. - -Should a request result in an error, a standard error code along with -an error specific message is returned. - -#### Query string type hints - -By default all arguments passed via the query string are represented as -strings. Most endpoint handlers know the data type for each of their -arguments, thus they can perform conversion from a string type if necessary. -However some endpoints accept arguments of a "generic" type, thus the -client is responsible for specifying the type if "string" is not desirable. -This is not a problem for websocket requests as the JSON parser can extract -the appropriate type. HTTP requests must provide "type hints" in these -scenarios. Moonraker supplies support for the following query string type hints: -- int -- bool -- float -- json -The `json` type hint can be specified to pass an array or an object via -the query string. Remember to percent encode the json string so that -the query string is correctly parsed. - -Type hints may be specified by post-fixing them to a key, with a ":" -separating the key and the hint. For example, lets assume that we -have a request that takes `seconds` (integer) and `enabled` (boolean) -arguments. The query string with type hints might look like: -``` -?seconds:int=120&enabled:bool=true -``` -A query string that takes a `value` argument with which we want to -pass an object, `{foo: 21.5, bar: "hello"}` might look like: -``` -?value:json=%7B%22foo%22%3A21.5%2C%22bar%22%3A%22hello%22%7D -``` -As you can see, a percent encoded json string is not human readable, -thus using this functionality should be seen as a "last resort." If at -all possible clients should attempt to put these arguments in the body -of a request. - -### JSON-RPC API Overview - -The Websocket and MQTT transports use the [JSON-RPC 2.0](https://jsonrpc.org) -protocol. The Websocket transmits JSON-RPC objects in a text frame, whereas MQTT -transmits them in the payload of a topic. When MQTT is configured Moonraker -subscribes to an api request topic. After an api request is processed Moonraker -publishes the return value to a response topic. By default these topics are -`{instance_name}/moonraker/api/request` and -`{instance_name}/moonraker/api/response`. The `{instance_name}` should be a -unique identifier for each instance of Moonraker and defaults to the machine's -host name. - -In addition, most JSON-RPC methods are available via the -[JSONRPC HTTP request](#json-rpc-over-http). - -An encoded request should look something like: -```json -{ - "jsonrpc": "2.0", - "method": "API method", - "params": {"arg_one": 1, "arg_two": true}, - "id": 354 -} -``` - -The `params` field may be left out if the API request takes no arguments. -The `id` should be a unique value that has no chance of colliding -with other JSON-RPC requests. The `method` is the API method, as defined -for each API in this document. - -!!! tip - MQTT requests may provide an optional `mqtt_timestamp` keyword - argument in the `params` field of the JSON-RPC request. To avoid - potential collisions from time drift it is recommended to specify - the timestamp in microseconds since the Unix Epoch. If provided - Moonraker will use the timestamp to discard duplicate requests. - It is recommended to either provide a timestamp or publish API - requests at a QoS level of 0 or 2. - -A successful request will return a response like the following: -```json -{ - "jsonrpc": "2.0", - "result": {"res_data": "success"}, - "id": 354 -} -``` -The `result` will generally contain an object, but as with the HTTP API in some -cases it may simply return a string. The `id` field will return an id that -matches the one provided by the request. - -Requests that result in an error will receive a properly formatted -JSON-RPC response: -```json -{ - "jsonrpc": "2.0", - "error": {"code": 36000, "message": "Error Message"}, - "id": 354 -} -``` -Some errors may not return a request ID, such as an improperly formatted request. - -The [moontest](https://www.github.com/arksine/moontest) repo includes a basic -test interface with example usage for most of the requests below. It also -includes a basic JSON-RPC implementation that uses promises to return responses -and errors (see json-rpc.js). - -### Websocket Connections - -#### Primary websocket - -The primary websocket supports Moonraker's JSON-RPC API. Most applications that -desire a websocket connection will make use of the primary websocket. - -The primary websocket is available at: -``` - ws://host_or_ip:port/websocket` -``` - -The primary websocket will remain connected until the application disconnects -or Moonraker is shutdown. - -#### Bridge websocket - -The "bridge" websocket provides a near direct passthrough to Klipper's API -Server. Klipper uses its own RPC protocol, which is effectively a simplified -version of the JSON-RPC specification. Developers should refer to -[Klipper's API documentation](https://www.klipper3d.org/API_Server.html) -for details on the protocol and available APIs. - -!!! Note - The bridge websocket is described as "near direct passthrough" because - Moonraker handles the ETX (`0x03`) terminator internally. Applications - can expect to receive complete JSON encoded messages in a text frame - without the ETX terminator. Likewise applications should send JSON encoded - messages without the ETX terminator. Messages may be sent using either - text frames or binary frames. - -The bridge websocket provides access to diagnostic APIs that are not generally -suitable for Moonraker's primary connection. These requests stream a -substantial amount of data; bridge connections allow Moonraker to avoid -decoding and re-encoding this data, reducing CPU load on the host. The "dump" -requests, such as `motion_report/dump_stepper` and `adxl345/dump_adxl345`, are -examples of APIs that should make use of the bridge websocket. - -The bridge websocket is available at: -``` -ws://host_or_ip:port/klippysocket -``` - -The availability of bridge connections depends on Klippy's availablility. -If Klippy is not running or its API server is not enabled then a bridge -websocket connection cannot be established. Established bridge connections -will close when Klippy is shutdown or restarted. Such connections will also -be closed if Moonraker is restarted or shutdown. - -!!! Note - If JWT or API Key authentication is required the application must use a - [oneshot token](#generate-a-oneshot-token) when connecting to a bridge - socket. Since Moonraker does not decode bridge requests it is not possible - to authenticate post connection. - -### Unix Socket Connection - -All JSON-RPC APIs available over the Websocket transport are also available -over the Unix Domain Socket connection. Moonraker creates the socket file at -`/comms/moonraker.sock` (ie: `~/printer_data/comms/moonraker.sock`). -The Unix Socket expects UTF-8 encoded JSON-RPC byte strings. Each JSON-RPC -request must be terminated with an ETX character (`0x03`). - -The Unix Socket is desirable for front ends and extensions running on the -local machine as authentication is not necessary. There should be a small -performance improvement due to the simplified transport protocol, however -the impact of this is likely negligible. - -The `moontest` repo contains a -[python script](https://github.com/Arksine/moontest/blob/master/scripts/unix_socket_test.py) -to test comms over the unix socket. - -### Jinja2 Template API Calls - -Some template options in Moonraker's configuration, such as those in the -[button](configuration.md#button) component, may call Moonraker APIs through -the `call_method(method_name, kwargs)` context function. The `call_method` -function takes the API's JSON-RPC method name as its first parameter, followed -by a set of keyword arguments as per the method's requirements. - -```ini -# moonraker.conf - -# Query Printer Objects example -[button check_status] -pin: gpio26 -on_press: - {% set query_objs = {"toolhead": ["position"], "print_stats": None} %} - # JSON-RPC method is "printer.objects.query", which takes a single "objects" - # argument - {% set status = call_method("printer.objects.query", objects=query_objs) %} - # do something with the value returned from the object query, perhaps - # send a websocket notification or publish a mqtt topic - -# Publish button event to MQTT Topic -[button check_status] -pin: gpio26 -on_release: - # JSON-RPC method is "server.mqtt.publish" - {% do call_method("server.mqtt.publish", - topic="moonraker/mybutton", - payload="Button Released") %} -``` - -### Server Administration - -#### Query Server Info -HTTP request: -```http -GET /server/info -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.info", - "id": 9546 -} -``` -Returns: - -An object containing various fields that report server state. - -```json -{ - "klippy_connected": true, - "klippy_state": "ready", - "components": [ - "database", - "file_manager", - "klippy_apis", - "machine", - "data_store", - "shell_command", - "proc_stats", - "history", - "octoprint_compat", - "update_manager", - "power" - ], - "failed_components": [], - "registered_directories": ["config", "gcodes", "config_examples", "docs"], - "warnings": [ - "Invalid config option 'api_key_path' detected in section [authorization]. Remove the option to resolve this issue. In the future this will result in a startup error.", - "Unparsed config section [fake_section] detected. This may be the result of a component that failed to load. In the future this will result in a startup error." - ], - "websocket_count": 2, - "moonraker_version": "v0.7.1-105-ge4f103c", - "api_version": [1, 0, 0], - "api_version_string": "1.0.0" -} -``` -!!! warning - This object also includes `plugins` and `failed_plugins` fields that - are now deprecated. They duplicate the information in - `components` and `failed_components`, and will be removed in the future. - -Note that `klippy_state` will match the `state` value received from -`/printer/info`. The `klippy_connected` item tracks the state of the -unix domain socket connect to Klippy. The `components` key will return a list -of enabled components. This can be used by clients to check if an optional -component is available. Optional components that do not load correctly will -not prevent the server from starting, thus any components that failed to load -will be reported in the `failed_components` field. - -The `websocket_count` field reports the total number of connected websockets. - -#### Get Server Configuration -HTTP request: -```http -GET /server/config -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.config", - "id": 5616 -} -``` -Returns: - -An object containing the server's full configuration. Note that -this includes auxiliary configuration sections not part of `moonraker.conf`, -for example the `update_manager static debian moonraker` section. -Options not specified in `moonraker.conf` with default values are also -included. - -```json -{ - "config": { - "server": { - "host": "0.0.0.0", - "port": 7125, - "ssl_port": 7130, - "enable_debug_logging": true, - "enable_asyncio_debug": false, - "klippy_uds_address": "/tmp/klippy_uds", - "max_upload_size": 210, - "ssl_certificate_path": null, - "ssl_key_path": null - }, - "dbus_manager": {}, - "database": { - "database_path": "~/.moonraker_database", - "enable_database_debug": false - }, - "file_manager": { - "enable_object_processing": true, - "queue_gcode_uploads": true, - "config_path": "~/printer_config", - "log_path": "~/logs" - }, - "klippy_apis": {}, - "machine": { - "provider": "systemd_dbus" - }, - "shell_command": {}, - "data_store": { - "temperature_store_size": 1200, - "gcode_store_size": 1000 - }, - "proc_stats": {}, - "job_state": {}, - "job_queue": { - "load_on_startup": true, - "automatic_transition": false, - "job_transition_delay": 2, - "job_transition_gcode": "\nM118 Transitioning to next job..." - }, - "http_client": {}, - "announcements": { - "dev_mode": false, - "subscriptions": [] - }, - "authorization": { - "login_timeout": 90, - "force_logins": false, - "cors_domains": [ - "*.home", - "http://my.mainsail.xyz", - "http://app.fluidd.xyz", - "*://localhost:*" - ], - "trusted_clients": [ - "192.168.1.0/24" - ] - }, - "zeroconf": {}, - "octoprint_compat": { - "enable_ufp": true, - "flip_h": false, - "flip_v": false, - "rotate_90": false, - "stream_url": "/webcam/?action=stream", - "webcam_enabled": true - }, - "history": {}, - "secrets": { - "secrets_path": "~/moonraker_secrets.ini" - }, - "mqtt": { - "address": "eric-work.home", - "port": 1883, - "username": "{secrets.mqtt_credentials.username}", - "password_file": null, - "password": "{secrets.mqtt_credentials.password}", - "mqtt_protocol": "v3.1.1", - "instance_name": "pi-debugger", - "default_qos": 0, - "status_objects": { - "webhooks": null, - "toolhead": "position,print_time", - "idle_timeout": "state", - "gcode_macro M118": null - }, - "api_qos": 0, - "enable_moonraker_api": true - }, - "template": {} - }, - "orig": { - "DEFAULT": {}, - "server": { - "enable_debug_logging": "True", - "max_upload_size": "210" - }, - "file_manager": { - "config_path": "~/printer_config", - "log_path": "~/logs", - "queue_gcode_uploads": "True", - "enable_object_processing": "True" - }, - "machine": { - "provider": "systemd_dbus" - }, - "announcements": {}, - "job_queue": { - "job_transition_delay": "2.", - "job_transition_gcode": "\nM118 Transitioning to next job...", - "load_on_startup": "True" - }, - "authorization": { - "trusted_clients": "\n192.168.1.0/24", - "cors_domains": "\n*.home\nhttp://my.mainsail.xyz\nhttp://app.fluidd.xyz\n*://localhost:*" - }, - "zeroconf": {}, - "octoprint_compat": {}, - "history": {}, - "secrets": { - "secrets_path": "~/moonraker_secrets.ini" - }, - "mqtt": { - "address": "eric-work.home", - "port": "1883", - "username": "{secrets.mqtt_credentials.username}", - "password": "{secrets.mqtt_credentials.password}", - "enable_moonraker_api": "True", - "status_objects": "\nwebhooks\ntoolhead=position,print_time\nidle_timeout=state\ngcode_macro M118" - } - }, - "files": [ - { - "filename": "moonraker.conf", - "sections": [ - "server", - "file_manager", - "machine", - "announcements", - "job_queue", - "authorization", - "zeroconf", - "octoprint_compat", - "history", - "secrets" - ] - }, - { - "filename": "include/extras.conf", - "sections": [ - "mqtt" - ] - } - ] -} -``` - -#### Request Cached Temperature Data -HTTP request: -```http -GET /server/temperature_store?include_monitors=false -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.temperature_store", - "params": { - "include_monitors": false - }, - "id": 2313 -} -``` - -Parameters: - -- `include_monitors`: _Optional, defaults to `false`._ When set to `true` - the response will include sensors reported as `temperature monitors` by - Klipper. A temperature monitor may report `null` values in the `temperatures` - field, applications should be sure that they are modified to handle this - condition before setting `inlcude_monitors` to `true`. - -Returns: - -An object where the keys are the available temperature sensor names, and with -the value being an array of stored temperatures. The array is updated every -1 second by default, containing a total of 1200 values (20 minutes). The -array is organized from oldest temperature to most recent (left to right). -Note that when the host starts each array is initialized to 0s. -```json -{ - "extruder": { - "temperatures": [21.05, 21.12, 21.1, 21.1, 21.1], - "targets": [0, 0, 0, 0, 0], - "powers": [0, 0, 0, 0, 0] - }, - "temperature_fan my_fan": { - "temperatures": [21.05, 21.12, 21.1, 21.1, 21.1], - "targets": [0, 0, 0, 0, 0], - "speeds": [0, 0, 0, 0, 0] - }, - "temperature_sensor my_sensor": { - "temperatures": [21.05, 21.12, 21.1, 21.1, 21.1] - } -} -``` - -#### Request Cached GCode Responses -HTTP request: -```http -GET /server/gcode_store?count=100 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.gcode_store", - "params": { - "count": 100 - }, - "id": 7643 -} -``` - -The `count` argument is optional, limiting number of returned items -in the response to the value specified. If omitted, the entire gcode -store will be returned (up to 1000 responses). - -Returns: - -An object with the field `gcode_store` that contains an array -of objects. Each object will contain `message`, `time`, and -`type` fields. The `time` field is reported in Unix Time. -The `type` field will either be `command` or `response`. -```json -{ - "gcode_store": [ - { - "message": "FIRMWARE_RESTART", - "time": 1615832299.1167388, - "type": "command" - }, - { - "message": "// Klipper state: Ready", - "time": 1615832309.9977088, - "type": "response" - }, - { - "message": "M117 This is a test", - "time": 1615834094.8662775, - "type": "command" - }, - { - "message": "G4 P1000", - "time": 1615834098.761729, - "type": "command" - }, - { - "message": "STATUS", - "time": 1615834104.2860553, - "type": "command" - }, - { - "message": "// Klipper state: Ready", - "time": 1615834104.3299904, - "type": "response" - } - ] -} -``` - -#### Rollover Logs - -Requests a manual rollover for log files registered with Moonraker's -log management facility. Currently these are limited to `moonraker.log` -and `klippy.log`. - -HTTP request: -```http -POST /server/logs/rollover -Content-Type: application/json - -{ - "application": "moonraker" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.logs.rollover", - "params": { - "application": "moonraker" - }, - "id": 4656 -} -``` - -Parameters: - -- `application` - (Optional) The name of the application to rollover. - Currently can be `moonraker` or `klipper`. The default is to rollover - all logs. - -!!! Note - Moonraker must be able to manage Klipper's systemd service to - perform a manual rollover. The rollover will fail under the following - conditions: - - - Moonraker cannot detect Klipper's systemd unit - - Moonraker cannot detect the location of Klipper's files - - A print is in progress - -Returns: An object in the following format: - -```json -{ - "rolled_over": [ - "moonraker", - "klipper" - ], - "failed": {} -} -``` - -- `rolled_over` - An array of application names successfully rolled over. -- `failed` - An object containing information about failed applications. The - key will match an application name and its value will be an error message. - -#### Restart Server -HTTP request: -```http -POST /server/restart -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.restart", - "id": 4656 -} -``` -Returns: - -`ok` upon receipt of the restart request. After the request -is returns, the server will restart. Any existing connection -will be disconnected. A restart will result in the creation -of a new server instance where the configuration is reloaded. - -#### Identify Connection -This method provides a way for persistent clients to identify -themselves to Moonraker. This information may be used by Moonraker -perform an action or present information based on if a specific -client is connected. Currently this method is only available -to websocket and unix socket connections. Once this endpoint returns -success it cannot be called again, repeated calls will result in an error. - -HTTP request: `Not Available` - -JSON-RPC request (Websocket/Unix Socket Only): -```json -{ - "jsonrpc": "2.0", - "method": "server.connection.identify", - "params": { - "client_name": "moontest", - "version": "0.0.1", - "type": "web", - "url": "http://github.com/arksine/moontest", - "access_token": "", - "api_key": "" - }, - "id": 4656 -} -``` - -Parameters: - -- `client_name`: (required) The name of your client, such as `Mainsail`, - `Fluidd`, `KlipperScreen`, `MoonCord`, etc. -- `version`: (required) The current version of the connected client -- `type`: (required) Application type. May be one of `web`, `mobile`, - `desktop`, `display`, `bot`, `agent` or `other`. These should be self - explanatory, use `other` if your client does not fit any of the prescribed - options. -- `url`: (required) The url for your client's homepage -- `access_token`: (optional) A JSON Web Token that may be used to assign a - logged in user to the connection. See the [authorization](#authorization) - section for APIs used to create and refresh the access token. -- `api_key`:. (optional) The system API Key. This key may be used to grant - access to clients that do not wish to implement user authentication. Note - that if the `access_token` is also supplied then this parameter will be - ignored. - -!!! Note - When identifying as an `agent`, only one instance should be connected - to Moonraker at a time. If multiple agents of the same `client_name` - attempt to identify themselves this endpoint will return an error. - See the [extension APIs](#extension-apis) for more information about - `agents`. - -Returns: - -The connection's unique identifier. -```json -{ - "connection_id": 1730367696 -} -``` - -#### Get Websocket ID - -!!! Warning - This method is deprecated. Please use the - [identify endpoint](#identify-connection) to retrieve the - Websocket's UID - -HTTP request: `Not Available` - -JSON-RPC request (Websocket/Unix Socket Only): -```json -{ - "jsonrpc": "2.0", - "method": "server.websocket.id", - "id": 4656 -} -``` -Returns: - -The connected websocket's unique identifier. -```json -{ - "websocket_id": 1730367696 -} -``` - -#### JSON-RPC over HTTP - -Exposes the JSON-RPC interface over HTTP. All JSON-RPC methods with -corresponding HTTP APIs are available. Methods exclusive to other -transports, such as [Identify Connection](#identify-connection), are -not available. - -HTTP request: -```http -POST /server/jsonrpc -Content-Type: application/json -{ - "jsonrpc": "2.0", - "method": "printer.info", - "id": 5153 -} -``` -!!! Note - If authentication is required it must be part of the HTTP request, - either using the API Key Header (`X-Api-Key`) or JWT Bearer Token. - -Returns: - -The full JSON-RPC response. - -```json -{ - "jsonrpc": "2.0", - "id": 5153, - "result": { - "state": "ready", - "state_message": "Printer is ready", - "hostname": "my-pi-hostname", - "software_version": "v0.9.1-302-g900c7396", - "cpu_info": "4 core ARMv7 Processor rev 4 (v7l)", - "klipper_path": "/home/pi/klipper", - "python_path": "/home/pi/klippy-env/bin/python", - "log_file": "/tmp/klippy.log", - "config_file": "/home/pi/printer.cfg" - } -} -``` - -!!! Note - This request will never return an HTTP error. When an error is - encountered a JSON-RPC error response will be returned. - -### Printer Administration - -#### Get Klippy host information - -HTTP Request: -```http -GET /printer/info -``` -JSON-RPC Request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.info", - "id": 5445 -} -``` -Returns: - -An object containing the build version, cpu info, Klippy's current state. - -```json -{ - "state": "ready", - "state_message": "Printer is ready", - "hostname": "my-pi-hostname", - "software_version": "v0.9.1-302-g900c7396", - "cpu_info": "4 core ARMv7 Processor rev 4 (v7l)", - "klipper_path": "/home/pi/klipper", - "python_path": "/home/pi/klippy-env/bin/python", - "log_file": "/tmp/klippy.log", - "config_file": "/home/pi/printer.cfg", -} -``` - -#### Emergency Stop -HTTP request: -```http -POST /printer/emergency_stop -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.emergency_stop", - "id": 4564 -} -``` - -!!! note - This endpoint will immediately halt the printer and put it in a "shutdown" - state. It should be used to implement an "emergency stop" button and - also used if a user enters `M112`(emergency stop) via a console. - -Returns: - -`ok` - -#### Host Restart -HTTP request: -```http -POST /printer/restart -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.restart", - "id": 4894 -} -``` -Returns: - -`ok` - -#### Firmware Restart -HTTP request: -```http -POST /printer/firmware_restart -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.firmware_restart", - "id": 8463 -} -``` -Returns: - -`ok` - -### Printer Status - -#### List available printer objects -HTTP request: -```http -GET /printer/objects/list -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.objects.list", - "id": 1454 -} -``` - -Returns: - -An array of "printer objects" that are currently available for query -or subscription. This list will be passed in an `objects` parameter. - -```json -{ - "objects": ["gcode", "toolhead", "bed_mesh", "configfile",...] -} -``` - -#### Query printer object status -HTTP request: -```http -GET /printer/objects/query?gcode_move&toolhead&extruder=target,temperature -``` -The above will request a status update for all `gcode_move` and `toolhead` -attributes. Only the `temperature` and `target` attributes are requested -for the `extruder`. - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.objects.query", - "params": { - "objects": { - "gcode_move": null, - "toolhead": ["position", "status"] - } - }, - "id": 4654 -} -``` -!!! note - A `null` value will fetch all available attributes for its key. - -Returns: - -An object where the top level items are "eventtime" and "status". The -"status" item contains data about the requested update. - -```json -{ - "eventtime": 578243.57824499, - "status": { - "gcode_move": { - "absolute_coordinates": true, - "absolute_extrude": true, - "extrude_factor": 1, - "gcode_position": [0, 0, 0, 0], - "homing_origin": [0, 0, 0, 0], - "position": [0, 0, 0, 0], - "speed": 1500, - "speed_factor": 1 - }, - "toolhead": { - "position": [0, 0, 0, 0], - "status": "Ready" - } - } -} -``` -See [Klipper's status reference](https://www.klipper3d.org/Status_Reference.html) for -details on the printer objects available for query. - -#### Subscribe to printer object status -HTTP request: -```http -POST /printer/objects/subscribe?connection_id=123456789&gcode_move&extruder` -``` -!!! note - The HTTP API requires that a `connection_id` is passed via the query - string or as part of the form. This should be the - [ID reported](#get-websocket-id) from a currently connected websocket. A - request that includes only the `connection_id` argument will cancel the - subscription on the specified websocket. - - This request is not available over MQTT as it can not be set per client. - Instead MQTT can publish printer status by setting the `status_objects` - option in the `[mqtt]` section. - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.objects.subscribe", - "params": { - "objects": { - "gcode_move": null, - "toolhead": ["position", "status"] - } - }, - "id": 5434 -} -``` -!!! note - If `objects` is set to an empty object then the subscription will - be cancelled. - -Returns: - -Status data for objects in the request, with the format matching that of -the `/printer/objects/query`: - -```json -{ - "eventtime": 578243.57824499, - "status": { - "gcode_move": { - "absolute_coordinates": true, - "absolute_extrude": true, - "extrude_factor": 1, - "gcode_position": [0, 0, 0, 0], - "homing_origin": [0, 0, 0, 0], - "position": [0, 0, 0, 0], - "speed": 1500, - "speed_factor": 1, - }, - "toolhead": { - "position": [0, 0, 0, 0], - "status": "Ready" - } - } -} -``` - -See [Klipper's status reference](https://www.klipper3d.org/Status_Reference.html) for -details on the printer objects available for subscription. - -Status updates for subscribed objects are sent asynchronously over the -websocket. See the [notify_status_update](#subscriptions) -notification for details. - -#### Query Endstops -HTTP request: -```http -GET /printer/query_endstops/status -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.query_endstops.status", - "id": 3456 -} -``` -Returns: - -An object containing the current endstop state, where each field is an -endstop identifier, with a string value of "open" or "TRIGGERED". -```json -{ - "x": "TRIGGERED", - "y": "open", - "z": "open" -} -``` - -### GCode APIs - -#### Run a gcode: -HTTP request: -```http -POST /printer/gcode/script?script=G28 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.gcode.script", - "params": { - "script": "G28" - }, - "id": 7466} -``` - -!!! warning - When `M112`(emergency stop) is requested via this endpoint it will not - immediately stop the printer. `M112` will be placed on the gcode queue and - executed after all previous gcodes are complete. If a client detects - `M112` via user input (such as a console) it should request the - `/printer/emergency_stop` endpoint to immediately halt the printer. This - may be done in addition to sending the `M112` gcode if desired. - -Returns: - -`ok` when the gcode has completed execution. -#### Get GCode Help -HTTP request: -```http -GET /printer/gcode/help -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.gcode.help", - "id": 4645 -} -``` - -Returns: - -An object where they keys are gcode handlers and values are the associated -help strings. Note that help strings are not available for default gcode -handlers such as G1, G28, etc, nor are they available for extended handlers -that failed to register a description in Klippy. -```json -{ - "RESTORE_GCODE_STATE": "Restore a previously saved G-Code state", - "PID_CALIBRATE": "Run PID calibration test", - "QUERY_ADC": "Report the last value of an analog pin", - "TUNING_TOWER": "Tool to adjust a parameter at each Z height", - "SAVE_CONFIG": "Overwrite config file and restart", - "SET_DISPLAY_GROUP": "Set the active display group", - "SAVE_GCODE_STATE": "Save G-Code coordinate state", - "SET_PRESSURE_ADVANCE": "Set pressure advance parameters", - "SET_GCODE_OFFSET": "Set a virtual offset to g-code positions", - "BED_TILT_CALIBRATE": "Bed tilt calibration script", - ... -} -``` - -### Print Management - -#### Print a file -HTTP request: -```http -POST /printer/print/start?filename=test_print.gcode -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.print.start", - "params": { - "filename": "test_pring.gcode" - }, - "id": 4654 -} -``` -Returns: - -`ok` - -#### Pause a print -HTTP request: -```http -POST /printer/print/pause -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.print.pause", - "id": 4564 -} -``` -Returns: - -`ok` - -#### Resume a print -HTTP request: -```http -POST /printer/print/resume -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.print.resume", - "id": 1465 -} -``` -Returns: - -`ok` - -#### Cancel a print -HTTP request: -```http -POST /printer/print/cancel -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "printer.print.cancel", - "id": 2578 -} -``` -Returns: - -`ok` - -### Machine Requests - -#### Get System Info -HTTP request: -```http -GET /machine/system_info -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.system_info", - "id": 4665 -} -``` -Returns: Information about the host system in the following format: -```json -{ - "system_info": { - "cpu_info": { - "cpu_count": 4, - "bits": "32bit", - "processor": "armv7l", - "cpu_desc": "ARMv7 Processor rev 4 (v7l)", - "serial_number": "b898bdb4", - "hardware_desc": "BCM2835", - "model": "Raspberry Pi 3 Model B Rev 1.2", - "total_memory": 945364, - "memory_units": "kB" - }, - "sd_info": { - "manufacturer_id": "03", - "manufacturer": "Sandisk", - "oem_id": "5344", - "product_name": "SU32G", - "product_revision": "8.0", - "serial_number": "46ba46", - "manufacturer_date": "4/2018", - "capacity": "29.7 GiB", - "total_bytes": 31914983424 - }, - "distribution": { - "name": "Raspbian GNU/Linux 10 (buster)", - "id": "raspbian", - "version": "10", - "version_parts": { - "major": "10", - "minor": "", - "build_number": "" - }, - "like": "debian", - "codename": "buster" - }, - "available_services": [ - "klipper", - "klipper_mcu", - "moonraker" - ], - "instance_ids": { - "moonraker": "moonraker", - "klipper": "klipper" - }, - "service_state": { - "klipper": { - "active_state": "active", - "sub_state": "running" - }, - "klipper_mcu": { - "active_state": "active", - "sub_state": "running" - }, - "moonraker": { - "active_state": "active", - "sub_state": "running" - } - }, - "virtualization": { - "virt_type": "none", - "virt_identifier": "none" - }, - "python": { - "version": [ - 3, - 7, - 3, - "final", - 0 - ], - "version_string": "3.7.3 (default, Jan 22 2021, 20:04:44) [GCC 8.3.0]" - }, - "network": { - "wlan0": { - "mac_address": "", - "ip_addresses": [ - { - "family": "ipv4", - "address": "192.168.1.127", - "is_link_local": false - }, - { - "family": "ipv6", - "address": "", - "is_link_local": false - }, - { - "family": "ipv6", - "address": "fe80::", - "is_link_local": true - } - ] - } - }, - "canbus": { - "can0": { - "tx_queue_len": 128, - "bitrate": 500000, - "driver": "mcp251x" - }, - "can1": { - "tx_queue_len": 128, - "bitrate": 500000, - "driver": "gs_usb" - } - } - } -} -``` - -!!! note - If no SD Card is detected the `sd_info` field will contain an empty object. - -#### Shutdown the Operating System -HTTP request: -```http -POST /machine/shutdown -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.shutdown", - "id": 4665 -} -``` -Returns: - -This request will not return. The machine will shutdown -and the socket connection will drop. - -#### Reboot the Operating System -HTTP request: -```http -POST /machine/reboot -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.reboot", - "id": 4665 -} -``` -Returns: - -This request will not return. The machine will reboot -and the socket connection will drop. - -#### Restart a system service -Uses: `sudo systemctl restart {name}` - -Services allowed: - -* `crowsnest` -* `MoonCord` -* `moonraker` -* `moonraker-telegram-bot` -* `klipper` -* `KlipperScreen` -* `sonar` -* `webcamd` - -HTTP request: -```http -POST /machine/services/restart?service={name} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.services.restart", - "params": { - "service": "{name}" - }, - "id": 4656} -``` - -Returns: - -`ok` when complete. -!!! note - If `moonraker` is chosen, the return - value will be sent prior to the service restart. - -#### Stop a system service -Stops a system service via `sudo systemctl stop `. Currently -only `webcamd` and `klipper` are supported. - -HTTP request: -```http -POST /machine/services/stop?service={name} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.services.stop", - "params": { - "service": "{name}" - }, - "id": 4645 -} -``` - -Returns: - -`ok` - -#### Start a system service -Starts a system service via `sudo systemctl start `. Currently -only `webcamd` and `klipper` are supported. - -HTTP request: -```http -POST /machine/services/start?service={name} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.services.start", - "params": { - "service": "{name}" - }, - "id": 4645 -} -``` - -Returns: - -`ok` - -#### Get Moonraker Process Stats -Returns system usage information about the moonraker process. - -HTTP request: -```http -GET /machine/proc_stats -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.proc_stats", - "id": 7896 -} -``` -Returns: - -An object in the following format: -```json -{ - "moonraker_stats": [ - { - "time": 1626612666.850755, - "cpu_usage": 2.66, - "memory": 24732, - "mem_units": "kB" - }, - { - "time": 1626612667.8521338, - "cpu_usage": 2.62, - "memory": 24732, - "mem_units": "kB" - } - ], - "throttled_state": { - "bits": 0, - "flags": [] - }, - "cpu_temp": 45.622, - "network": { - "lo": { - "rx_bytes": 113516429, - "tx_bytes": 113516429, - "bandwidth": 3342.68 - }, - "wlan0": { - "rx_bytes": 48471767, - "tx_bytes": 113430843, - "bandwidth": 4455.91 - } - }, - "system_cpu_usage": { - "cpu": 2.53, - "cpu0": 3.03, - "cpu1": 5.1, - "cpu2": 1.02, - "cpu3": 1 - }, - "system_uptime": 2876970.38089603, - "websocket_connections": 4 -} -``` -Process information is sampled every second. The `moonraker_stats` field -will return up to 30 samples, each sample with the following fields: - -- `time`: Time of the sample (in seconds since the Epoch) -- `cpu_usage`: A floating point value between 0-100, representing the -CPU usage of the Moonraker process. -- `memory`: Integer value representing the current amount of memory -allocated in RAM (resident set size). -- `mem_units`: A string identifying the units of the value in the -`memory` field. This is typically "kB", but not guaranteed. - -If the system running Moonraker supports `vcgencmd` then Moonraker -will check the current throttled flags via `vcgencmd get_throttled` -and report them in the `throttled_state` field: - -- `bits`: An integer value that represents the bits reported by -`vcgencmd get_throttled` -- `flags`: Descriptive flags parsed out of the bits. One or more -of the following flags may be reported: -- "Under-Voltage Detected" -- "Frequency Capped" -- "Currently Throttled" -- "Temperature Limit Active" -- "Previously Under-Volted" -- "Previously Frequency Capped" -- "Previously Throttled" -- "Previously Temperature Limited" - -The first four flags indicate an active throttling condition, -whereas the last four indicate a previous condition (may or -may not still be active). If `vcgencmd` is not available -`throttled_state` will report `null`. - -If the system reports CPU temp at `/sys/class/thermal/thermal_zone0` -then temperature will be supplied in the `cpu_temp` field. Otherwise -the field will be set to `null`. - -If the system reports network statistics at `/proc/net/dev` then the -`network` field will contain network statistics. All available interfaces -will be tracked. Each interface reports the following fields: - -- `rx_bytes`: total number of bytes received over the interface -- `tx_bytes`: total number of bytes transferred over the interface -- `bandwidth`: estimated current bandwidth used (both rx and tx) in - bytes/second - -If network information is not available then the `network` field will -contain an empty object. - -If the system reports cpu usage at `/proc/stat` then the `system_cpu_usage` -field will contain an object with cpu usage data. The `cpu` field of this -object reports total cpu usage, while each `cpuX` field is usage per core. - -The `websocket_connections` field reports the number of active websockets -currently connected to moonraker. - -#### Get Sudo Info -Retrieve sudo information status. Optionally checks if Moonraker has -permission to run commands as root. - -HTTP request: -```http -GET /machine/sudo/info?check_access=false -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.sudo.info", - "params": { - "check_access": false - }, - "id": 7896 -} -``` - -Parameters: - -- `check_access`: A boolean value, when set to `true` Moonraker will - attempt to run a command with elevated permissions. The result will - be returned in the `sudo_access` field of the response. Defaults to - `false`. - -Returns: - -An object in the following format: -```json -{ - "sudo_access": null, - "linux_user": "pi", - "sudo_requested": false, - "request_messages": [] -} -``` - -- `sudo_access`: The result of a request to check access. Returns - `true` if Moonraker has sudo permission, `false` if it does not, - and `null` if `check_access` is `false`. -- `linux_user`: The current linux user running Moonraker. -- `sudo_requested`: Returns true if Moonraker is currently requesting - sudo access. -- `request_messages`: An array of strings, each string describing - a pending sudo request. The array will be empty if no sudo - requests are pending. - -#### Set sudo password -Sets/updates the sudo password currently used by Moonraker. When -the password is set using this endpoint the change is not persistent -across restarts. If Moonraker has one or more pending sudo requests -they will be processed. - -HTTP request: -```http -POST /machine/sudo/password -Content-Type: application/json - -{ - "password": "linux_user_password" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.sudo.password", - "params": { - "password": "linux_user_password" - }, - "id": 7896 -} -``` - -Parameters: - -- `password`: The linux user password used to grant elevated - permission. This parameter must be provided. - -Returns: - -An object in the following format: -```json -{ - "sudo_responses": [ - "Sudo password successfully set." - ], - "is_restarting": false -} -``` - -- `sudo_responses`: An array of one or more sudo responses. - If there are pending sudo requests each request will provide - a response. -- `is_restarting`: A boolean value indicating that a sudo request - prompted Moonraker to restart its service. - -This request will return an error if the supplied password is -incorrect or if any pending sudo requests fail. - -#### List USB Devices - -Returns a list of all USB devices currently detected on the system. - -```http title="HTTP Request" -GET /machine/peripherals/usb -``` - -```json title="JSON-RPC Request" -{ - "jsonrpc": "2.0", - "method": "machine.peripherals.usb", - "id": 7896 -} -``` - -/// api-example-response -```json -{ - "usb_devices": [ - { - "device_num": 1, - "bus_num": 1, - "vendor_id": "1d6b", - "product_id": "0002", - "usb_location": "1:1", - "manufacturer": "Linux 6.1.0-rpi7-rpi-v8 dwc_otg_hcd", - "product": "DWC OTG Controller", - "serial": "3f980000.usb", - "class": "Hub", - "subclass": null, - "protocol": "Single TT", - "description": "Linux Foundation 2.0 root hub" - }, - { - "device_num": 3, - "bus_num": 1, - "vendor_id": "046d", - "product_id": "0825", - "usb_location": "1:3", - "manufacturer": "Logitech, Inc.", - "product": "Webcam C270", - "serial": "", - "class": "Miscellaneous Device", - "subclass": null, - "protocol": "Interface Association", - "description": "Logitech, Inc. Webcam C270" - }, - { - "device_num": 2, - "bus_num": 1, - "vendor_id": "1a40", - "product_id": "0101", - "usb_location": "1:2", - "manufacturer": "Terminus Technology Inc.", - "product": "USB 2.0 Hub", - "serial": null, - "class": "Hub", - "subclass": null, - "protocol": "Single TT", - "description": "Terminus Technology Inc. Hub" - }, - { - "device_num": 5, - "bus_num": 1, - "vendor_id": "0403", - "product_id": "6001", - "usb_location": "1:5", - "manufacturer": "FTDI", - "product": "FT232R USB UART", - "serial": "", - "class": null, - "subclass": null, - "protocol": null, - "description": "Future Technology Devices International, Ltd FT232 Serial (UART) IC" - }, - { - "device_num": 4, - "bus_num": 1, - "vendor_id": "1d50", - "product_id": "614e", - "usb_location": "1:4", - "manufacturer": "Klipper", - "product": "stm32f407xx", - "serial": "", - "class": "Communications", - "subclass": null, - "protocol": null, - "description": "OpenMoko, Inc. Klipper 3d-Printer Firmware" - } - ] -} -``` -/// - -/// api-response-schema - open: True -Response - -| Field | Type | Description | -| ------------- | :---: | ------------------------------------------------------ | -| `usb_devices` | array | An array of objects containing USB device information. | - - - USB Device - -| Field | Type | Description | -| -------------- | :-----: | --------------------------------------------------- | -| `bus_num` | int | The USB bus number as reported by the host. | -| `device_num` | int | The USB device number as reported by the host. | -| `usb_location` | string | A combination of the bus number and device number, | -| | | yielding a unique location ID on the host system. |^ -| `vendor_id` | string | The vendor ID as reported by the driver. | -| `product_id` | string | The product ID as reported by the driver. | -| `manufacturer` | string? | The manufacturer name as reported by the driver. | -| | | This will be `null` if no manufacturer is found. |^ -| `product` | string? | The product description as reported by the driver. | -| | | This will be `null` if no description is found. |^ -| `class` | string? | The class description as reported by the driver. | -| | | This will be `null` if no description is found. |^ -| `subclass` | string? | The subclass description as reported by the driver. | -| | | This will be `null` if no description is found. |^ -| `protocol` | string? | The protocol description as reported by the driver. | -| | | This will be `null` if no description is found. |^ -| `description` | string? | The full device description string as reported by | -| | | the usb.ids file. This will be `null` if no |^ -| | | description is found. |^ -/// - -#### List Serial Devices - -Returns a list of all serial devices detected on the system. These may be USB -CDC-ACM devices or hardware UARTs. - -```http title="HTTP Request" -GET /machine/peripherals/serial -``` - -```json title="JSON-RPC Request" -{ - "jsonrpc": "2.0", - "method": "machine.peripherals.serial", - "id": 7896 -} -``` - -/// api-example-response -```json -{ - "serial_devices": [ - { - "device_type": "hardware_uart", - "device_path": "/dev/ttyS0", - "device_name": "ttyS0", - "driver_name": "serial8250", - "path_by_hardware": null, - "path_by_id": null, - "usb_location": null - }, - { - "device_type": "usb", - "device_path": "/dev/ttyACM0", - "device_name": "ttyACM0", - "driver_name": "cdc_acm", - "path_by_hardware": "/dev/serial/by-path/platform-3f980000.usb-usb-0:1.2:1.0", - "path_by_id": "/dev/serial/by-id/usb-Klipper_stm32f407xx_unique_serial-if00", - "usb_location": "1:4" - }, - { - "device_type": "usb", - "device_path": "/dev/ttyUSB0", - "device_name": "ttyUSB0", - "driver_name": "ftdi_sio", - "path_by_hardware": "/dev/serial/by-path/platform-3f980000.usb-usb-0:1.4:1.0-port0", - "path_by_id": "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_unique_serial-if00-port0", - "usb_location": "1:5" - }, - { - "device_type": "hardware_uart", - "device_path": "/dev/ttyAMA0", - "device_name": "ttyAMA0", - "driver_name": "uart-pl011", - "path_by_hardware": null, - "path_by_id": null, - "usb_location": null - } - ] -} -``` -/// - -/// api-response-schema - open: True -Response - -| Field | Type | Description | -| ---------------- | ----- | --------------------------------------------------------- | -| `serial_devices` | array | An array of objects containing serial device information. | - - -Serial Device - -| Field | Type | Description | -| ------------------ | :-----: | ----------------------------------------------------------- | -| `device_type` | string | The type of serial device. Can be `hardware_uart` or `usb`. | -| `device_path` | string | The absolute file path to the device. | -| `device_name` | string | The device file name as reported by sysfs. | -| `driver_name` | string | The name of the device driver. | -| `path_by_hardware` | string? | A symbolic link to the device based on its physical | -| | | connection, ie: usb port. Will be `null` if no |^ -| | | matching link exists. |^ -| `path_by_id` | string? | A symbolic link the the device based on its reported IDs. | -| | | Will be `null` if no matching link exists. |^ -| `usb_location` | string? | An identifier derived from the reported usb bus and . | -| | | device numbers Can be used to match results from |^ -| | | `/machine/peripherals/usb`. Will be `null` for non-usb |^ -| | | devices. |^ -/// - -#### List Video Capture Devices - -Retrieves a list of V4L2 video capture devices on the system. If -the python3-libcamera system package is installed this request will -also return libcamera devices. - -```http title="HTTP Request" -GET /machine/peripherals/video -``` - -```json title="JSON-RPC Request" -{ - "jsonrpc": "2.0", - "method": "machine.peripherals.video", - "id": 7896 -} -``` - -/// api-example-response -```json -{ - "v4l2_devices": [ - { - "device_name": "video0", - "device_path": "/dev/video0", - "camera_name": "unicam", - "driver_name": "unicam", - "hardware_bus": "platform:3f801000.csi", - "modes": [], - "capabilities": [ - "VIDEO_CAPTURE", - "EXT_PIX_FORMAT", - "READWRITE", - "STREAMING", - "IO_MC" - ], - "version": "6.1.63", - "path_by_hardware": "/dev/v4l/by-path/platform-3f801000.csi-video-index0", - "path_by_id": null, - "alt_name": "unicam-image", - "usb_location": null - }, - { - "device_name": "video1", - "device_path": "/dev/video1", - "camera_name": "UVC Camera (046d:0825)", - "driver_name": "uvcvideo", - "hardware_bus": "usb-3f980000.usb-1.1", - "modes": [ - { - "format": "YUYV", - "description": "YUYV 4:2:2", - "flags": [], - "resolutions": [ - "640x480", - "160x120", - "176x144", - "320x176", - "320x240", - "352x288", - "432x240", - "544x288", - "640x360", - "752x416", - "800x448", - "800x600", - "864x480", - "960x544", - "960x720", - "1024x576", - "1184x656", - "1280x720", - "1280x960" - ] - }, - { - "format": "MJPG", - "description": "Motion-JPEG", - "flags": [ - "COMPRESSED" - ], - "resolutions": [ - "640x480", - "160x120", - "176x144", - "320x176", - "320x240", - "352x288", - "432x240", - "544x288", - "640x360", - "752x416", - "800x448", - "800x600", - "864x480", - "960x544", - "960x720", - "1024x576", - "1184x656", - "1280x720", - "1280x960" - ] - } - ], - "capabilities": [ - "VIDEO_CAPTURE", - "EXT_PIX_FORMAT", - "STREAMING" - ], - "version": "6.1.63", - "path_by_hardware": "/dev/v4l/by-path/platform-3f980000.usb-usb-0:1.1:1.0-video-index0", - "path_by_id": "/dev/v4l/by-id/usb-046d_0825_66EF0390-video-index0", - "alt_name": "UVC Camera (046d:0825)", - "usb_location": "1:3" - }, - { - "device_name": "video14", - "device_path": "/dev/video14", - "camera_name": "bcm2835-isp", - "driver_name": "bcm2835-isp", - "hardware_bus": "platform:bcm2835-isp", - "modes": [], - "capabilities": [ - "VIDEO_CAPTURE", - "EXT_PIX_FORMAT", - "STREAMING" - ], - "version": "6.1.63", - "path_by_hardware": null, - "path_by_id": null, - "alt_name": "bcm2835-isp-capture0", - "usb_location": null - }, - { - "device_name": "video15", - "device_path": "/dev/video15", - "camera_name": "bcm2835-isp", - "driver_name": "bcm2835-isp", - "hardware_bus": "platform:bcm2835-isp", - "modes": [], - "capabilities": [ - "VIDEO_CAPTURE", - "EXT_PIX_FORMAT", - "STREAMING" - ], - "version": "6.1.63", - "path_by_hardware": null, - "path_by_id": null, - "alt_name": "bcm2835-isp-capture1", - "usb_location": null - }, - { - "device_name": "video21", - "device_path": "/dev/video21", - "camera_name": "bcm2835-isp", - "driver_name": "bcm2835-isp", - "hardware_bus": "platform:bcm2835-isp", - "modes": [], - "capabilities": [ - "VIDEO_CAPTURE", - "EXT_PIX_FORMAT", - "STREAMING" - ], - "version": "6.1.63", - "path_by_hardware": "/dev/v4l/by-path/platform-bcm2835-isp-video-index1", - "path_by_id": null, - "alt_name": "bcm2835-isp-capture0", - "usb_location": null - }, - { - "device_name": "video22", - "device_path": "/dev/video22", - "camera_name": "bcm2835-isp", - "driver_name": "bcm2835-isp", - "hardware_bus": "platform:bcm2835-isp", - "modes": [], - "capabilities": [ - "VIDEO_CAPTURE", - "EXT_PIX_FORMAT", - "STREAMING" - ], - "version": "6.1.63", - "path_by_hardware": "/dev/v4l/by-path/platform-bcm2835-isp-video-index2", - "path_by_id": null, - "alt_name": "bcm2835-isp-capture1", - "usb_location": null - } - ], - "libcamera_devices": [ - { - "libcamera_id": "/base/soc/i2c0mux/i2c@1/ov5647@36", - "model": "ov5647", - "modes": [ - { - "format": "SGBRG10_CSI2P", - "resolutions": [ - "640x480", - "1296x972", - "1920x1080", - "2592x1944" - ] - } - ] - }, - { - "libcamera_id": "/base/soc/usb@7e980000/usb-port@1/usb-port@1-1.1:1.0-046d:0825", - "model": "UVC Camera (046d:0825)", - "modes": [ - { - "format": "MJPEG", - "resolutions": [ - "160x120", - "176x144", - "320x176", - "320x240", - "352x288", - "432x240", - "544x288", - "640x360", - "640x480", - "752x416", - "800x448", - "864x480", - "800x600", - "960x544", - "1024x576", - "960x720", - "1184x656", - "1280x720", - "1280x960" - ] - }, - { - "format": "YUYV", - "resolutions": [ - "160x120", - "176x144", - "320x176", - "320x240", - "352x288", - "432x240", - "544x288", - "640x360", - "640x480", - "752x416", - "800x448", - "864x480", - "800x600", - "960x544", - "1024x576", - "960x720", - "1184x656", - "1280x720", - "1280x960" - ] - } - ] - } - ] -} -``` -/// - -/// api-response-schema - open: True -Response - -| Field | Type | Description | -| ------------------- | :---: | ------------------------------------- | -| `v4l2_devices` | array | An array of V4L2 Device objects. | -| `libcamera_devices` | array | An array of Libcamera Device objects. | - -V4L2 Device - -| Field | Type | Description | -| ------------------ | :-----: | -------------------------------------------------------- | -| `device_name` | string | The V4L2 name assigned to the device. This is typically | -| | | the name of the file associated with the device. |^ -| `device_path` | string | The absolute system path to the device file. | -| `camera_name` | string | The camera name reported by the device driver. | -| `driver_name` | string | The name of the driver loaded for the device. | -| `alt_name` | string? | An alternative device name optionally reported by | -| | | sysfs. Will be `null` if the name file does not exist. |^ -| `hardware_bus` | string | A description of the hardware location of the device | -| `modes` | array | An array of V4L2 mode objects. | -| `capabilities` | array | An array of strings indicating the capabilities the | -| | | device supports as reported by V4L2. |^ -| `version` | string | The device version as reported by V4L2. | -| `path_by_hardware` | string? | A symbolic link to the device based on its physical | -| | | connection, ie: usb port.. Will be `null` if no |^ -| | | matching link exists. |^ -| `path_by_id` | string? | A symbolic link the the device based on its reported | -| | | ID. Will be `null` if no matching link exists. |^ -| `usb_location` | string? | An identifier derived from the reported usb bus and | -| | | device numbers. Will be `null` for non-usb devices. |^ - -V4L2 Mode - -| Field | Type | Description | -| ------------- | :----: | ------------------------------------------------------------ | -| `description` | string | The description of the mode reported by the V4L2 driver. | -| `flags` | array | An array of strings describing flags reported by the driver. | -| `format` | string | The pixel format of the mode. | -| `resolutions` | array | An array of strings describing the resolutions supported by | -| | | the mode. Each entry is reported as `x` |^ - -Libcamera Device - -| Field | Type | Description | -| -------------- | :----: | ------------------------------------------------------- | -| `libcamera_id` | string | The ID of the device as reported by libcamera. | -| `model` | string | The model name of the device. | -| `modes` | array | An array of `Libcamera Mode` objects, each describing a | -| | | mode supported by the device. |^ - -Libcamera Mode - -| Field | Type | Description | -| ------------- | :----: | ----------------------------------------------------------- | -| `format` | string | The pixel format of the mode. | -| `resolutions` | array | An array of strings describing the resolutions supported by | -| | | the mode. Each entry is reported as `x` |^ -/// - -#### Query Unassigned Canbus UUIDs - -Queries the provided canbus interface for unassigned Klipper or Katapult -node IDs. - -!!! Warning - It is recommended that frontends provide users with an explanation - of how UUID queries work and the potential pitfalls when querying - a bus with multiple unassigned nodes. An "unassigned" node is a - CAN node that has not been activated by Katapult or Klipper. If - either Klipper or Katapult has connected to the node, it will be - assigned a Node ID and therefore will no longer respond to queries. - A device reset is required to remove the assignment. - - When multiple unassigned nodes are on the network, each responds to - the query at roughly the same time. This results in arbitration - errors. Nodes will retry the send until the response reports success. - However, nodes track the count of arbitration errors, and once a - specific threshold is reached they will go into a "bus off" state. A - device reset is required to reset the counter and recover from "bus off". - - For this reason, it is recommended that users only issue a query when - a single unassigned node is on the network. If a user does wish to - query multiple unassigned nodes it is vital that they reset all nodes - on the network before running Klipper. - -```http title="HTTP Request" -GET /machine/peripherals/canbus?interface=can0 -``` - -```json title="JSON-RPC Request" -{ - "jsonrpc": "2.0", - "method": "machine.peripherals.canbus", - "params": { - "interface": "can0" - }, - "id": 7896 -} -``` - -/// api-parameters - open: True -| Name | Type | Description | -| ----------- | :----: | ----------------------------------------------------- | -| `interface` | string | The cansocket interface to query. Default is `can0`. | -/// - -/// api-example-response -```json -{ - "can_uuids": [ - { - "uuid": "11AABBCCDD", - "application": "Klipper" - } - ] -} -``` -/// - -/// api-response-schema - open: True -Response - -| Field | Type | Description | -| ----------- | :---: | ---------------------------------------------------------------- | -| `can_uuids` | array | An array of discovered CAN UUID objects, or an empty array if no | -| | | unassigned CAN nodes are found. |^ - -Can UUID - -| Field | Type | Description | -| ------------- | :----: | ----------------------------------------------------------- | -| `uuid` | string | The UUID of the unassigned node. | -| `application` | string | The name of the application running on the unassigned Node. | -| | | Should be "Klipper" or "Katapult". |^ -/// - -### File Operations - -Most file operations are available over both APIs, however file upload and -file download are currently only available via HTTP APIs. - -Moonraker organizes local directories into "roots". For example, -gcodes are located at `http:\\host\server\files\gcodes\*`, otherwise known -as the "gcodes" root. The following roots are available: - -- gcodes -- config -- config_examples (read-only) -- docs (read-only) - -Write operations (upload, delete, make directory, remove directory) are -only available on the `gcodes` and `config` roots. Note that the `config` root -is only available if the `config_path` option has been set in Moonraker's -configuration. - -#### List available files -Walks through a directory and fetches all files. All file names include a -path relative to the specified `root`. - -HTTP request: -```http -GET /server/files/list?root={root_folder} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.list", - "params": { - "root": "{root_folder}" - }, - "id": 4644 -} -``` -!!! tip - If the `root` argument is omitted the request will default to - the `gcodes` root. - -!!! note - The `gcodes` root will only return files with valid gcode - extensions. - -Returns: -A list of objects, where each object contains file data. -```json -[ - { - "path": "3DBenchy_0.15mm_PLA_MK3S_2h6m.gcode", - "modified": 1615077020.2025201, - "size": 4926481, - "permissions": "rw" - }, - { - "path": "Shape-Box_0.2mm_PLA_Ender2_20m.gcode", - "modified": 1614910966.946807, - "size": 324236, - "permissions": "rw" - }, - { - "path": "test_dir/A-Wing.gcode", - "modified": 1605202259, - "size": 1687387, - "permissions": "rw" - }, - { - "path": "test_dir/CE2_CubeTest.gcode", - "modified": 1614644445.4025, - "size": 1467339, - "permissions": "rw" - }, - { - "path": "test_dir/V350_Engine_Block_-_2_-_Scaled.gcode", - "modified": 1615768477.5133543, - "size": 189713016, - "permissions": "rw" - } -] -``` - -#### List registered roots -Reports all "root" directories registered with Moonraker. Information -such as location on disk and permissions are included. - -HTTP request: -```http -GET /server/files/roots -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.roots", - "id": 4644 -} -``` - -Returns: -A list of objects, where each object contains file data: - -```json -[ - { - "name": "config", - "path": "/home/pi/printer_data/config", - "permissions": "rw" - }, - { - "name": "logs", - "path": "/home/pi/printer_data/logs", - "permissions": "r" - }, - { - "name": "gcodes", - "path": "/home/pi/printer_data/gcodes", - "permissions": "rw" - }, - { - "name": "config_examples", - "path": "/home/pi/klipper/config", - "permissions": "r" - }, - { - "name": "docs", - "path": "/home/pi/klipper/docs", - "permissions": "r" - } -] -``` - -#### Get GCode Metadata - -Get metadata for a specified gcode file. - -HTTP request: -```http -GET /server/files/metadata?filename={filename} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.metadata", - "params": { - "filename": "{filename}" - }, - "id": 3545 -} -``` - -Parameters: - -- `filename`: Path to the gcode file, relative to the `gcodes` root. - For example, if the file is located at - `http://host/server/files/gcodes/tools/drill_head.gcode`, - the `filename` should be specified as `tools/drill_head.gcode` - -Returns: - -Metadata for the requested file if it exists. If any fields failed -parsing they will be omitted. The metadata will always include the file name, -modified time, and size. - -```json -{ - "print_start_time": null, - "job_id": null, - "size": 4926481, - "modified": 1615077020.2025201, - "slicer": "SuperSlicer", - "slicer_version": "2.2.52", - "layer_height": 0.15, - "first_layer_height": 0.2, - "object_height": 48.05, - "filament_total": 4056.4, - "estimated_time": 7569, - "thumbnails": [ - { - "width": 32, - "height": 32, - "size": 2596, - "relative_path": ".thumbs/3DBenchy_0.15mm_PLA_MK3S_2h6m-32x32.png" - }, - { - "width": 400, - "height": 300, - "size": 73308, - "relative_path": ".thumbs/3DBenchy_0.15mm_PLA_MK3S_2h6m-400x300.png" - } - ], - "first_layer_bed_temp": 60, - "first_layer_extr_temp": 215, - "gcode_start_byte": 79451, - "gcode_end_byte": 4915668, - "filename": "3DBenchy_0.15mm_PLA_MK3S_2h6m.gcode" -} -``` -!!! Note - The `print_start_time` and `job_id` fields are initialized to - `null`. They will be updated for each print job if the user has the - `[history]` component configured - -#### Scan GCode Metadata - -Initiate a metadata scan for a selected file. If the file has already -been scanned the endpoint will force a rescan - -HTTP request: -```http -GET /server/files/metascan?filename={filename} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.metascan", - "params": { - "filename": "{filename}" - }, - "id": 3545 -} -``` - -Parameters: - -- `filename`: Path to the gcode file, relative to the `gcodes` root. - For example, if the file is located at - `http://host/server/files/gcodes/tools/drill_head.gcode`, - the `filename` should be specified as `tools/drill_head.gcode` - -Returns: - -- An object containing the metadata resulting from the scan, matching - the return value of the [Get Metdata Endpoint](#get-gcode-metadata). - -#### Get GCode Thumbnails - -Returns thumbnail information for a supplied gcode file. If no thumbnail -information is available - -HTTP request: -```http -GET /server/files/thumbnails?filename={filename} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.thumbnails", - "params": { - "filename": "{filename}" - }, - "id": 3545 -} -``` - -Parameters: - -- `filename`: Path to the gcode file, relative to the `gcodes` root. - For example, if the file is located at - `http://host/server/files/gcodes/tools/drill_head.gcode`, - the `filename` should be specified as `tools/drill_head.gcode` - -Returns: - -An array of objects containing thumbnail information. If no -thumbnail information exists for the specified file then the -returned array wil be empty. - -```json -[ - { - "width": 32, - "height": 32, - "size": 1551, - "thumbnail_path": "test/.thumbs/CE2_FanCover-120mm-Mesh-32x32.png" - }, - { - "width": 300, - "height": 300, - "size": 31819, - "thumbnail_path": "test/.thumbs/CE2_FanCover-120mm-Mesh.png" - } -] -``` - -!!! Note - This information is the same as reported in the `thumbnails` field - of a [metadata](#get-gcode-metadata) object, with one exception. - The `thumbnail_path` field in the result above contains a - path relative to the `gcodes` root, whereas the `relative_path` - field reported in the metadata is relative to the gcode file's - parent folder. - -#### Get directory information -Returns a list of files and subdirectories given a supplied path. -Unlike `/server/files/list`, this command does not walk through -subdirectories. This request will return all files in a directory, -including files in the `gcodes` root that do not have a valid gcode -extension. - -HTTP request: -```http -GET /server/files/directory?path=gcodes/my_subdir&extended=true -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.get_directory", - "params": { - "path": "gcodes/my_subdir", - "extended": true - }, - "id": 5644 -} -``` -!!! tip - If the `path` argument is omitted then the command will return - directory information from the `gcodes` root. - -The `extended` argument is optional and defaults to false. If -supplied and set to true then data returned for gcode files -will also include metadata (if available). - -Returns: - -An object containing file and subdirectory information in the -following format: - -```json -{ - "dirs": [ - { - "modified": 1615768162.0412788, - "size": 4096, - "permissions": "rw", - "dirname": "test" - }, - { - "modified": 1613569827.489749, - "size": 4096, - "permissions": "rw", - "dirname": "Cura" - }, - { - "modified": 1615767459.6265886, - "size": 4096, - "permissions": "rw", - "dirname": "thumbs" - } - ], - "files": [ - { - "modified": 1615578004.9639666, - "size": 7300692, - "permissions": "rw", - "filename": "Funnel_0.2mm_PLA_Ender2_2h4m.gcode" - }, - { - "modified": 1589156863.9726968, - "size": 4214831, - "permissions": "rw", - "filename": "CE2_Pi3_A+_CaseLID.gcode" - }, - { - "modified": 1615030592.7722695, - "size": 2388774, - "permissions": "rw", - "filename": "CE2_calicat.gcode" - } - ], - "disk_usage": { - "total": 7522213888, - "used": 4280369152, - "free": 2903625728 - }, - "root_info": { - "name": "gcodes", - "permissions": "rw" - } -} -``` - -#### Create directory -Creates a directory at the specified path. - -HTTP request: -```http -POST /server/files/directory?path=gcodes/my_new_dir -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.post_directory", - "params": { - "path": "gcodes/my_new_dir" - }, - "id": 6548 -} -``` - -Returns: Information about the created directory -```json -{ - "item": { - "path": "my_new_dir", - "root": "gcodes", - "modified": 1676983427.3732708, - "size": 4096, - "permissions": "rw" - }, - "action": "create_dir" -} -``` - -#### Delete directory -Deletes a directory at the specified path. - -HTTP request: -```http -DELETE /server/files/directory?path=gcodes/my_subdir&force=false -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.delete_directory", - "params": { - "path": "gcodes/my_subdir", - "force": false - }, - "id": 6545 -} -``` -!!! warning - If the specified directory contains files then the delete request - will fail unless the `force` argument is set to `true`. - -Returns: Information about the deleted directory -```json -{ - "item": { - "path": "my_subdir", - "root": "gcodes", - "modified": 0, - "size": 0, - "permissions": "" - - }, - "action": "delete_dir" -} -``` - -#### Move a file or directory -Moves a file or directory from one location to another. The following -conditions must be met for a move successful move: - -- The source must exist -- The user (typically "pi") must have the appropriate file permissions -- Neither the source nor destination can be loaded by the `virtual_sdcard`. - If the source is a directory, it must not contain a file loaded by the - `virtual_sdcard`. - -When specifying the `source` and `dest`, the `root` directory should be -prefixed. Currently the only supported roots for `dest` are `gcodes`" -and `config`". - -This API may also be used to rename a file or directory. Be aware that an -attempt to rename a directory to a directory that already exists will result -in *moving* the source directory into the destination directory. Also be aware -that renaming a file to a file that already exists will result in overwriting -the existing file. - -HTTP request: -```http -POST /server/files/move?source=gcodes/testdir/my_file.gcode&dest=gcodes/subdir/my_file.gcode -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.move", - "params": { - "source": "gcodes/testdir/my_file.gcode", - "dest": "gcodes/subdir/my_file.gcode" - }, - "id": 5664 -} -``` - -Returns: Information about the moved file or directory -```json -{ - "item": { - "root": "gcodes", - "path": "subdir/my_file.gcode", - "modified": 1676940082.8595376, - "size": 384096, - "permissions": "rw" - }, - "source_item": { - "path": "testdir/my_file.gcode", - "root": "gcodes" - }, - "action": "move_file" -} -``` - -!!! Note - The `item` field contains file info for the destination. The `source_item` - contains the `path` and `root` the item was moved from. The `action` field - will be `move_file` if the source is a file or `move_dir` if the source is - a directory. - -#### Copy a file or directory -Copies a file or directory from one location to another. A successful copy has -the prerequisites as a move with one exception, a copy may complete if the -source file or directory is loaded by the `virtual_sdcard`. As with the move -API, the `source` and `dest` should have the root prefixed to the path. - -HTTP request: -```http -POST /server/files/copy?source=gcodes/my_file.gcode&dest=gcodes/subdir/my_file.gcode -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.copy", - "params": { - "source": "gcodes/my_file.gcode", - "dest": "gcodes/subdir/my_file.gcode" - }, - "id": 5623 -} -``` - -Returns: Information about the copied file or directory -```json -{ - "item": { - "root": "gcodes", - "path": "subdir/my_file.gcode", - "modified": 1676940082.8595376, - "size": 384096, - "permissions": "rw" - }, - "action": "create_file" -} -``` - -!!! Note - The `item` field contains file info for the destination. The `action` field - will be `create_file` if a new file was created, `modify_file` if an exiting - file was overwitten, or `create_dir` if an entire directory was copied. - -#### Create a ZIP archive - -Creates a `zip` file consisting of one or more files. - -HTTP request: -```http -POST /server/files/zip -Content-Type: application/json - -{ - "dest": "config/errorlogs.zip", - "items": [ - "config/printer.cfg", - "logs", - "gcodes/subfolder" - ], - "store_only": false -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.zip", - "params": { - "dest": "config/errorlogs.zip", - "items": [ - "config/printer.cfg", - "logs", - "gcodes/subfolder" - ], - "store_only": false - }, - "id": 5623 -} -``` - -Parameters: - -- `dest` - (Optional) - Relative path to the destination zip. The first element - of the path must be valid `root` with write access. If the path contains subfolders - the parent folder must exist. The default is `config/collection-{timestamp}.zip`, - where `{timestamp}` is generated based on the localtime. -- `items` - (Required) - An array of relative paths containing files and or folders - to include in the archive. Each item must meet the following requirements: - - The first element of the item must be a registered `root` with read access. - - Each item must point to a valid file or folder. - - Moonraker must have permission to read the specified files and/or directories. - - If the path is to a directory then all files with read permissions are included. - Subfolders are not included recursively. -- `store_only` - (Optional) - If set to `true` then the archive will not compress its - contents. Otherwise the traditional `deflation` algorithm is used to compress the - archives contents. The default is `false`. - -Returns: An object in the following format: - -```json -{ - "destination": { - "root": "config", - "path": "errorlogs.zip", - "modified": 1676984423.8892415, - "size": 420, - "permissions": "rw" - }, - "action": "zip_files" -} -``` - -- `destination` - an object containing the destination `root` and a path to the file - relative to the root. -- `action` - The file action, will be `zip_files` - -#### File download -Retrieves file `filename` at root `root`. The `filename` must include -the relative path if it is not in the root folder. - -HTTP request: -```http -GET /server/files/{root}/{filename} -``` -JSON-RPC request: Not Available - -Returns: - -The requested file - -#### File upload -Upload a file. Currently files may be uploaded to the `gcodes` or `config` -roots, with `gcodes` being the default. If one wishes to upload -to a subdirectory, the path may be added to the upload's file name -(relative to the root). If the directory does not exist an error will be -returned. Alternatively, the `path` form argument may be set, as explained -below. - -HTTP request: -```http -POST /server/files/upload` -Content-Type: multipart/form-data - -------FormBoundaryemap3PkuvKX0B3HH -Content-Disposition: form-data; name="file"; filename="myfile.gcode" -Content-Type: application/octet-stream - - -------FormBoundaryemap3PkuvKX0B3HH-- -``` - -The file must be uploaded in the request's body `multipart/form-data` (ie: -``). The following arguments may also be added to the -form-data: - -- `root`: The root location in which to upload the file. Currently this may - be `gcodes` or `config`. If not specified the default is `gcodes`. -- `path`: This argument may contain a path (relative to the root) indicating - a subdirectory to which the file is written. If a `path` is present the - server will attempt to create any subdirectories that do not exist. -- `checksum`: A SHA256 hex digest calculated by the client for the uploaded - file. If this argument is supplied the server will compare it to its own - checksum calculation after the upload has completed. A checksum mismatch - will result in a 422 error. - -Arguments available only for the `gcodes` root: - -- `print`: If set to "true", Klippy will attempt to start the print after - uploading. Note that this value should be a string type, not boolean. This - provides compatibility with OctoPrint's upload API. - -JSON-RPC request: Not Available - -Returns: Information about the uploaded file. Note that `print_started` -is only included when the supplied root is set to `gcodes`. -```json -{ - "item": { - "path": "Lock Body Shim 1mm_0.2mm_FLEX_MK3S_2h30m.gcode", - "root": "gcodes", - "modified": 1676984527.636818, - "size": 71973, - "permissions": "rw" - }, - "print_started": false, - "print_queued": false, - "action": "create_file" -} -``` - -#### File delete -Delete a file in the requested root. If the file exists in a subdirectory, -its relative path must be part of the `{filename}` argument. - -HTTP request: -```http -DELETE /server/files/{root}/{filename} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.files.delete_file", - "params": { - "path": "{root}/{filename}" - }, - "id": 1323 -} -``` -Returns: Information about the deleted file -```json -{ - "item": { - "path": "Lock Body Shim 1mm_0.2mm_FLEX_MK3S_2h30m.gcode", - "root": "gcodes", - "size": 0, - "modified": 0, - "permissions": "" - }, - "action": "delete_file" -} -``` - -#### Download klippy.log -!!! Note - Logs are now available in the `logs` root. Front ends should consider - presenting all available logs using "file manager" type of UI. That said, - If Klipper has not been configured to write logs in the `logs` root then - this endpoint is available as a fallback. - -HTTP request: -```http -GET /server/files/klippy.log -``` -JSON-RPC request: Not Available - -Returns: - -The requested file - -#### Download moonraker.log -!!! Note - Logs are now available in the `logs` root. Front ends should consider - presenting all available logs using "file manager" type of UI. That said, - If Moonraker has not been configured to write logs in the `logs` root then - this endpoint is available as a fallback. - -HTTP request: -```http -GET /server/files/moonraker.log -``` -JSON-RPC request: Not Available - -Returns: - -The requested file - -### Authorization - -The Authorization endpoints are enabled when the user has the -`[authorization]` component configured in `moonraker.conf`. - -Untrusted clients must use either a JSON Web Token or an API key to access -Moonraker's HTTP APIs. JWTs should be included in the `Authorization` -header as a `Bearer` type for each HTTP request. If using an API Key it -should be included in the `X-Api-Key` header for each HTTP Request. - -Websocket authentication can be achieved via the request itself or -post connection. Unlike HTTP requests it is not necessasry to pass a -token and/or API Key to each request. The -[identify connection](#identify-connection) endpoint takes optional -`access_token` and `api_key` parameters that may be used to authentiate -a user already logged in, otherwise the `login` API may be used for -authentication. Websocket connections will stay authenticated until -the connection is closed or the user logs out. - -!!! note - ECMAScript imposes limitations on certain requests that prohibit the - developer from modifying the HTTP headers (ie: The request to open a - websocket, "download" requests that open a dialog). In these cases - it is recommended for the developer to request a `oneshot_token`, then - send the result via the `token` query string argument in the desired - request. - -!!! warning - It is strongly recommended that arguments for the below APIs are - passed in the request's body. - -#### Login User -HTTP Request: -```http -POST /access/login -Content-Type: application/json - -{ - "username": "my_user", - "password": "my_password", - "source": "moonraker" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.login", - "params": { - "username": "my_user", - "password": "my_password", - "source": "moonraker" - }, - "id": 1323 -} -``` - -Arguments: -- `username`: The user login name. This argument is required. -- `password`: The user password. This arugment is required. -- `source`: The authentication source. Can be `moonraker` or `ldap`. The - default is `moonraker`. - -Returns: An object the logged in username, auth token, refresh token, -and action summary: -```json -{ - "username": "my_user", - "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY4MDAuNDgxNjU1LCAiZXhwIjogMTYxODg4MDQwMC40ODE2NTUsICJ1c2VybmFtZSI6ICJteV91c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.QdieeEskrU0FrH7rXKuPDSZxscM54kV_vH60uJqdU9g", - "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY4MDAuNDgxNzUxNCwgImV4cCI6IDE2MjY2NTI4MDAuNDgxNzUxNCwgInVzZXJuYW1lIjogIm15X3VzZXIiLCAidG9rZW5fdHlwZSI6ICJyZWZyZXNoIn0.btJF0LJfymInhGJQ2xvPwkp2dFUqwgcw4OA_wE-EcCM", - "action": "user_logged_in", - "source": "moonraker" -} -``` -- The `token` field is a JSON Web Token used to authorize the user. It should - be included in the `Authorization` header as a `Bearer` type for all HTTP - requests. The `token` expires after 1 hour. -- The `refresh_token` field contains a JWT that can be used to generate new - tokens after they are expire. See the - [refresh token section](#refresh-json-web-token) for details. - -!!! Note - This endpoint may be accessed by unauthorized clients. A 401 would - only be returned if the authentication failed. - -#### Logout Current User -HTTP Request: -```http -POST /access/logout -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.logout", - "id": 1323 -} -``` - -Returns: An object containing the logged out username and action summary. -```json -{ - "username": "my_user", - "action": "user_logged_out" -} - -``` - -#### Get Current User -HTTP Request: -```http -GET /access/user -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.get_user", - "id": 1323 -} -``` - -Returns: An object containing the currently logged in user name, the source and -the date on which the user was created (in unix time). -```json -{ - "username": "my_user", - "source": "moonraker", - "created_on": 1618876783.8896716 -} -``` - -#### Create User -HTTP Request: -```http -POST /access/user -Content-Type: application/json - -{ - "username": "my_user", - "password": "my_password" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.post_user", - "params": { - "username": "my_user", - "password": "my_password" - }, - "id": 1323 -} -``` - -Returns: An object containing the created user name, an auth token, -a refresh token, the source, and an action summary. Creating a user also -effectively logs the user in. - -```json -{ - "username": "my_user", - "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY3ODMuODkxNjE5LCAiZXhwIjogMTYxODg4MDM4My44OTE2MTksICJ1c2VybmFtZSI6ICJteV91c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.oH0IShTL7mdlVs4kcx3BIs_-1j0Oe-qXezJKjo-9Xgo", - "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzY3ODMuODkxNzAyNCwgImV4cCI6IDE2MjY2NTI3ODMuODkxNzAyNCwgInVzZXJuYW1lIjogIm15X3VzZXIiLCAidG9rZW5fdHlwZSI6ICJyZWZyZXNoIn0.a6ZeRjk8RQQJDDH0JV-qGY_d_HIgfI3XpsqUlUaFT7c", - "source": "moonraker", - "action": "user_created" -} -``` -!!! note - Unlike `/access/login`, `/access/user` is a protected endpoint. To - create a new user a client must either be trusted, use the API Key, - or be logged in as another user. - -#### Delete User -Deletes a registered user. - -!!! note - A request to delete a user MUST come from an authorized source - other than the account to be deleted. This can be a "trusted user", - the "api key user", or any other user account. - -HTTP Request: -```http -DELETE /access/user -Content-Type: application/json - -{ - "username": "my_username" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.delete_user", - "params": { - "username": "my_username" - }, - "id": 1323 -} -``` - -Returns: The username of the deleted user and an action summary. This -effectively logs the user out, as all outstanding tokens will be invalid. -```json -{ - "username": "my_user", - "action": "user_deleted" -} -``` - -#### List Available Users -HTTP Request: -```http -GET /access/users/list -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.users.list", - "id": 1323 -} -``` - -Returns: A list of created users on the system -```json -{ - "users": [ - { - "username": "testuser", - "source": "moonraker", - "created_on": 1618771331.1685035 - }, - { - "username": "testuser2", - "source": "ldap", - "created_on": 1620943153.0191233 - } - ] -} -``` - -#### Reset User Password -HTTP Request: -```http -POST /access/user/password -Content-Type: application/json - -{ - "password": "my_current_password", - "new_password": "my_new_pass" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.user.password", - "params": { - "password": "my_current_password", - "new_password": "my_new_pass" - }, - "id": 1323 -} -``` - -Returns: The username and action summary. -```json -{ - "username": "my_user", - "action": "user_password_reset" -} -``` - -#### Refresh JSON Web Token -This endpoint can be used to refresh an expired auth token. If this -request returns an error then the refresh token is no longer valid and -the user must login with their credentials. - -HTTP Request: -```http -POST /access/refresh_jwt -Content-Type: application/json - -{ - "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4Nzc0ODUuNzcyMjg5OCwgImV4cCI6IDE2MjY2NTM0ODUuNzcyMjg5OCwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAicmVmcmVzaCJ9.Y5YxGuYSzwJN2WlunxlR7XNa2Y3GWK-2kt-MzHvLbP8" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.refresh_jwt", - "params": { - "refresh_token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4Nzc0ODUuNzcyMjg5OCwgImV4cCI6IDE2MjY2NTM0ODUuNzcyMjg5OCwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAicmVmcmVzaCJ9.Y5YxGuYSzwJN2WlunxlR7XNa2Y3GWK-2kt-MzHvLbP8" - }, - "id": 1323 -} -``` - -Returns: The username, new auth token, the source and action summary. -```json -{ - "username": "my_user", - "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiTW9vbnJha2VyIiwgImlhdCI6IDE2MTg4NzgyNDMuNTE2Nzc5MiwgImV4cCI6IDE2MTg4ODE4NDMuNTE2Nzc5MiwgInVzZXJuYW1lIjogInRlc3R1c2VyIiwgInRva2VuX3R5cGUiOiAiYXV0aCJ9.Ia_X_pf20RR4RAEXcxalZIOzOBOs2OwearWHfRnTSGU", - "source": "moonraker", - "action": "user_jwt_refresh" -} -``` -!!! Note - This endpoint may be accessed by unauthorized clients. A 401 would - only be returned if the refresh token is invalid. - -#### Generate a Oneshot Token - -Javascript is not capable of modifying the headers for some HTTP requests -(for example, the `websocket`), which is a requirement to apply JWT or API Key -authorization. To work around this clients may request a Oneshot Token and -pass it via the query string for these requests. Tokens expire in 5 seconds -and may only be used once, making them relatively safe for inclusion in the -query string. - -HTTP request: -```http -GET /access/oneshot_token -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.oneshot_token", - "id": 1323 -} -``` - -Returns: - -A temporary token that may be added to a request's query string for access -to any API endpoint. The query string should be added in the form of: -``` -?token={base32_random_token} -``` - -#### Retrieve information about authorization endpoints -HTTP Request: -```http -GET /access/info -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.info", - "id": 1323 -} -``` - -Returns: An object containing information about authorization endpoints, such as -default_source and available_sources. -```json -{ - "default_source": "moonraker", - "available_sources": [ - "moonraker", - "ldap" - ] -} -``` - -#### Get the Current API Key -HTTP request: -```http -GET /access/api_key -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.get_api_key", - "id": 1323 -} -``` - -Returns: - -The current API key - -#### Generate a New API Key -HTTP request: -```http -POST /access/api_key -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "access.post_api_key", - "id": 1323 -} -``` - -Returns: - -The newly generated API key. This overwrites the previous key. Note that -the API key change is applied immediately, all subsequent HTTP requests -from untrusted clients must use the new key. Changing the API Key will -not affect open websockets authenticated using the previous API Key. - -### Database APIs -The following endpoints provide access to Moonraker's lmdb database. The -database is divided into `namespaces`. Each client may define its own -namespace to store information. From the client's point of view, a -namespace is an `object`. Items in the database are accessed by providing -a namespace and a key. A key may be specified as string, where a "." is a -delimiter, to access nested fields. Alternatively the key may be specified -as an array of strings, where each string references a nested field. -This is useful for scenarios where your namespace contains keys that include -a "." character. - -!!! note - Moonraker reserves the `moonraker`, `gcode_metadata`, and `history` - namespaces. Clients may read from these namespaces but they may not - modify them. - -For example, assume the following object is stored in the "superclient" -namespace: - -```json -{ - "settings": { - "console": { - "enable_autocomplete": true - } - }, - "theme": { - "background_color": "black" - } -} -``` -One may access the `enable_autocomplete` field by supplying `superclient` as -the `namespace` argument and `settings.console.enable_autocomplete` or -`["settings", "console", "enable_autocomplete"]` as the `key` argument for -the request. The entire settings object could be accessed by providing -`settings` or `["settings"]` as the `key` argument. The entire namespace -may be read by omitting the `key` argument, however as explained below it -is not possible to modify a namespace without specifying a key. - -#### List Database Info - -Lists all namespaces with read and/or write access. Also lists database -backup files. - -HTTP request: -```http -GET /server/database/list -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.list", - "id": 8694 -} -``` - -Returns: - -An object containing an array of namespaces and an array of backup files. -```json -{ - "namespaces": [ - "gcode_metadata", - "webcams", - "update_manager", - "announcements", - "database", - "moonraker" - ], - "backups": [ - "sqldb-backup-20240513-134542.db", - "testbackup.db", - "testbackup2.db" - ] -} -``` - -#### Get Database Item -Retrieves an item from a specified namespace. The `key` argument may be -omitted, in which case an object representing the entire namespace will -be returned in the `value` field. If the `key` is provided and does not -exist in the database an error will be returned. - -HTTP request: -```http -GET /server/database/item?namespace={namespace}&key={key} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.get_item", - "params": { - "namespace": "{namespace}", - "key": "{key}" - }, - "id": 5644 -} -``` -Returns: - -An object containing the requested `namespace`, `key`, and `value`. -```json -{ - "namespace": "moonraker", - "key": "file_manager.metadata_version", - "value": 2 -} -``` - -#### Add Database Item -Inserts an item into the database. If the `namespace` does not exist -it will be created. If the `key` specifies a nested field, all parents -will be created if they do not exist. If the key exists it will be -overwritten with the provided `value`. The `key` parameter must be provided, -as it is not possible to assign a value directly to a namespace. - -HTTP request: -```http -POST /server/database/item?namespace={namespace}&key={key}value={value}` -``` -!!! note - If the `value` is not a string type, the `value` argument must - provide a [type hint](#query-string-type-hints). Alternatively, - arguments may be passed via the request body in JSON format. For - example: -```http -POST /server/database/item -Content-Type: application/json - -{ - "namespace": "my_client", - "key": "settings.some_count", - "value": 100 -} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.post_item", - "params": { - "namespace": "{namespace}", - "key": "{key}", - "value": 100 - }, - "id": 4654 -} -``` -Returns: - -An object containing the inserted `namespace`, `key`, and `value`. -```json -{ - "namespace": "test", - "key": "settings.some_count", - "value": 9001 -} -``` - -#### Delete Database Item -Deletes an item from a `namespace` at the specified `key`. If the key does not -exist in the namespace an error will be returned. If the deleted item results -in an empty namespace, the namespace will be removed from the database. - -HTTP request: -```http -DELETE /server/database/item?namespace={namespace}&key={key} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.delete_item", - "params": { - "namespace": "{namespace}", - "key": "{key}" - }, - "id": 4654 -} -``` -Returns: -An object containing the `namespace`, `key`, and `value` of the -deleted item. -```json -{ - "namespace": "test", - "key": "settings.some_count", - "value": 9001 -} -``` - -#### Compact Database - -Compacts and defragments the the sqlite database using the `VACUUM` command. -This API cannot be requested when Klipper is printing. - -HTTP request: -```http -POST /server/database/compact -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.compact", - "id": 4654 -} -``` -Returns: -An object containing the size of the database on disk before and after -the database is compacted. -```json -{ - "previous_size": 139264, - "new_size": 122880 -} -``` - -#### Backup Database - -Creates a backup of the current database. The backup will be -created in the `/backup/database/`. - -This API cannot be requested when Klipper is printing. - -HTTP request: -```http -POST /server/database/backup -Content-Type: application/json - -{ - "filename": "sql-db-backup.db" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.post_backup", - "params": { - "filename": "sql-db-backup.db" - }, - "id": 4654 -} -``` - -Parameters: - -- `filename`: An optional file name for the backup file. The default - is `sqldb-backup--`. - - -Returns: -An object containing the path on disk to the backup. -```json -{ - "backup_path": "/home/test/printer_data/backup/database/sql-db-backup.db" -} -``` - -#### Delete a backup - -Deletes a previously backed up database. - -HTTP request: -```http -DELETE /server/database/backup -Content-Type: application/json - -{ - "filename": "sql-db-backup.db" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.delete_backup", - "params": { - "filename": "sql-db-backup.db" - }, - "id": 4654 -} -``` - -Parameters: - -- `filename`: The name of the backup file to delete. Must be a valid - filename reported in by the [database list](#list-database-info) API. - This parameter must be provided. - -Returns: -An object containing the path on disk to the backup file that was removed. -```json -{ - "backup_path": "/home/test/printer_data/backup/database/sql-db-backup.db" -} -``` - -#### Restore Database - -Restores a previously backed up sqlite database file. The backup -must be located at `/backup/database/`. The -`` must be a valid filename reported in by the -[database list](#list-database-info) API. - -This API cannot be requested when Klipper is printing. - -!!! Note - Moonraker will restart immediately after this request is processed. - -HTTP request: -```http -POST /server/database/restore -Content-Type: application/json - -{ - "filename": "sql-db-backup.db" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.database.restore", - "params": { - "filename": "sql-db-backup.db" - }, - "id": 4654 -} -``` - -Parameters: - -- `filename`: The name of the database file to restore. Must be a valid - filename reported in by the [database list](#list-database-info) API. - This parameter must be provided. - -Returns: -An object containing a list of restored namespaces and restored tables. -```json -{ - "restored_tables": [ - "table_registry", - "namespace_store", - "authorized_users", - "job_history", - "job_totals" - ], - "restored_namespaces": [ - "database", - "fluidd", - "gcode_metadata", - "mainsail", - "moonraker", - "update_manager", - "webcams" - ] -} -``` - -### Job Queue APIs - -The following endpoints may be used to manage Moonraker's job queue. -Note that Moonraker's Job Queue is implemented as a FIFO queue and it may -contain multiple references to the same job. - -!!! Note - All filenames provided to and returned by these endpoints are relative to - the `gcodes` root. - -#### Retrieve the job queue status - -Retrieves the current state of the job queue - -HTTP request: -```http -GET /server/job_queue/status -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.job_queue.status", - "id": 4654 -} -``` - -Returns: - -The current state of the job queue: - -```json -{ - "queued_jobs": [ - { - "filename": "job1.gcode", - "job_id": "0000000066D99C90", - "time_added": 1636151050.7666452, - "time_in_queue": 21.89680004119873 - }, - { - "filename": "job2.gcode", - "job_id": "0000000066D991F0", - "time_added": 1636151050.7766452, - "time_in_queue": 21.88680004119873 - }, - { - "filename": "subdir/job3.gcode", - "job_id": "0000000066D99D80", - "time_added": 1636151050.7866452, - "time_in_queue": 21.90680004119873 - } - ], - "queue_state": "ready" -} -``` - -Below is a description of the returned fields: - -- `queued_jobs`: an array of objects representing each queued job. Each - object contains the `filename` of the enqueued job and a unique `job_id` - generated for each job. The `job_id` is a 64-bit Hexadecimal string value. - On 32-bit systems the most significant bits will always contain zeros. Items - are ordered by the time they were queued, the first item will be the next job - loaded. -- `queue_state`: The current state of the queue. Can be one of the following: - - `ready`: The queue is active and will load the next job upon completion - of the current job - - `loading`: The queue is currently loading the next job. If the user - specified a `job_transition_delay` and/or `job_transition_gcode`, the - queue will remain in the `loading` state until both are completed or - an error is encountered. - - `starting`: The queue enters this state after the `loading` phase is - complete before attempting to start the job. - - `paused`: The queue is currently paused and will not load the next job - upon completion of the current job. The queue will enter the `paused` - state if an error is encountered during the `loading` or `starting` phases, - or if the user pauses the queue through the provided endpoint. -- `time_added`: The time (in Unix Time) the job was added to the queue -- `time_in_queue`: The cumulative amount of time (in seconds) the job has been - pending in the queue - -#### Enqueue a job - -Adds a job, or an array of jobs, to the end of the job queue. The same -filename may be specified multiple times to queue a job that repeats. -When multiple jobs are specified they will be enqueued in the order they -are received. - -!!! Note - The request will be aborted and return an error if any of the supplied - files do not exist. - -HTTP request: -```http -POST /server/job_queue/job?filenames=job1.gcode,job2.gcode,subdir/job3.gcode -``` - -!!! Note - Multiple jobs should be comma separated as shown above. - Alternatively `filenames` maybe be specified as a json object - in the body of the request. - -```http -POST /server/job_queue/job -Content-Type: application/json - -{ - "filenames": [ - "job1.gcode", - "job2.gcode", - "subdir/job3.gcode" - ], - "reset": false -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.job_queue.post_job", - "params": { - "filenames": [ - "job1.gcode", - "job2.gcode", - "subdir/job3.gcode" - ], - "reset": false - }, - "id": 4654 -} -``` - -Parameters: - -- `reset`: A boolean value indicating whether Moonraker should clear the - existing queued jobs before adding the new jobs. Defaults to `false`. - -Returns: - -The current state of the job queue: - -```json -{ - "queued_jobs": [ - { - "filename": "job1.gcode", - "job_id": "0000000066D99C90", - "time_added": 1636151050.7666452, - "time_in_queue": 21.89680004119873 - }, - { - "filename": "job2.gcode", - "job_id": "0000000066D991F0", - "time_added": 1636151050.7766452, - "time_in_queue": 21.88680004119873 - }, - { - "filename": "subdir/job3.gcode", - "job_id": "0000000066D99D80", - "time_added": 1636151050.7866452, - "time_in_queue": 21.90680004119873 - } - ], - "queue_state": "ready" -} -``` - -#### Remove a Job - -Removes one or more jobs from the queue. - -!!! Note - Unlike the POST version of this method, it is not necessary that - all job ids exist. If any supplied job id does not exist in the - queue it will be silently ignored. Clients can verify the contents - of the queue via the return value. - -HTTP request: -```http -DELETE /server/job_queue/job?job_ids=0000000066D991F0,0000000066D99D80 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.job_queue.delete_job", - "params": { - "job_ids": [ - "0000000066D991F0", - "0000000066D99D80" - ] - }, - "id": 4654 -} -``` -!!! Tip - Alternatively `all=true` (`"all": true` for JSON-RPC) may specified - to clear the job queue. - -Returns: - -The current state of the job queue: - -```json -{ - "queued_jobs": [ - { - "filename": "job1.gcode", - "job_id": "0000000066D99C90", - "time_added": 1636151050.7666452, - "time_in_queue": 21.89680004119873 - } - ], - "queue_state": "ready" -} -``` -#### Pause the job queue - -Sets the job queue state to "pause", which prevents the next job -in the queue from loading after an job in progress is complete. - -!!! Note - If the queue is paused while the queue is in the `loading` state - the load will be aborted. - -HTTP request: -```http -POST /server/job_queue/pause -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.job_queue.pause", - "id": 4654 -} -``` - -Returns: - -The current state of the job queue: - -```json -{ - "queued_jobs": [ - { - "filename": "job1.gcode", - "job_id": "0000000066D99C90", - "time_added": 1636151050.7666452, - "time_in_queue": 21.89680004119873 - }, - { - "filename": "job2.gcode", - "job_id": "0000000066D991F0", - "time_added": 1636151050.7766452, - "time_in_queue": 21.88680004119873 - }, - { - "filename": "subdir/job3.gcode", - "job_id": "0000000066D99D80", - "time_added": 1636151050.7866452, - "time_in_queue": 21.90680004119873 - } - ], - "queue_state": "paused" -} -``` - -#### Start the job queue - -Starts the job queue. If Klipper is ready to start a print the next -job in the queue will be loaded. Otherwise the queue will be put -into the "ready" state, enabling automatic transition after the next -completed print. - -HTTP request: -```http -POST /server/job_queue/start -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.job_queue.start", - "id": 4654 -} -``` - -Returns: - -The current state of the job queue: - -```json -{ - "queued_jobs": [ - { - "filename": "job1.gcode", - "job_id": "0000000066D99C90", - "time_added": 1636151050.7666452, - "time_in_queue": 21.89680004119873 - }, - { - "filename": "job2.gcode", - "job_id": "0000000066D991F0", - "time_added": 1636151050.7766452, - "time_in_queue": 21.88680004119873 - }, - { - "filename": "subdir/job3.gcode", - "job_id": "0000000066D99D80", - "time_added": 1636151050.7866452, - "time_in_queue": 21.90680004119873 - } - ], - "queue_state": "loading" -} -``` - -#### Perform a Queue Jump - -Jumps a job to the front of the queue. - -HTTP request: -```http -POST /server/job_queue/jump?job_id=0000000066D991F0 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.job_queue.jump", - "params": { - "job_id": "0000000066D991F0" - }, - "id": 4654 -} -``` - -Returns: - -The current state of the job queue: - -```json -{ - "queued_jobs": [ - { - "filename": "job2.gcode", - "job_id": "0000000066D991F0", - "time_added": 1636151050.7766452, - "time_in_queue": 21.88680004119873 - }, - { - "filename": "job1.gcode", - "job_id": "0000000066D99C90", - "time_added": 1636151050.7666452, - "time_in_queue": 21.89680004119873 - }, - { - "filename": "subdir/job3.gcode", - "job_id": "0000000066D99D80", - "time_added": 1636151050.7866452, - "time_in_queue": 21.90680004119873 - } - ], - "queue_state": "loading" -} -``` - -### Announcement APIs -The following endpoints are available to manage announcements. See -[the appendix](#announcements) for details on how -announcements work and recommendations for your implementation. - -#### List announcements -Retrieves a list of current announcements. The `include_dismissed` -argument is optional and defaults to `true`. If set to `false` -dismissed entries will be omitted from the return value. - -HTTP request: -```http -GET /server/announcements/list?include_dismissed=false -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.announcements.list", - "params": { - "include_dismissed": false - }, - "id": 4654 -} -``` - -Returns: - -The current list of announcements, in descending order (newest to oldest) -sorted by `date` and a list of feeds Moonraker is currently subscribed to: - -```json -{ - "entries": [ - { - "entry_id": "arksine/moonlight/issue/3", - "url": "https://github.com/Arksine/moonlight/issues/3", - "title": "Test announcement 3", - "description": "Test Description [with a link](https://moonraker.readthedocs.io).", - "priority": "normal", - "date": 1647459219, - "dismissed": false, - "date_dismissed": null, - "dismiss_wake": null, - "source": "moonlight", - "feed": "moonlight" - }, - { - "entry_id": "arksine/moonlight/issue/2", - "url": "https://github.com/Arksine/moonlight/issues/2", - "title": "Announcement Test Two", - "description": "This is a high priority announcement. This line is included in the description.", - "priority": "high", - "date": 1646855579, - "dismissed": false, - "date_dismissed": null, - "dismiss_wake": null, - "source": "moonlight", - "feed": "moonlight" - }, - { - "entry_id": "arksine/moonlight/issue/1", - "url": "https://github.com/Arksine/moonlight/issues/1", - "title": "Announcement Test One", - "description": "This is the description. Anything here should appear in the announcement, up to 512 characters.", - "priority": "normal", - "date": 1646854678, - "dismissed": false, - "date_dismissed": null, - "dismiss_wake": null, - "source": "moonlight", - "feed": "moonlight" - }, - { - "entry_id": "arksine/moonraker/issue/349", - "url": "https://github.com/Arksine/moonraker/issues/349", - "title": "PolicyKit warnings; unable to manage services, restart system, or update packages", - "description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.", - "priority": "normal", - "date": 1643392406, - "dismissed": false, - "source": "moonlight", - "feed": "Moonraker" - } - ], - "feeds": [ - "moonraker", - "klipper", - "moonlight" - ] -} -``` - -#### Update announcements -Requests that Moonraker check for announcement updates. This is generally -not required in production, as Moonraker will automatically check for -updates every 30 minutes. However, during development this endpoint is -useful to force an update when it is necessary to perform integration -tests. - -HTTP request: -```http -POST /server/announcements/update -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.announcements.update", - "id": 4654 -} -``` - -Returns: - -The current list of announcements, in descending order (newest to oldest) -sorted by `date`, and a `modified` field that contains a boolean value -indicating if the update resulted in a change: - -```json -{ - "entries": [ - { - "entry_id": "arksine/moonraker/issue/349", - "url": "https://github.com/Arksine/moonraker/issues/349", - "title": "PolicyKit warnings; unable to manage services, restart system, or update packages", - "description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.", - "priority": "normal", - "date": 1643392406, - "dismissed": false, - "source": "moonlight", - "feed": "Moonraker" - }, - { - "entry_id": "arksine/moonlight/issue/1", - "url": "https://github.com/Arksine/moonlight/issues/1", - "title": "Announcement Test One", - "description": "This is the description. Anything here should appear in the announcement, up to 512 characters.", - "priority": "normal", - "date": 1646854678, - "dismissed": true, - "source": "moonlight", - "feed": "Moonlight" - }, - { - "entry_id": "arksine/moonlight/issue/2", - "url": "https://github.com/Arksine/moonlight/issues/2", - "title": "Announcement Test Two", - "description": "This is a high priority announcement. This line is included in the description.", - "priority": "high", - "date": 1646855579, - "dismissed": false, - "source": "moonlight", - "feed": "Moonlight" - }, - { - "entry_id": "arksine/moonlight/issue/3", - "url": "https://github.com/Arksine/moonlight/issues/3", - "title": "Test announcement 3", - "description": "Test Description [with a link](https://moonraker.readthedocs.io).", - "priority": "normal", - "date": 1647459219, - "dismissed": false, - "source": "moonlight", - "feed": "Moonlight" - } - ], - "modified": false -} -``` - -#### Dismiss an announcement -Sets the dismiss flag of an announcement to `true`. The `entry_id` -field is required. The `entry_id` contains forward slashes so remember -to escape the ID if including it in the query string of an HTTP request. - -HTTP request: -```http -POST /server/announcements/dismiss?entry_id=arksine%2Fmoonlight%2Fissue%2F1&wake_time=600 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.announcements.dismiss", - "params": { - "entry_id": "arksine/moonlight/issue/1", - "wake_time": 600 - }, - "id": 4654 -} -``` - -Parameters: - -- `entry_id`: The entry identifier. This field may contain forward slashes so - it should be url escaped when placed in the query string of an http request. - This parameter is required. -- `wake_time`: The time, in seconds, in which the entry's `dismissed` state - will revert to false. This parameter is optional, if omitted the entry will - be dismissed indefinitely. - -Returns: - -The `entry_id` of the dismissed entry: - -```json -{ - "entry_id": "arksine/moonlight/issue/1" -} -``` - -#### List announcement feeds - -HTTP request: -```http -GET /server/announcements/feeds -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.announcements.feeds", - "id": 4654 -} -``` - -Returns: - -A list of feeds the instance of Moonraker is subscribed to. - -```json -{ - "feeds": [ - "moonraker", - "klipper" - ] -} -``` - -#### Add an announcement feed -Specifies a new feed for Moonraker's `announcements` component to query -in addition to `moonraker`, `klipper`, and feeds configured in -`moonraker.conf`. - -HTTP request: -```http -POST /server/announcements/feed?name=my_feed -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.announcements.post_feed", - "params": { - "name": "my_feed" - }, - "id": 4654 -} -``` - -Parameters: - -- `name`: The name of the new feed. This parameter is required. - -Returns: - -The name of the new feed and the action taken. The `action` will be `added` -if a new feed added, or `skipped` if the feed already exists. - -```json -{ - "feed": "my_feed", - "action": "added" -} -``` - -#### Remove an announcement feed -Removes a subscribed feed. Only feeds previously subscribed to using -the [add feed](#add-an-announcement-feed) API may be removed. Feeds -configured in `moonraker.conf` may not be removed. - -HTTP request: -```http -DELETE /server/announcements/feed?name=my_feed -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.announcements.delete_feed", - "params": { - "name": "my_feed" - }, - "id": 4654 -} -``` - -Parameters: - -- `name`: The name of the new feed to remove. This parameter is required. - -Returns: - -The name of the new feed and the action taken. The `action` will be -`removed` if the operation was successful. - -```json -{ - "feed": "my_feed", - "action": "removed" -} -``` - -### Webcam APIs - -The following APIs are available to manage webcam configuration: - -#### List Webcams - -HTTP request: -```http -GET /server/webcams/list -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.webcams.list", - "id": 4654 -} -``` - -Returns: - -A list of configured webcams: - -```json -{ - "webcams": [ - { - "name": "testcam3", - "location": "door", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 20, - "target_fps_idle": 5, - "stream_url": "http://camera.lan/webcam?action=stream", - "snapshot_url": "http://camera.lan/webcam?action=snapshot", - "flip_horizontal": false, - "flip_vertical": true, - "rotation": 90, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "config", - "uid": "55d3801e-fdc1-438d-8728-2fff8b83b909" - }, - { - "name": "tc2", - "location": "printer", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 15, - "target_fps_idle": 5, - "stream_url": "http://printer.lan/webcam?action=stream", - "snapshot_url": "http://printer.lan/webcam?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "database", - "uid": "65e51c8a-6763-41d4-8e76-345bb6e8e7c3" - }, - { - "name": "TestCam", - "location": "printer", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 15, - "target_fps_idle": 5, - "stream_url": "/webcam/?action=stream", - "snapshot_url": "/webcam/?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "database", - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - } - ] -} -``` - -#### Get Webcam Information - -HTTP request: -```http -GET /server/webcams/item?uid=341778f9-387f-455b-8b69-ff68442d41d9 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.webcams.get_item", - "params": { - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - }, - "id": 4654 -} -``` - -Parameters: - -- `uid`: The webcam's assigned unique ID. This parameter is optional, when - not specified the request will fallback to the `name` parameter. -- `name`: The name of the webcam to request information for. If the named - webcam is not available the request will return with an error. This - parameter must be provided when the `uid` is omitted. - -Returns: - -The full configuration for the requested webcam: - -```json -{ - "webcam": { - "name": "TestCam", - "location": "printer", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 15, - "target_fps_idle": 5, - "stream_url": "/webcam/?action=stream", - "snapshot_url": "/webcam/?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "database", - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - } -} -``` -#### Add or update a webcam - -Adds a new webcam entry or updates an existing entry. When updating -an entry only the fields provided will be modified. - -!!! Note - A webcam configured via `moonraker.conf` cannot be updated or - overwritten using this API. - -HTTP request: -```http -POST /server/webcams/item -Content-Type: application/json - -{ - "name": "cam_name", - "snapshot_url": "http://printer.lan:8080/webcam?action=snapshot", - "stream_url": "http://printer.lan:8080/webcam?action=stream" -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.webcams.post_item", - "params": { - "name": "cam_name", - "snapshot_url": "/webcam?action=snapshot", - "stream_url": "/webcam?action=stream" - }, - "id": 4654 -} -``` - -Parameters: - -- `uid`: The unique ID of the webcam. This parameter may be specified to - modify an existing webcam. New entries must omit the `uid`. -- `name`: The name of the camera to add or update. This parameter must - be provided for new entries. -- `location`: A description of the webcam location, ie: what the webcam is - observing. The default is `printer` for new entries. -- `icon`: The name of the icon to use for the camera. The default is `mdiWebcam` - for new entries. -- `enabled`: A boolean value to indicate if this webcam should be enabled. - Default is True for new entries. -- `service`: The name of the webcam application streaming service. The default - is "mjpegstreamer" for new entries. -- `target_fps`: The target framerate. The default is 15 for new entries. -- `target_fps_idle`: The target framerate when the printer is idle. - The default is 5 for new entries. -- `stream_url`: The url for the camera stream request. This may be a full url - or a url relative to Moonraker's host machine. If the url is relative it is - assumed that the stream is available over http on port 80. This parameter - must be provided for new entries. -- `snapshot_url`: The url for the camera snapshot request. This may be a full - url or a url relative to Moonraker's host machine. If the url is relative - it is assumed that the snapshot is available over http on port 80. The - default is an empty string for new entries. -- `flip_horizontal`: A boolean value indicating whether the stream should be - flipped horizontally. The default is false for new entries. -- `flip_vertical`: A boolean value indicating whether the stream should be - flipped vertically. The default is false for new entries. -- `rotation`: An integer value indicating the amount of clockwise rotation to - apply to the stream. May be 0, 90, 180, or 270. The default is 0 for new entries. -- `aspect_ratio`: The aspect ratio to display for the camera. Note that this option - is specific to certain services, otherwise it is ignored. The default is `4:3` - for new entries. -- `extra_data`: Additional webcam data set by the front end in the form of a json - object. This may be used to store any additional webcam options and/or data. The - default is an empty object for new entries. - -!!! Tip - When modifying existing entries it is possible to rename an existing item by - specifying its current `uid` and a new value for `name`. Keep in mind that - names must be unique, an attempt to rename an existing webcam to another name - that is reserved will result in an error. - -Returns: - -The full configuration for the added/updated webcam: - -```json -{ - "webcam": { - "name": "TestCam", - "location": "printer", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 15, - "target_fps_idle": 5, - "stream_url": "/webcam/?action=stream", - "snapshot_url": "/webcam/?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "database", - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - } -} -``` - -#### Delete a webcam - -!!! Note - A webcam configured via `moonraker.conf` cannot be deleted - using this API. - -HTTP request: -```http -DELETE /server/webcams/item?uid=341778f9-387f-455b-8b69-ff68442d41d9 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.webcams.delete_item", - "params": { - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - }, - "id": 4654 -} -``` - -Parameters: - -- `uid`: The webcam's assigned unique ID. This parameter is optional, when - not specified the request will fallback to the `name` parameter. -- `name`: The name of the webcam to delete. If the named webcam is not - available the request will return with an error. This parameter must - be provided when the `uid` is omitted. - -Returns: - -The full configuration of the deleted webcam: - -```json -{ - "webcam": { - "name": "TestCam", - "location": "printer", - "service": "mjpegstreamer", - "target_fps": 15, - "stream_url": "/webcam/?action=stream", - "snapshot_url": "/webcam/?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "source": "database", - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - } -} -``` - -#### Test a webcam - -Resolves a webcam's stream and snapshot urls. If the snapshot -is served over http, a test is performed to see if the url is -reachable. - -HTTP request: -```http -POST /server/webcams/test?uid=341778f9-387f-455b-8b69-ff68442d41d9 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.webcams.test", - "params": { - "uid": "341778f9-387f-455b-8b69-ff68442d41d9" - }, - "id": 4654 -} -``` - -Parameters: - -- `uid`: The webcam's assigned unique ID. This parameter is optional, when - not specified the request will fallback to the `name` parameter. -- `name`: The name of the webcam to test. If the named webcam is not - available the request will return with an error. This parameter must - be provided when the `uid` is omitted. - -Returns: Test results in the following format - -```json -{ - "name": "TestCam", - "snapshot_reachable": true, - "snapshot_url": "http://127.0.0.1:80/webcam/?action=snapshot", - "stream_url": "http://127.0.0.1:80/webcam/?action=stream" -} -``` - -### Notifier APIs -The following APIs are available to view and tests notifiers. - -#### List Notifiers - -HTTP request: -```http -GET /server/notifiers/list -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.notifiers.list", - "id": 4654 -} -``` - -Returns: - -A list of configured notifiers: - -```json -{ - "notifiers": [ - { - "name": "print_start", - "url": "tgram://{bottoken}/{ChatID}", - "events": [ - "started" - ], - "body": "Your printer started printing '{event_args[1].filename}'", - "title": null, - "attach": null - }, - { - "name": "print_complete", - "url": "tgram://{bottoken}/{ChatID}", - "events": [ - "complete" - ], - "body": "Your printer completed printing '{event_args[1].filename}", - "title": null, - "attach": "http://192.168.1.100/webcam/?action=snapshot" - }, - { - "name": "print_error", - "url": "tgram://{bottoken}/{ChatID}", - "events": [ - "error" - ], - "body": "{event_args[1].message}", - "title": null, - "attach": "http://192.168.1.100/webcam/?action=snapshot" - } - ] -} -``` - -### Update Manager APIs -The following endpoints are available when the `[update_manager]` component has -been configured: - -#### Get update status -Retrieves the current state of each item available for update. Items may -include the linux package manager (`system`), applications such as `moonraker` and -`klipper`, web clients such as `mainsail` and `fluidd`, and other configured -applications/extensions. - -HTTP request: -```http -GET /machine/update/status?refresh=false -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.status", - "params": { - "refresh": false - }, - "id": 4644 -} -``` - -Parameters: - -- `refresh`: (Optional) When set to true state for all updaters will be refreshed. - The default is `false`. A request to refresh is aborted under the following - conditions: - - An update is in progress - - A print is in progress - - The update manager hasn't completed initialization - - A previous refresh has occured within the last 60 seconds - -!!! Note - The `refresh` parameter is deprecated. Client developers should use the - [refresh endpoint](#refresh-application-state) to request a refresh. - -Returns: - -Status information for each update package. Note that `mainsail` -and `fluidd` are present as clients configured in `moonraker.conf` -```json -{ - "busy": false, - "github_rate_limit": 60, - "github_requests_remaining": 57, - "github_limit_reset_time": 1615836932, - "version_info": { - "system": { - "package_count": 4, - "package_list": [ - "libtiff5", - "raspberrypi-sys-mods", - "rpi-eeprom-images", - "rpi-eeprom" - ] - }, - "moonraker": { - "channel": "dev", - "debug_enabled": true, - "is_valid": true, - "configured_type": "git_repo", - "corrupt": false, - "info_tags": [], - "detected_type": "git_repo", - "remote_alias": "arksine", - "branch": "master", - "owner": "?", - "repo_name": "moonraker", - "version": "v0.7.1-364", - "remote_version": "v0.7.1-364", - "rollback_version": "v0.7.1-360", - "current_hash": "ecfad5cff15fff1d82cb9bdc64d6b548ed53dfaf", - "remote_hash": "ecfad5cff15fff1d82cb9bdc64d6b548ed53dfaf", - "is_dirty": false, - "detached": true, - "commits_behind": [], - "git_messages": [], - "full_version_string": "v0.7.1-364-gecfad5c", - "pristine": true, - "recovery_url": "https://github.com/Arksine/moonraker.git", - "remote_url": "https://github.com/Arksine/moonraker.git", - "warnings": [], - "anomalies": [ - "Unofficial remote url: https://github.com/Arksine/moonraker-fork.git", - "Repo not on offical remote/branch, expected: origin/master, detected: altremote/altbranch", - "Detached HEAD detected" - ] - }, - "mainsail": { - "name": "mainsail", - "owner": "mainsail-crew", - "version": "v2.1.1", - "remote_version": "v2.1.1", - "rollback_version": "v2.0.0", - "configured_type": "web", - "channel": "stable", - "info_tags": [ - "desc=Mainsail Web Client", - "action=some_action" - ], - "warnings": [], - "anomalies": [], - "is_valid": true - }, - "fluidd": { - "name": "fluidd", - "owner": "fluidd-core", - "version": "v1.16.2", - "remote_version": "v1.16.2", - "rollback_version": "v1.15.0", - "configured_type": "web", - "channel": "beta", - "info_tags": [], - "warnings": [], - "anomalies": [], - "is_valid": true - }, - "klipper": { - "channel": "dev", - "debug_enabled": true, - "is_valid": true, - "configured_type": "git_repo", - "corrupt": false, - "info_tags": [], - "detected_type": "git_repo", - "remote_alias": "origin", - "branch": "master", - "owner": "Klipper3d", - "repo_name": "klipper", - "version": "v0.10.0-1", - "remote_version": "v0.10.0-41", - "rollback_version": "v0.9.1-340", - "current_hash": "4c8d24ae03eadf3fc5a28efb1209ce810251d02d", - "remote_hash": "e3cbe7ea3663a8cd10207a9aecc4e5458aeb1f1f", - "is_dirty": false, - "detached": false, - "commits_behind": [ - { - "sha": "e3cbe7ea3663a8cd10207a9aecc4e5458aeb1f1f", - "author": "Kevin O'Connor", - "date": "1644534721", - "subject": "stm32: Clear SPE flag on a change to SPI CR1 register", - "message": "The stm32 specs indicate that the SPE bit must be cleared before\nchanging the CPHA or CPOL bits.\n\nReported by @cbc02009 and @bigtreetech.\n\nSigned-off-by: Kevin O'Connor ", - "tag": null - }, - { - "sha": "99d55185a21703611b862f6ce4b80bba70a9c4b5", - "author": "Kevin O'Connor", - "date": "1644532075", - "subject": "stm32: Wait for transmission to complete before returning from spi_transfer()", - "message": "It's possible for the SCLK pin to still be updating even after the\nlast byte of data has been read from the receive pin. (In particular\nin spi mode 0 and 1.) Exiting early from spi_transfer() in this case\ncould result in the CS pin being raised before the final updates to\nSCLK pin.\n\nAdd an additional wait at the end of spi_transfer() to avoid this\nissue.\n\nSigned-off-by: Kevin O'Connor ", - "tag": null - } - ], - "git_messages": [], - "full_version_string": "v0.10.0-1-g4c8d24ae-shallow", - "pristine": true, - "recovery_url": "https://github.com/Klipper3d/klipper.git", - "remote_url": "https://github.com/Klipper3d/klipper.git", - "warnings": [], - "anomalies": [] - } - } -} -``` -Below is an explanation for each field: - -- `busy`: set to true if an update is in progress. Moonraker will not - allow concurrent updates. -- `github_rate_limit`: the maximum number of github API requests - the user currently is allowed. An unauthenticated user typically has 60 - requests per hour. -- `github_requests_remaining`: the number of API request the user - currently has remaining. -- `github_limit_reset_time`: the time when the rate limit will reset, - reported as seconds since the epoch (aka Unix Time). - -Extensions configured with the `git_repo` type will contain the following -fields: - -- `configured_type`: the application type configured by the user -- `detected_type`: the application type as detected by Moonraker. -- `channel`: the currently configured update channel. For Moonraker - and Klipper this is set in the `[update_manager]` configuration. - For clients the channel is determined by the configured type -- `pristine`: Indicates that there are no modified files or untracked - source files in a `git_repo`. A repo with untracked files can still - be updated, however a repo with modified files (ie: `dirty`) cannot - be updated. -- `owner`: the owner of the repo / application -- `branch`: the name of the current git branch. This should typically - be "master". -- `remote_alias`: the alias for the remote. This should typically be - "origin". -- `version`: abbreviated version of the current repo on disk -- `remote_version`: abbreviated version of the latest available update -- `rollback_version`: version the repo will revert to when a rollback is - requested -- `full_version_string`: The complete version string of the current repo. -- `current_hash`: hash of the most recent commit on disk -- `remote_hash`: hash of the most recent commit pushed to the remote -- `is_valid`: true if the `git_repo` is valid and can be updated. -- `corrupt`: Indicates that the git repo has been corrupted. When a repo - is in this state it a hard recovery (ie: re-cloning the repo) is necessary. - Note that the most common cause of repo corruption is removing power from - the host machine without safely shutting down. Damaged storage can also - lead to repo corruption. -- `is_dirty`: true if a `git_repo` has modified files. A dirty repo cannot - be updated. -- `detached`: true if the `git_repo` is currently in a detached state. -- `debug_enabled`: True when debug flag has been set via the command line. - When debug is enabled Moonraker will allow detached updates. -- `commits_behind`: A list of commits behind. Up to 30 "untagged" commits - will be reported. Moonraker checks the last 100 commits for tags, any - commits beyond the last 30 with a tag will also be reported. -- `git_messages`: If a repo is in the "invalid" state this field will hold - a list of string messages containing the output of the last failed git - command. Note that it is possible for a git command to fail without - providing output (for example, it may become non-responsive and time out), - so it is possible for this field to be an empty list when the repo is - invalid. -- `info_tags`: These are tags defined in the `[update_manager client_name]` - configuration for each client. Client developers my define what tags, - if any, users will configure. They can choose to use those tags to display - information or perform an additional action after an update if necessary. -- `recovery_url`: The url Moonraker will use to re-clone the repo when a - hard recovery is requested. If this reports a "?" then a hard recovery is - not possible. -- `remote_url`: The url for the currently configured remote. -- `warnings`: An array of strings that describe warnings detected during - repo init. These warnings provide additional context when the `is_valid` - field reports `true`. -- `anomalies`: An array of strings that describe anomalies found during - initialization. An anomaly can be defined as an unexpected condition, they - will not result in an invalid state, nor will they prevent an update. For - example, when the detected remote url does not match the configured/expected - url Moonraker will fall back to the detected url and report this condition - as an anomaly. - -Extensions configured with the `web` type will contain the following fields: - -- `channel`: channel to fetch updates from -- `configured_type`: will be `web` -- `name`: name of the configured client -- `owner`: the owner of the client -- `version`: version of the installed client. -- `remote_version`: version of the latest release published to GitHub -- `rollback_version`: version the client will revert to when a rollback is - requested -- `info_tags`: These are tags defined in the `[update_manager client_name]` - configuration for each client. Client developers my define what tags, - if any, users will configure. They can choose to use those tags to display - information or perform an additional action after an update if necessary. -- `is_valid`: A boolean that reports true if an update is possible, false - if an update cannot be performed. -- `warnings`: An array of strings that describe warnings detected during - updater init. These warnings add context when the `is_valid` field reports - `true`. -- `anomalies`: An array of strings that describe anomalies found during - initialization. An anomaly can be defined as an unexpected condition, they - will not result in an invalid state, nor will they prevent an update. - For example, when the configured repo to check for updates does not match - the detected repo Moonraker will fall back to the detected repo and report - this condition as an anomaly. - - -The `system` object contains the following fields: - -- `package_count`: the number of system packages available for update -- `package_list`: an array containing the names of packages available - for update - -#### Refresh update status - -Refreshes the internal update state for the requested item(s). - -HTTP request: -```http -POST /machine/update/refresh?name=klipper -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.refresh", - "params": { - "name": "klipper" - }, - "id": 4644 -} -``` - -Parameters: - -- `name`: (Optional) The name of the specified application. If omitted - all registered applications will be refreshed. - -Returns: - -An object containing full update status matching the response in the -[status endpoint](#get-update-status). - -!!! Note - This endpoint will raise 503 error under the following conditions: - - - An update is in progress - - A print is in progress - - The update manager hasn't completed initialization - -!!! Warning - Applications should use care when calling this method as a refresh - is CPU intensive and may be time consuming. Moonraker can be - configured to refresh state periodically, thus it is recommended - that applications avoid their own procedural implementations. - Instead it is best to call this API only when a user requests a - refresh. - -#### Perform a full update -Attempts to update all configured items in Moonraker. Updates are -performed in the following order: - -- `system` if enabled -- All configured clients -- Klipper -- Moonraker - -HTTP request: -```http -POST /machine/update/full -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.full", - "id": 4645 -} -``` -Returns: - -`ok` when complete - - -#### Update Moonraker -Pulls the most recent version of Moonraker from GitHub and restarts -the service. If an update is requested while a print is in progress then -this request will return an error. - -HTTP request: -```http -POST /machine/update/moonraker -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.moonraker", - "id": 4645 -} -``` -Returns: - -`ok` when complete - -#### Update Klipper -Pulls the most recent version of Klipper from GitHub and restarts -the service. If an update is requested while a print is in progress -then this request will return an error. - -HTTP request: -```http -POST /machine/update/klipper -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.klipper", - "id": 5745 -} -``` -Returns: - -`ok` when complete - -#### Update Client -If one more more `[update_manager client client_name]` sections have -been configured this endpoint can be used to install the most recently -published release of the client. If an update is requested while a -print is in progress then this request will return an error. The -`name` argument is required, it's value should match the `client_name` -of the configured section. - -HTTP request: -```http -POST /machine/update/client?name={client_name} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.client", - "params": { - "name": "client_name" - }, - "id": 8546 -} -``` -Returns: - -`ok` when complete - -#### Update System Packages -Upgrades system packages. Currently only `apt-get` is supported. -If an update is requested while a print is in progress then this request -will return an error. - -HTTP request: -```http -POST /machine/update/system -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.system", - "id": 4564 -} -``` -Returns: - -`ok` when complete - -#### Recover a corrupt repo -On occasion a git command may fail resulting in a repo in a -dirty or invalid state. When this happens it is possible -to recover. The `name` argument must specify the name of -the repo to recover, it must be of a git repo type. There are two -methods of recovery, the `hard` argument determines which method -is used: - -- `hard == true`: Moonraker will remove the old directory - entirely. It will then attempt to recover with `rsync` - by restoring a backup of a recent valid repo. -- `hard == false`: Will run `git clean -f -d` followed - by `git reset --hard {remote}/{branch}`. This is useful - for recovering dirty repos that are valid. It is possible - that this will work on an invalid repo, however it will - not work on a corrupt repo. - -The `hard` argument defaults to `false`. - -HTTP request: -```http -POST /machine/update/recover?name=moonraker&hard=false -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.recover", - "params": { - "name": "moonraker", - "hard": false - }, - "id": 4564 -} -``` -Returns: - -`ok` when complete - -#### Rollback to the previous version - -HTTP request: - -```http -POST /machine/update/rollback?name=moonraker -``` - -JSON-RPC request: - -```json -{ - "jsonrpc": "2.0", - "method": "machine.update.rollback", - "params": { - "name": "moonraker" - }, - "id": 4564 -} -``` - -Returns: - -`ok` when complete - -### Power APIs -The APIs below are available when the `[power]` component has been configured. - -#### Get Device List -HTTP request: -```http -GET /machine/device_power/devices -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.device_power.devices", - "id": 5646 -} -``` -Returns: - -An array of objects containing info for each configured device. -```json -{ - "devices": [ - { - "device": "green_led", - "status": "off", - "locked_while_printing": true, - "type": "gpio" - }, - { - "device": "printer", - "status": "off", - "locked_while_printing": false, - "type": "tplink_smartplug" - } - ] -} -``` - -#### Get Device Status -Returns the status for a single configured device. - -HTTP request: -```http -GET /machine/device_power/device?device=green_led -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.device_power.get_device", - "params": { - "device": "green_led" - }, - "id": 4564 -} -``` -Returns: - -An object containing power state for the requested device: -```json -{ - "green_led": "off" -} -``` - -#### Set Device State -Toggle, turn on, or turn off a specified device. - -HTTP request: -```http -POST /machine/device_power/device?device=green_led&action=on -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.device_power.post_device", - "params": { - "device": "green_led", - "action": "on" - }, - "id": 4564 -} -``` - -!!! note - The `action` argument may be `on`, `off`, or `toggle`. Any - other value will result in an error. - -Returns: - -An object containing new power state for the requested device: -```json -{ - "green_led": "off" -} -``` - -#### Get Batch Device Status -Get power status for the requested devices. At least one device must be -specified. - -HTTP request: -```http -GET /machine/device_power/status?dev_one&dev_two -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.device_power.status", - "params": { - "dev_one":null, - "dev_two": null - }, - "id": 4564 -} -``` -Returns: - -An object containing power state for each requested device: -```json -{ - "green_led": "off", - "printer": "off" -} -``` - -#### Batch Power On Devices -Power on the requested devices. At least one device must be -specified. - -HTTP request: -```http -POST /machine/device_power/on?dev_one&dev_two -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.device_power.on", - "params": { - "dev_one":null, - "dev_two": null - }, - "id": 4564 -} -``` -An object containing power state for each requested device: -```json -{ - "green_led": "on", - "printer": "on" -} -``` - -#### Batch Power Off Devices -Power off the requested devices. At least one device must be -specified. - -HTTP request: -```http -POST /machine/device_power/off?dev_one&dev_two -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.device_power.off", - "params": { - "dev_one": null, - "dev_two": null - }, - "id": 4564 -} -``` -An object containing power state for each requested device: -```json -{ - "green_led": "off", - "printer": "off" -} -``` -### WLED APIs -The APIs for WLED are available when the `[wled]` component has been configured. For lower-level control of wled consider using the WLED [JOSN API](https://kno.wled.ge/interfaces/json-api/) directly. - -#### Get strips -HTTP request: -```http -GET /machine/wled/strips -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.strips", - "id": 7123 -} -``` -Returns: - -Strip information for all wled strips. -```json -{ - "strips": { - "lights": { - "strip": "lights", - "status": "on", - "chain_count": 79, - "preset": -1, - "brightness": 255, - "intensity": -1, - "speed": -1, - "error": null - }, - "desk": { - "strip": "desk", - "status": "on", - "chain_count": 60, - "preset": 8, - "brightness": -1, - "intensity": -1, - "speed": -1, - "error": null - } - } -} -``` - -#### Get strip status -HTTP request: -```http -GET /machine/wled/status?strip1&strip2 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.status", - "params": { - "lights": null, - "desk": null - }, - "id": 7124 -} -``` -Returns: - -Strip information for requested strips. -```json -{ - "lights": { - "strip": "lights", - "status": "on", - "chain_count": 79, - "preset": -1, - "brightness": 255, - "intensity": -1, - "speed": -1, - "error": null - }, - "desk": { - "strip": "desk", - "status": "on", - "chain_count": 60, - "preset": 8, - "brightness": -1, - "intensity": -1, - "speed": -1, - "error": null - } -} -``` - -#### Turn strip on -Turns the specified strips on to the initial colors or intial preset. - -HTTP request: -```http -POST /machine/wled/on?strip1&strip2 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.on", - "params": { - "lights": null, - "desk": null - }, - "id": 7125 -} -``` -Returns: - -Strip information for requested strips. -```json -{ - "lights": { - "strip": "lights", - "status": "on", - "chain_count": 79, - "preset": -1, - "brightness": 255, - "intensity": -1, - "speed": -1, - "error": null - }, - "desk": { - "strip": "desk", - "status": "on", - "chain_count": 60, - "preset": 8, - "brightness": -1, - "intensity": -1, - "speed": -1, - "error": null - } -} -``` - -#### Turn strip off -Turns off all specified strips. - -HTTP request: -```http -POST /machine/wled/off?strip1&strip2 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.off", - "params": { - "lights": null, - "desk": null - }, - "id": 7126 -} -``` -Returns: - -The new state of the specified strips. -```json -{ - "lights": { - "strip": "lights", - "status": "off", - "chain_count": 79, - "preset": -1, - "brightness": 255, - "intensity": -1, - "speed": -1, - "error": null - }, - "desk": { - "strip": "desk", - "status": "off", - "chain_count": 60, - "preset": 8, - "brightness": -1, - "intensity": -1, - "speed": -1, - "error": null - } -} -``` - -#### Toggle strip on/off state -Turns each strip off if it is on and on if it is off. - -HTTP request: -```http -POST /machine/wled/toggle?strip1&strip2 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.toggle", - "params": { - "lights": null, - "desk": null - }, - "id": 7127 -} -``` -Returns: - -The new state of the specified strips. -```json -{ - "lights": { - "strip": "lights", - "status": "on", - "chain_count": 79, - "preset": -1, - "brightness": 255, - "intensity": -1, - "speed": -1, - "error": null - }, - "desk": { - "strip": "desk", - "status": "off", - "chain_count": 60, - "preset": 8, - "brightness": -1, - "intensity": -1, - "speed": -1, - "error": null - } -} -``` - -#### Control individual strip state -Toggle, turn on, turn off, turn on with preset, turn on with brightness, or -turn on preset will some of brightness, intensity, and speed. Or simply set -some of brightness, intensity, and speed. - -HTTP requests: - -Turn strip `lights` off -```http -POST /machine/wled/strip?strip=lights&action=off -``` - -Turn strip `lights` on to the initial colors or intial preset. -```http -POST /machine/wled/strip?strip=lights&action=on -``` - -Turn strip `lights` on activating preset 3. -```http -POST /machine/wled/strip?strip=lights&action=on&preset=3 -``` - -Turn strip `lights` on activating preset 3 while specifying speed and -intensity. -```http -POST /machine/wled/strip?strip=lights&action=on&preset=3&intensity=50&speed=255 -``` - -Change strip `lights` brightness (if on) and speed (if a preset is active). -```http -POST /machine/wled/strip?strip=lights&action=control&brightness=99&speed=50 -``` - -JSON-RPC request: - -Returns information for the specified strip. -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.get_strip", - "params": { - "strip": "lights" - }, - "id": 7128 -} -``` - -Calls the action with the arguments for the specified strip. -```json -{ - "jsonrpc": "2.0", - "method": "machine.wled.post_strip", - "params": { - "strip": "lights", - "action": "on", - "preset": 1, - "brightness": 255, - "intensity": 255, - "speed": 255 - }, - "id": 7129 -} -``` -!!! note - The `action` argument may be `on`, `off`, `toggle` or `control`. Any - other value will result in an error. - -The `intensity` and `speed` arguments are only used if a preset is active. -Permitted ranges are 1-255 for `brightness` and 0-255 for `intensity` and -`speed`. When action is `on` a `preset` with some or all of `brightness`, -`intensity` and `speed` may also be specified. If the action `control` is used -one or all of `brightness`, `intensity`, and `speed` must be specified. - -Returns: - -State of the strip. -```json -{ - "lights": { - "strip": "lights", - "status": "on", - "chain_count": 79, - "preset": 1, - "brightness": 50, - "intensity": 255, - "speed": 255, - "error": null - } -} -``` - -### Sensor APIs -The APIs below are available when the `[sensor]` component has been configured. - -#### Get Sensor List -HTTP request: -```http -GET /server/sensors/list?extended=False -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.sensors.list", - "params": { - "extended": false - } - "id": 5646 -} -``` - -Parameters: - -- `extended`: When set to `true` then each sensor will also include - parameter info and history field configuration. The default is false. - - -Returns: - -An array of objects containing info for each configured sensor. The -`parameter_info` and `history_fields` items will only be present when -the `extended` parameter is set to true. - -```json -{ - "sensors": { - "sensor1": { - "id": "sensor1", - "friendly_name": "Sensor 1", - "type": "mqtt", - "values": { - "value1": 0, - "value2": 119.8 - }, - "parameter_info": [ - { - "units": "kWh", - "name": "value1" - }, - { - "units": "V", - "name": "value2" - } - ], - "history_fields": [ - { - "field": "power_consumption", - "provider": "sensor sensor1", - "description": "Printer Power Consumption", - "strategy": "delta", - "units": "kWh", - "init_tracker": true, - "exclude_paused": false, - "report_total": true, - "report_maximum": true, - "precision": 6, - "parameter": "value1" - }, - { - "field": "max_voltage", - "provider": "sensor sensor1", - "description": "Maximum voltage", - "strategy": "maximum", - "units": "V", - "init_tracker": true, - "exclude_paused": false, - "report_total": false, - "report_maximum": false, - "precision": 6, - "parameter": "value2" - } - ] - } - } -} -``` - -#### Get Sensor Information -Returns the status for a single configured sensor. - -HTTP request: -```http -GET /server/sensors/info?sensor=sensor1&extended=false -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.sensors.info", - "params": { - "sensor": "sensor1", - "extended": false - }, - "id": 4564 -} -``` - -Parameters: - -- `extended`: When set to `true` then the response will also include - parameter info and history field configuration. The default is false. - - -Returns: - -An object containing sensor information for the requested sensor. The -`parameter_info` and `history_fields` items will only be present when -the `extended` parameter is set to true. - -```json -{ - "id": "sensor1", - "friendly_name": "Sensor 1", - "type": "mqtt", - "values": { - "value1": 0.0, - "value2": 120.0 - }, - "parameter_info": [ - { - "units": "kWh", - "name": "value1" - }, - { - "units": "V", - "name": "value2" - } - ], - "history_fields": [ - { - "field": "power_consumption", - "provider": "sensor sensor1", - "description": "Printer Power Consumption", - "strategy": "delta", - "units": "kWh", - "init_tracker": true, - "exclude_paused": false, - "report_total": true, - "report_maximum": true, - "precision": 6, - "parameter": "value1" - }, - { - "field": "max_voltage", - "provider": "sensor sensor1", - "description": "Maximum voltage", - "strategy": "maximum", - "units": "V", - "init_tracker": true, - "exclude_paused": false, - "report_total": false, - "report_maximum": false, - "precision": 6, - "parameter": "value2" - } - ] -} -``` - -#### Get Sensor Measurements -Returns all recorded measurements for a configured sensor. - -HTTP request: -```http -GET /server/sensors/measurements?sensor=sensor1 -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.sensors.measurements", - "params": { - "sensor": "sensor1" - }, - "id": 4564 -} -``` -Returns: - -An object containing all recorded measurements for the requested sensor: -```json -{ - "sensor1": { - "value1": [ - 3.1, - 3.2, - 3.0 - ], - "value2": [ - 120.0, - 120.0, - 119.9 - ] - } -} -``` - -#### Get Batch Sensor Measurements -Returns recorded measurements for all sensors. - -HTTP request: -```http -GET /server/sensors/measurements -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.sensors.measurements", - "id": 4564 -} -``` -Returns: - -An object containing all measurements for every configured sensor: -```json -{ - "sensor1": { - "value1": [ - 3.1, - 3.2, - 3.0 - ], - "value2": [ - 120.0, - 120.0, - 119.9 - ] - }, - "sensor2": { - "value_a": [ - 1, - 1, - 0 - ] - } -} -``` - -### Spoolman APIs -The following APIs are available to interact with the Spoolman integration: - -#### Get Spoolman Status -Returns the current status of the spoolman module. - -HTTP request: -```http -GET /server/spoolman/status -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.spoolman.status", - "id": 4654 -} -``` - -Returns: - -An object containing details about the current status: - -```json -{ - "spoolman_connected": false, - "pending_reports": [ - { - "spool_id": 1, - "filament_used": 10 - } - ], - "spool_id": 2 -} -``` - -- `spoolman_connected`: A boolean indicating if Moonraker is connected to - Spoolman. When `false` Spoolman is unavailable. -- `pending_reports`: A list of objects containing spool data that has - yet to be reported to Spoolman. -- `spool_id`: The current Spool ID. Can be an integer value or `null`. - -#### Set active spool -Set the ID of the spool that Moonraker should report usage to Spoolman of. - -HTTP request: -```http -POST /server/spoolman/spool_id -Content-Type: application/json - -{ - "spool_id": 1 -} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.spoolman.post_spool_id", - "params": { - "spool_id": 1 - }, - "id": 4654 -} -``` - -Returns: - -The id of the now active spool: - -```json -{ - "spool_id": 1 -} -``` - -!!! note - Send an empty object, `{}`, to un-set the spool ID and stop any reporting. - The response `spool_id` will then be set to *null* - -#### Get active spool -Retrieve the ID of the spool to which Moonraker reports usage for Spoolman. - -HTTP request: -```http -GET /server/spoolman/spool_id -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.spoolman.get_spool_id", - "id": 4654 -} -``` - -Returns: - -The id of the active spool: - -```json -{ - "spool_id": 1 -} -``` - -!!! note - The `spool_id` can be *null* if there is no active spool. - -#### Proxy - -Moonraker supplies a proxy endpoint where you have full access to the Spoolman -API without having to configure the endpoint yourself. - -See Spoolman's [OpenAPI Description](https://donkie.github.io/Spoolman/) for -detailed information about it's API. - -HTTP request: -```http -POST /server/spoolman/proxy -Content-Type: application/json - -{ - "request_method": "POST", - "path": "/v1/spool", - "query": "a=1&b=4", - "body": { - "filament_id": 1 - } -} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.spoolman.proxy", - "params": { - "use_v2_response": true, - "request_method": "POST", - "path": "/v1/spool", - "query": "a=1&b=4", - "body": { - "filament_id": 1 - } - }, - "id": 4654 -} -``` - -The following parameters are available. `request_method` and `path` are required, the rest are optional. - -- `request_method`: The HTTP request method, e.g. `GET`, `POST`, `DELETE`, etc.. -- `path`: The endpoint, including API version, e.g. `/v1/filament`. -- `query`: The query part of the URL, e.g. `filament_material=PLA&vendor_name=Prima`. -- `body`: The request body for the request. -- `use_v2_response`: Returns the spoolman response in version 2 format. - Default is false. - -!!! Note - The version 2 response has been added to eliminate ambiguity between - Spoolman errors and Moonraker errors. With version 1 a frontend - is not able to reliably to determine if the error is sourced from - Spoolman or Moonraker. Version 2 responses will return success - unless Moonraker is the source of the error. - - The version 2 response is currently opt-in to avoid breaking - existing implementations, however in the future it will be - required, at which point the version 1 response will be removed. - The version 1 response is now deprecated. - -Returns: - -- Version 1 - -> The json response from the Spoolman server. Errors are proxied directly. -For example, if a request returns 404, Moonraker will return a 404 error -or the JSON-RPC equivalent of -32601, Method Not Found. - -- Version 2 - -> Returns the spoolman response wrapped in an object. The object contains -two fields, `error` and `response`. A successful request will place the -returned value in the `response` field and `error` will be `null.` When -Spoolman returns an error the `response` field will be `null` and the -`error` field will contain details about the error. -```json -{ - "response": { - "id": 2, - "registered": "2023-11-23T12:18:31Z", - "first_used": "2023-11-22T12:17:56.123000Z", - "last_used": "2023-11-23T10:17:59.900000Z", - "filament": { - "id": 2, - "registered": "2023-11-23T12:17:44Z", - "name": "Reactor Red", - "vendor": { - "id": 2, - "registered": "2023-06-26T21:00:42Z", - "name": "Fusion" - }, - "material": "PLA", - "price": 25, - "density": 1.24, - "diameter": 1.75, - "weight": 1000, - "color_hex": "BD0B0B" - }, - "remaining_weight": 950, - "used_weight": 50, - "remaining_length": 318519.4384459262, - "used_length": 16764.18097083822, - "archived": false - }, - "error": null -} -``` -> On Spoolman error: -```json -{ - "response": null, - "error": { - "status_code": 404, - "message": "No spool with ID 3 found." - } -} -``` - - -### OctoPrint API emulation -Partial support of OctoPrint API is implemented with the purpose of -allowing uploading of sliced prints to a moonraker instance. -Currently we support Slic3r derivatives and Cura with Cura-OctoPrint. - -#### Version information -HTTP request: -```http -GET /api/version -``` -JSON-RPC request: Not Available - -Returns: - -An object containing simulated OctoPrint version information -```json -{ - "server": "1.5.0", - "api": "0.1", - "text": "OctoPrint (Moonraker v0.3.1-12)" -} -``` - -#### Server status -HTTP request: -```http -GET /api/server -``` -JSON-RPC request: Not Available - -Returns: - -An object containing simulated OctoPrint server status -```json -{ - "server": "1.5.0", - "safemode": "settings" -} -``` - -#### Login verification & User information -HTTP request: -```http -GET /api/login -``` -JSON-RPC request: Not Available - -Returns: - -An object containing stubbed OctoPrint login/user verification -```json -{ - "_is_external_client": false, - "_login_mechanism": "apikey", - "name": "_api", - "active": true, - "user": true, - "admin": true, - "apikey": null, - "permissions": [], - "groups": ["admins", "users"] -} -``` - -#### Get settings -HTTP request: -```http -GET /api/settings -``` -JSON-RPC request: Not Available - -Returns: - -An object containing stubbed OctoPrint settings. -The webcam route is hardcoded to Fluidd/Mainsail default path. -We say we have the UFP plugin installed so that Cura-OctoPrint will -upload in the preferred UFP format. -```json -{ - "plugins": { - "UltimakerFormatPackage": { - "align_inline_thumbnail": false, - "inline_thumbnail": false, - "inline_thumbnail_align_value": "left", - "inline_thumbnail_scale_value": "50", - "installed": true, - "installed_version": "0.2.2", - "scale_inline_thumbnail": false, - "state_panel_thumbnail": true - } - }, - "feature": { - "sdSupport": false, - "temperatureGraph": false - }, - "webcam": { - "flipH": false, - "flipV": false, - "rotate90": false, - "streamUrl": "/webcam/?action=stream", - "webcamEnabled": true - } -} -``` - -#### OctoPrint File Upload -HTTP request: -```http -POST /api/files/local -``` -JSON-RPC request: Not Available - -Alias for Moonraker's [file upload API](#file-upload). - -#### Get Job status -HTTP request: -```http -GET /api/job -``` -JSON-RPC request: Not Available - -Returns: - -An object containing stubbed OctoPrint Job status -```json -{ - "job": { - "file": {"name": null}, - "estimatedPrintTime": null, - "filament": {"length": null}, - "user": null - }, - "progress": { - "completion": null, - "filepos": null, - "printTime": null, - "printTimeLeft": null, - "printTimeOrigin": null - }, - "state": "Offline" -} -``` - -#### Get Printer status -HTTP request: -```http -GET /api/printer -``` -JSON-RPC request: Not Available - -Returns: - -An object containing OctoPrint Printer status -```json -{ - "temperature": { - "tool0": { - "actual": 22.25, - "offset": 0, - "target": 0 - }, - "bed": { - "actual": 22.25, - "offset": 0, - "target": 0 - }, ... - }, - "state": { - "text": "state", - "flags": { - "operational": true, - "paused": false, - "printing": false, - "cancelling": false, - "pausing": false, - "error": false, - "ready": false, - "closedOrError": false - } - } -} -``` - -#### Send GCode command -HTTP request: -```http -POST /api/printer/command -Content-Type: application/json - -{ - "commands": ["G28"] -} -``` -JSON-RPC request: Not Available - -Returns: - -An empty JSON object -```json -{} -``` - -#### List Printer profiles -HTTP request: -```http -GET /api/printerprofiles -``` -JSON-RPC request: Not Available - -Returns: - -An object containing simulates OctoPrint Printer profile -```json -{ - "profiles": { - "_default": { - "id": "_default", - "name": "Default", - "color": "default", - "model": "Default", - "default": true, - "current": true, - "heatedBed": true, - "heatedChamber": false - } - } -} -``` - -### History APIs -The APIs below are available when the `[history]` component has been configured. - -#### Get job list -HTTP request: -```http -GET /server/history/list?limit=50&start=50&since=1&before=5&order=asc -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.history.list", - "params":{ - "limit": 50, - "start": 10, - "since": 464.54, - "before": 1322.54, - "order": "asc" - }, - "id": 5656 -} -``` - -All arguments are optional. Arguments are as follows: - -- `start` Record number to start from (i.e. 10 would start at the 10th print) -- `limit` Maximum Number of prints to return (default: 50) -- `before` All jobs before this UNIX timestamp -- `since` All jobs after this UNIX timestamp -- `order` Define return order `asc` or `desc` (default) - -Returns: - -An array of requested historical jobs: -```json -{ - "count": 1, - "jobs": [ - { - "job_id": "000001", - "exists": true, - "end_time": 1615764265.6493807, - "filament_used": 7.83, - "filename": "test/history_test.gcode", - "metadata": { - // Object containing metadata at time of job - }, - "print_duration": 18.37201827496756, - "status": "completed", - "start_time": 1615764496.622146, - "total_duration": 18.37201827496756, - "user": "testuser", - "auxiliary_data": [ - { - "provider": "sensor hist_test", - "name": "power_consumption", - "value": 4.119977, - "description": "Printer Power Consumption", - "units": "kWh" - }, - { - "provider": "sensor hist_test", - "name": "max_current", - "value": 2.768851, - "description": "Maximum current draw", - "units": "A" - }, - { - "provider": "sensor hist_test", - "name": "min_current", - "value": 0.426725, - "description": "Minmum current draw", - "units": "A" - }, - { - "provider": "sensor hist_test", - "name": "avg_current", - "value": 1.706872, - "description": "Average current draw", - "units": "A" - }, - { - "provider": "sensor hist_test", - "name": "status", - "value": 2, - "description": "Power Switch Status", - "units": null - }, - { - "provider": "sensor hist_test", - "name": "filament", - "value": 19.08058495194607, - "description": "filament usage tracker", - "units": "mm" - }, - { - "provider": "spoolman", - "name": "spool_ids", - "value": [ - 1 - ], - "description": "Spool IDs used", - "units": null - } - ] - } - ] -} -``` - -#### Get job totals -HTTP request: -```http -GET /server/history/totals -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.history.totals", - "id": 5656 -} -``` - -Returns: - -An object containing the following total job statistics: -```json -{ - "job_totals": { - "total_jobs": 3, - "total_time": 11748.077333278954, - "total_print_time": 11348.794790096988, - "total_filament_used": 11615.718840001999, - "longest_job": 11665.191012736992, - "longest_print": 11348.794790096988 - }, - "auxiliary_totals": [ - { - "provider": "sensor hist_test", - "field": "power_consumption", - "maximum": 4.119977, - "total": 4.119977 - }, - { - "provider": "sensor hist_test", - "field": "avg_current", - "maximum": 1.706872, - "total": null - }, - { - "provider": "sensor hist_test", - "field": "filament", - "maximum": 19.08058495194607, - "total": 19.08058495194607 - } - ] -} -``` - -#### Reset totals -Resets the persistent "job totals" to zero. - -HTTP request: -```http -POST /server/history/reset_totals -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.history.reset_totals", - "id": 5534 -} -``` - -Returns: - -The totals prior to the reset: - -```json -{ - "last_totals": { - "total_jobs": 3, - "total_time": 11748.077333278954, - "total_print_time": 11348.794790096988, - "total_filament_used": 11615.718840001999, - "longest_job": 11665.191012736992, - "longest_print": 11348.794790096988 - }, - "last_auxiliary_totals": [ - { - "provider": "sensor hist_test", - "field": "power_consumption", - "maximum": 4.119977, - "total": 4.119977 - }, - { - "provider": "sensor hist_test", - "field": "avg_current", - "maximum": 1.706872, - "total": null - }, - { - "provider": "sensor hist_test", - "field": "filament", - "maximum": 19.08058495194607, - "total": 19.08058495194607 - } - ] -} -``` - -#### Get a single job -HTTP request: -```http -GET /server/history/job?uid= -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.history.get_job", - "params":{"uid": "{uid}"}, - "id": 4564 -} -``` -Returns: - -Data associated with the job ID in the following format: -```json -{ - "job": { - "job_id": "000001", - "exists": true, - "end_time": 1615764265.6493807, - "filament_used": 7.83, - "filename": "test/history_test.gcode", - "metadata": { - // Object containing metadata at time of job - }, - "print_duration": 18.37201827496756, - "status": "completed", - "start_time": 1615764496.622146, - "total_duration": 18.37201827496756 - } -} -``` - -#### Delete job -HTTP request: -```http -DELETE /server/history/job?uid= -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.history.delete_job", - "params":{ - "uid": "{uid}" - }, - "id": 5534 -} -``` -!!! tip - It is possible to replace the `uid` argument with `all=true` - to delete all jobs in the history database. - -Returns: - -An array of deleted job ids -```json -[ - "000000", - "000001", -] -``` - -### MQTT APIs - -The following API is available when `[mqtt]` has been configured. - -!!! Note - These requests are not available over the `mqtt` transport as they - are redundant. MQTT clients can publish and subscribe to - topics directly. - -#### Publish a topic - -HTTP request: -```http -POST /server/mqtt/publish -Content-Type: application/json - -{ - "topic": "home/test/pub", - "payload": "hello", - "qos": 0, - "retain": false, - "timeout": 5 -} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.mqtt.publish", - "params":{ - "topic": "home/test/pub", - "payload": "hello", - "qos": 0, - "retain": false, - "timeout": 5 - }, - "id": 4564 -} -``` -Only the `topic` parameter is required. Below is an explanation for -each parameter: - -- `topic`: The topic to publish. -- `payload`: Payload to send with the topic. May be a boolean, float, - integer, string, object, or array. All values are converted to strings prior - to publishing. Objects and Arrays are JSON encoded. If omitted an empty - payload is sent. -- `qos`: QOS level to use when publishing the topic. Must be an integer value - from 0 to 2. If omitted the system configured default is used. -- `retain`: If set to `true` the MQTT broker will retain the payload of this - request. Note that only the mostly recently tagged payload is retained. - When other clients first subscribe to the topic they immediately receive the - retained message. The default is `false`. -- `timeout`: A float value in seconds. By default requests with QoS levels of - 1 or 2 will block until the Broker acknowledges confirmation. This option - applies a timeout to the request, returning a 504 error if the timeout is - exceeded. Note that the topic will still be published if the QoS level is 1 - or 2. - -!!! tip - To clear a retained value of a topic, publish the topic with an empty - payload and `retain` set to `true`. - -Returns: - -The published topic: -```json -{ - "topic": "home/test/pub" -} -``` - -#### Subscribe to a topic - - -HTTP request: -```http -POST /server/mqtt/subscribe -Content-Type: application/json - -{ - "topic": "home/test/sub", - "qos": 0, - "timeout": 5 -} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.mqtt.subscribe", - "params":{ - "topic": "home/test/sub", - "qos": 0, - "timeout": 5 - }, - "id": 4564 -} -``` - -Only the `topic` parameter is required. Below is an explanation for -each paramater: - -- `topic`: The topic to subscribe. Note that wildcards may not be used. -- `qos`: QOS level to use when subscribing to the topic. Must be an integer - value from 0 to 2. If omitted the system configured default is used. -- `timeout`: A float value in seconds. By default requests will block - indefinitely until a response is received. This option applies a timeout to - the request, returning a 504 error if the timeout is exceeded. The - subscription will be removed after a timeout. - -!!! note - If the topic was previously published with a retained payload this request - will return with the retained value. - -Returns: - -The subscribed topic and its payload: -```json -{ - "topic": "home/test/pub", - "payload": "test" -} -``` -If the payload is json encodable it will be returned as an object or array. -Otherwise it will be a string. - -### Extension APIs - -Moonraker currently has limited support for 3rd party extensions. These -extensions must create a websocket connect and [identify](#identify-connection) -themselves as an `agent`. Agents may host their own JSON-RPC methods -that other clients may call. Agents may also emit events that are -broadcast to all other websocket connections. - -#### List Extensions - -Returns a list of all available extensions. Currently Moonraker can only -be officially extended through connected `agents`. - -HTTP request: -```http -GET /server/extensions/list -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.extensions.list", - "id": 4564 -} -``` - -Returns: - -A list of connected agents, where each item is an object containing the -agent's identity: - -```json -{ - "agents": [ - { - "name": "moonagent", - "version": "0.0.1", - "type": "agent", - "url": "https://github.com/arksine/moontest" - } - ] -} -``` - -#### Call an extension method - -This API may be used to call a method on a connected agent. The -request effectively relays a JSON-RPC request from a client -to the agent. - -HTTP request: -```http -POST /server/extensions/request -Content-Type: application/json - -{ - "agent": "moonagent", - "method": "moontest.hello_world", - "arguments": {"argone": true, "argtwo": 9000} -} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "server.extensions.request", - "params":{ - "agent": "moonagent", - "method": "moontest.hello_world", - "arguments": {"argone": true, "argtwo": 9000} - }, - "id": 4564 -} -``` - -Parameters: - -- `agent`: The name of the agent. This parameter is required. -- `method`: The name of the method to call on the agent. Agents determine - the method names they expose. This parameter is required. -- `arguments`: This parameter is optional, depending on if the method - being called takes parameters. This should be either an array of positional - arguments or an object of keyword arguments. - -Returns: - -The result returned by the JSON-RPC call to the agent. This can be any JSON -value as determined by the agent. - -#### Send an agent event - -!!! Note - This API is only available to websocket connections that have - identified themselves as an `agent` type. - -HTTP Request: Not Available - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "connection.send_event", - "params":{ - "event": "my_event", - "data": {"my_arg": "optional data"} - } -} -``` - -Parameters: -- `event`: The name of the event. This may be any name as - determined by the agent, with the exception of the reserved - names noted below. -- `data`: Optional supplemental data sent with the event. This - can be any JSON value. - - -!!! Note - The `connected` and `disconnected` events are reserved for use - by Moonraker. - -Returns: - -`ok` if an `id` was present in the request, otherwise no response is -returned. Once received, Moonraker will broadcast this event via -the [agent event notification](#agent-events) to all other connections. - -#### Register a method with Klipper - -Allows agents to register remote methods with Klipper. These methods -may be called in `gcode_macros`. - -!!! Note - This API is only available to websocket connections that have - identified themselves as an `agent` type. - -HTTP Request: Not Available - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "connection.register_remote_method", - "params": { - "method_name": "firemon_alert_heated" - } -} -``` - -Parameters: - -- `method_name`: The name of the desired method. Agents should make sure that - the name is unique. One recommendation is to prefix the agent's name - to each method it registers. - -Returns: - -`ok` if registration is successful. An error is returned if the method name -is already registered. - -!!! Note - Methods registered by agents will persist until the agent disconnects. - Upon connection, it is only necessary that they register their desired - methods once. - -Example: - -Presume an application named `firemon` has connected to Moonraker's websocket -and identified itself as an `agent`. After identification it registers a -remote method named `firemon_alert_heated`. - -In addition, the user the following `gcode_macro` configured in `printer.cfg`: - -```ini -# printer.cfg - -[gcode_macro ALERT_HEATED] -gcode: - {% if not params %} - {action_call_remote_method("firemon_alert_heated")} - {% else %} - {% set htr = params.HEATER|default("unknown") %} - {% set tmp = params.TEMP|default(0)|float %} - {action_call_remote_method( - "firemon_alert_heated", heater=htr, temp=tmp)} - {% endif %} - - -``` - -When the `ALERT_HEATED HEATER=extruder TEMP=200` gcode is executed by Klipper, -the agent will receive the following: - -```json -{ - "jsonrpc": "2.0", - "method": "firemon_alert_heated", - "params": { - "heater": "extruder", - "temp": 200 - } -} -``` - -When the `ALERT_HEATED` gcode is executed with no parameters, the agent will -receive the following: - -```json -{ - "jsonrpc": "2.0", - "method": "monitor_alert_heated" -} -``` - -!!! Note - Methods called from Klipper never contain the "id" field, as Klipper - does not accept return values to remote methods. - -### Debug APIs - -The APIs in this section are available when Moonraker the debug argument -(`-g`) has been supplied via the command line. Some APIs may also depend -on Moonraker's configuration, ie: an optional component may choose to -register a debug API. - -!!! Warning - Debug APIs may expose security vulnerabilities. They should only be - enabled by developers on secured machines. - -#### List Database Info (debug) - -Debug version of [List Database Info](#list-database-info). Returns -all namespaces, including those exlusively reserved for Moonraker. -In addition, registered SQL tables are reported. - - -HTTP request: -```http -GET /debug/database/list -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "debug.database.list", - "id": 8694 -} -``` - -Returns: - -An object containing an array of namespaces, an array of tables, and -an array of backup files. -```json -{ - "namespaces": [ - "gcode_metadata", - "webcams", - "update_manager", - "announcements", - "database", - "moonraker" - ], - "backups": [ - "sqldb-backup-20240513-134542.db", - "testbackup.db", - "testbackup2.db" - ], - "tables": [ - "job_history", - "job_totals", - "namespace_store", - "table_registry", - "authorized_users" - ] -} -``` - -#### Get Database Item (debug) - -Debug version of [Get Database Item](#get-database-item). Keys within -protected and forbidden namespaces are accessible. - -!!! Warning - Moonraker's forbidden namespaces may include items such as user credentials. - This endpoint should NOT be implemented in front ends directly. - -HTTP request: -```http -GET /debug/database/item?namespace={namespace}&key={key} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "debug.database.get_item", - "params": { - "namespace": "{namespace}", - "key": "{key}" - }, - "id": 5644 -} -``` - -#### Add Database Item (debug) - -Debug version of [Add Database Item](#add-database-item). Keys within -protected and forbidden namespaces may be added. - -!!! Warning - This endpoint should be used for testing/debugging purposes only. - Modifying protected namespaces outside of Moonraker can result in - broken functionality and is not supported for production environments. - Issues opened with reports/queries related to this endpoint will be - redirected to this documentation and closed. - -```http -POST /debug/database/item -Content-Type: application/json - -{ - "namespace": "my_client", - "key": "settings.some_count", - "value": 100 -} -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "debug.database.post_item", - "params": { - "namespace": "{namespace}", - "key": "{key}", - "value": 100 - }, - "id": 4654 -} -``` - -#### Delete Database Item (debug) - -Debug version of [Delete Database Item](#delete-database-item). Keys within -protected and forbidden namespaces may be removed. - -!!! Warning - This endpoint should be used for testing/debugging purposes only. - Modifying protected namespaces outside of Moonraker can result in - broken functionality and is not supported for production environments. - Issues opened with reports/queries related to this endpoint will be - redirected to this documentation and closed. - -HTTP request: -```http -DELETE /debug/database/item?namespace={namespace}&key={key} -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "debug.database.delete_item", - "params": { - "namespace": "{namespace}", - "key": "{key}" - }, - "id": 4654 -} -``` - -#### Get Database Table - -Requests all the contents of a specified table. - -HTTP request: -```http -GET /debug/database/table?table=job_history -``` - -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "debug.database.table", - "params": { - "table": "job_history" - }, - "id": 4654 -} -``` - -Parameters: - -- `table`: The name of the table to request. This parameter must - be provided. - -Returns: - -An object with the table's name and a list of all rows contained -within the table. The `rowid` will always be included for each -row, however it may be represented by an alias. In the example -below the alias for `rowid` is `job_id`. - -```json -{ - "table_name": "job_history", - "rows": [ - { - "job_id": 1, - "user": "No User", - "filename": "active_test.gcode", - "status": "completed", - "start_time": 1690749153.2661753, - "end_time": 1690749173.076986, - "print_duration": 0.0, - "total_duration": 19.975574419135228, - "filament_used": 0.0, - "metadata": { - "size": 211, - "modified": 1635771217.0, - "uuid": "627371e0-faa5-4ced-8bb4-7017d29226fa", - "slicer": "Unknown", - "gcode_start_byte": 8, - "gcode_end_byte": 211 - }, - "auxiliary_data": [], - "instance_id": "default" - }, - { - "job_id": 2, - "user": "No User", - "filename": "active_test.gcode", - "status": "completed", - "start_time": 1701262034.9242446, - "end_time": 1701262054.7332363, - "print_duration": 0.0, - "total_duration": 19.990913168992847, - "filament_used": 0.0, - "metadata": { - "size": 211, - "modified": 1635771217.0, - "uuid": "627371e0-faa5-4ced-8bb4-7017d29226fa", - "slicer": "Unknown", - "gcode_start_byte": 8, - "gcode_end_byte": 211 - }, - "auxiliary_data": { - "spool_ids": [ - 2 - ] - }, - "instance_id": "default" - } - ] -} -``` - -#### Test a notifier (debug) - -You can trigger a notifier manually using this endpoint. - -HTTP request: -```http -POST /debug/notifiers/test?name=notifier_name -``` -JSON-RPC request: -```json -{ - "jsonrpc": "2.0", - "method": "debug.notifiers.test", - "params": { - "name": "notifier_name" - }, - "id": 4654 -} -``` - -Parameters: - -- `name`: The name of the notifier to test. - -Returns: Test results in the following format - -```json -{ - "status": "success", - "stats": { - "print_duration": 0.0, - "total_duration": 0.0, - "filament_used": 0.0, - "filename": "notifier_test.gcode", - "state": "standby", - "message": "" - } -} -``` - -### Websocket notifications -Printer generated events are sent over the websocket as JSON-RPC 2.0 -notifications. These notifications are sent to all connected clients -in the following format: -```json -{ - "jsonrpc": "2.0", - "method": "{event method name}" -} -``` -OR -```json -{ - "jsonrpc": "2.0", - "method": "{event method name}", - "params": [] -} -``` - -If a notification has parameters, the `params` value will always be -wrapped in an array as directed by the JSON-RPC standard. Currently -all notifications available are broadcast with either no parameters -or a single parameter. - -#### Gcode Response -All of Klippy's gcode responses are forwarded over the websocket. They arrive -as a "gcode_response" notification: -```json -{ - "jsonrpc": "2.0", - "method": "notify_gcode_response", - "params": ["response message"] -} -``` - -#### Subscriptions -Status Subscriptions arrive as a "notify_status_update" notification: -```json -{ - "jsonrpc": "2.0", - "method": "notify_status_update", - "params": [{}, ] -} -``` -The structure of the `status object` is identical to the structure that is -returned from an [object query's](#query-printer-object-status) -`status` field. - -The `eventtime` is a timestamp generated by Klipper when -the update was originally pushed. This timestamp is a float value, -relative to Klipper's monotonic clock. - -#### Klippy Ready -Notify clients when Klippy has reported a ready state -```json -{ - "jsonrpc": "2.0", - "method": "notify_klippy_ready" -} -``` - -#### Klippy Shutdown -Notify clients when Klippy has reported a shutdown state -```json -{ - "jsonrpc": "2.0", - "method": "notify_klippy_shutdown" -} -``` - -#### Klippy Disconnected -Notify clients when Moonraker's connection to Klippy has terminated -```json -{ - "jsonrpc": "2.0", - "method": "notify_klippy_disconnected" -} -``` - -#### File List Changed -When a client makes a change to a file or directory in a registered -`root` (via upload, delete, move, etc) a notification is broadcast -to alert all connected clients of the change: -```json -{ - "jsonrpc": "2.0", - "method": "notify_filelist_changed", - "params": [ - { - "action": "{action}", - "item": { - "path": "{file or directory path relative to root}", - "root": "{root}", - "size": 46458, - "modified": 545465, - "permissions": "rw" - }, - "source_item": { - "path": "{file or directory path relative to root}", - "root": "{root_name}" - } - } - ] -} -``` - -!!! Note - The `source_item` field is only present for `move_file` and - `move_dir` actions. - -The `action` field will be set to one of the following values: - -- `create_file` -- `create_dir` -- `delete_file` -- `delete_dir` -- `move_file` -- `move_dir` -- `modify_file` -- `root_update` - -Most of the above actions are self explanatory. The `root_update` -notification is sent when a `root` folder has changed its location. -This should be a rare event as folders are now managed in using the -data folder structure. - -Notifications are bundled where applicable. For example, when a -directory containing children is deleted a single `delete_dir` notification -is pushed. Likewise, when a directory is moved or copied, a single -`move_dir` or `create_dir` notification is pushed. Children that are -moved, copied, or deleted as a result of a parent's action will -not receive individual notifications. - -#### Update Manager Response -The update manager will send asynchronous messages to the client during an -update: -```json -{ - "jsonrpc": "2.0", - "method": "notify_update_response", - "params": [ - { - "application": "{app_name}", - "proc_id": 446461, - "message": "Update Response Message", - "complete": false - } - ] -} -``` -The fields reported in the response are as follows: - -- The `application` field contains the name of application currently being - updated. Generally this will be either "moonraker", "klipper", "system", - or "client". -- The `proc_id` field contains a unique id associated with the current update - process. This id is generated for each update request. -- The `message` field contains an asynchronous message sent during the update - process. -- The `complete` field is set to true on the final message sent during an - update, indicating that the update completed successfully. Otherwise it - will be false. - -#### Update Manager Refreshed -The update manager periodically auto refreshes the state of each application -it is tracking. After an auto refresh has completed the following -notification is broadcast: -```json -{ - "jsonrpc": "2.0", - "method": "notify_update_refreshed", - "params": [{update_info}]} -``` -Where `update_info` is an object that matches the response from an -[update status](#get-update-status) request. - -#### CPU Throttled -If the system supports throttled CPU monitoring Moonraker will send the -following notification when it detects a change to the current throttled -state: -```json -{ - "jsonrpc": "2.0", - "method": "notify_cpu_throttled", - "params": [{throttled_state}] -} -``` - -Where `throttled_state` is an object that matches the `throttled_state` field -in the response from a [Moonraker process stats](#get-moonraker-process-stats) -request. It is possible for clients to receive this notification multiple times -if the system repeatedly transitions between an active and inactive throttled -condition. - -#### Moonraker Process Statistic Update -Moonraker will emit the following notification each time it samples its -process statistics: -```json -{ - "jsonrpc": "2.0", - "method": "notify_proc_stat_update", - "params": [{ - "moonraker_stats": { - "time": 1615837812.0894408, - "cpu_usage": 1.99, - "memory": 23636, - "mem_units": "kB" - }, - "cpu_temp": 44.008, - "network": { - "lo": { - "rx_bytes": 114555457, - "tx_bytes": 114555457, - "bandwidth": 2911.49 - }, - "wlan0": { - "rx_bytes": 48773134, - "tx_bytes": 115035939, - "bandwidth": 3458.77 - } - }, - "system_cpu_usage": { - "cpu": 2.53, - "cpu0": 3.03, - "cpu1": 5.1, - "cpu2": 1.02, - "cpu3": 1 - }, - "websocket_connections": 2 - }] -} -``` - -As with the [proc_stats request](#get-moonraker-process-stats) the `cpu_temp` -field will be set to `null` if the host machine does not support retrieving CPU -temperatures at `/sys/class/thermal/thermal_zone0`. - -#### History Changed -If the `[history]` module is enabled the following notification is sent when -a job is added or finished: -```json -{ - "jsonrpc": "2.0", - "method": "notify_history_changed", - "params": [ - { - "action": "added", - "job": - } - ] -} -``` -The `action` field may be `added` or `finished`. The `job` field contains -an object matches the one returned when requesting -[job data](#get-a-single-job). - -#### Authorized User Created -If the `[authorization]` module is enabled the following notification is -sent when a new user is created: -```json -{ - "jsonrpc": "2.0", - "method": "notify_user_created", - "params": [ - { - "username": "" - } - ] -} -``` - -#### Authorized User Deleted -If the `[authorization]` module is enabled the following notification is -sent when an existing user is deleted. -```json -{ - "jsonrpc": "2.0", - "method": "notify_user_deleted", - "params": [ - { - "username": "" - } - ] -} -``` - -#### Authorized User Logged Out -If the `[authorization]` module is enabled the following notification is -sent when an existing user is logged out. -```json -{ - "jsonrpc": "2.0", - "method": "notify_user_logged_out", - "params": [ - { - "username": "" - } - ] -} -``` - -#### Service State Changed -Moonraker monitors the state of systemd services it is authorized to track. -When the state of a service changes the following notification is sent: - -```json -{ - "jsonrpc": "2.0", - "method": "notify_service_state_changed", - "params": [ - { - "klipper": { - "active_state": "inactive", - "sub_state": "dead" - } - } - ] -} -``` - -The example above shows that the `klipper` service has changed to `inactive`. - -#### Job Queue Changed -Moonraker will send a `job_queue_changed` notification when a change is -detected to the queue state or the queue itself: - -```json -{ - "jsonrpc": "2.0", - "method": "notify_job_queue_changed", - "params": [ - { - "action": "state_changed", - "updated_queue": null, - "queue_state": "paused" - } - ] -} -``` - -The object sent with the notification contains the following fields: - -- `action`: The action taken to the queue which led to the notification. - Will be a string set to one of the following values: - - `state_changed`: The queue state has changed - - `jobs_added`: One or more jobs were added to the queue - - `jobs_removed`: One or more jobs were removed from the queue - - `job_loaded`: A job was popped off the queue and successfully started -- `updated_queue`: If the queue itself is changed this will be a list - containing each item in the queue. If the queue has not changed this will - be `null`. -- `queue_state`: The current queue state - -#### Button Event -Moonraker `[button]` components may be configured to emit websocket -notifications. - -```json -{ - "jsonrpc": "2.0", - "method": "notify_button_event", - "params": [ - { - "name": "my_button", - "type": "gpio", - "event": { - "elapsed_time": 0.09323832602240145, - "received_time": 698614.214597004, - "render_time": 698614.214728513, - "pressed": false - }, - "aux": null - } - ] -} -``` - -The `params` array will always contain a single object with the following -fields: - -- `name`: The name of the configured button -- `type`: The button type, currently this will always be `gpio` -- `event`: An object with details about the button event, containing the - following fields: - - `elapsed_time`: The time elapsed (in seconds) since the last detected - button event - - `received_time`: The time the event was detected according to asyncio's - monotonic clock. Note that this is not in "unix time". - - `render_time`: The time the template was rendered (began execution) - according to asyncio's monotonic clock. It is possible execution of - an event may be delayed well beyond the `received_time`. - - `pressed`: A boolean value to indicate if the button is currently pressed. -- `aux`: This is an optional field where the button may specify any json - encodable value. Clients may suggest a specific button configuration - that includes details about the event. If no aux parameter is specified - in the configuration this will be a `null` value. - -#### Announcement update event - -Moonraker will emit the `notify_announcement_update` notification when -a announcement entries are added or removed: - -```json -{ - "jsonrpc": "2.0", - "method": "notify_announcement_update", - "params": [ - { - "entries": [ - { - "entry_id": "arksine/moonlight/issue/3", - "url": "https://github.com/Arksine/moonlight/issues/3", - "title": "Test announcement 3", - "description": "Test Description [with a link](https://moonraker.readthedocs.io).", - "priority": "normal", - "date": 1647459219, - "dismissed": false, - "date_dismissed": null, - "dismiss_wake": null, - "source": "moonlight", - "feed": "moonlight" - }, - { - "entry_id": "arksine/moonlight/issue/2", - "url": "https://github.com/Arksine/moonlight/issues/2", - "title": "Announcement Test Two", - "description": "This is a high priority announcement. This line is included in the description.", - "priority": "high", - "date": 1646855579, - "dismissed": false, - "date_dismissed": null, - "dismiss_wake": null, - "source": "moonlight", - "feed": "moonlight" - } - { - "entry_id": "arksine/moonraker/issue/349", - "url": "https://github.com/Arksine/moonraker/issues/349", - "title": "PolicyKit warnings; unable to manage services, restart system, or update packages", - "description": "This announcement is an effort to get ahead of a coming change that will certainly result in issues. PR #346 has been merged, and with it are some changes to Moonraker's default behavior.", - "priority": "normal", - "date": 1643392406, - "dismissed": false, - "source": "moonlight", - "feed": "Moonraker" - } - ] - } - ] -} -``` - -The `params` array will contain an object with all current announcement entries. -This object is identical to that returned by the -[list announcements](#list-announcements) endpoint. - -#### Announcement dismissed event -Moonraker will emit the `notify_announcement_dismissed` notification when -a dismissed announcement is detected: - -```json -{ - "jsonrpc": "2.0", - "method": "notify_announcement_dismissed", - "params": [ - { - "entry_id": "arksine/moonlight/issue/3" - } - ] -} -``` - -The `params` array will contain an object with the `entry_id` of the dismissed -announcement. - -#### Announcement wake event -Moonraker will emit the `notify_announcement_wake` notification when -a specified `wake_time` for a dismissed announcement has expired. - -```json -{ - "jsonrpc": "2.0", - "method": "notify_announcement_wake", - "params": [ - { - "entry_id": "arksine/moonlight/issue/1" - } - ] -} -``` - -The `params` array will contain an object with the `entry_id` of the -announcement that is no longer dismissed. - -#### Sudo alert event -Moonraker will emit the `notify_sudo_alert` notification when -a component has requested sudo access. The event is also emitted -when a sudo request has been granted. - -```json -{ - "jsonrpc": "2.0", - "method": "notify_sudo_alert", - "params": [ - { - "sudo_requested": true, - "sudo_messages": [ - "Sudo password required to update Moonraker's systemd service." - ] - } - ] -} -``` - -The `params` array contains an object with the following fields: - -- `sudo_requested`: Returns true if Moonraker is currently requesting - sudo access. -- `request_messages`: An array of strings, each string describing - a pending sudo request. The array will be empty if no sudo - requests are pending. - -#### Webcams changed event - -Moonraker will emit the `notify_webcams_changed` event when a configured -webcam is added, removed, or updated. - -```json -{ - "jsonrpc": "2.0", - "method": "notify_webcams_changed", - "params": [ - { - "webcams": [ - { - "name": "tc2", - "location": "printer", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 15, - "target_fps_idle": 5, - "stream_url": "http://printer.lan/webcam?action=stream", - "snapshot_url": "http://printer.lan/webcam?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "database" - }, - { - "name": "TestCam", - "location": "printer", - "service": "mjpegstreamer", - "enabled": true, - "icon": "mdiWebcam", - "target_fps": 15, - "target_fps_idle": 5, - "stream_url": "/webcam/?action=stream", - "snapshot_url": "/webcam/?action=snapshot", - "flip_horizontal": false, - "flip_vertical": false, - "rotation": 0, - "aspect_ratio": "4:3", - "extra_data": {}, - "source": "database" - } - ] - } - ] -} -``` - -The `webcams` field contans an array of objects like those returned by the -[list webcams](#list-webcams) API. - -#### Spoolman active spool ID changed - -Moonraker will emit the `notify_active_spool_set` event when the active spool -ID for the Spoolman integration has been changed. - -See the [Spoolman API](#spoolman-apis) for more information. - -```json -{ - "jsonrpc": "2.0", - "method": "notify_active_spool_set", - "params": [ - { - "spool_id": 1 - } - ] -} -``` - -#### Spoolman Status Changed - -Moonraker will emit the `notify_spoolman_status_changed` event when the -connection state to the Spoolman service has changed: - -```json -{ - "jsonrpc": "2.0", - "method": "notify_spoolman_status_changed", - "params": [ - { - "spoolman_connected": false - } - ] -} -``` - -#### Agent Events -Moonraker will emit the `notify_agent_event` notification when it -an agent event is received. - -```json -{ - "jsonrpc": "2.0", - "method": "notify_agent_event", - "params": [ - { - "agent": "moonagent", - "event": "connected", - "data": { - "name": "moonagent", - "version": "0.0.1", - "type": "agent", - "url": "https://github.com/arksine/moontest" - } - } - ] -} -``` - -When an agent connects, all connections will receive a `connected` event -for that agent, with its identity info in the `data` field. When an agent -disconnects clients will receive a `disconnected` event with the data field -omitted. All other events are determined by the agent, where each event may -or may not include optional `data`. - -#### Sensor Events - -Moonraker will emit a `sensors:sensor_update` notification when a measurement -from at least one monitored sensor changes. - -```json -{ - "jsonrpc": "2.0", - "method": "sensors:sensor_update", - "params": [ - { - "sensor1": { - "humidity": 28.9, - "temperature": 22.4 - } - } - ] -} -``` - -When a sensor reading changes, all connections will receive a -`sensors:sensor_update` event where the params contains a data struct -with the sensor id as the key and the sensors letest measurements as value -struct. - -### Appendix - -#### Websocket setup -The websocket is located at `ws://host:port/websocket`, for example: -```javascript -var s = new WebSocket("ws://" + location.host + "/websocket"); -``` - -!!! tip - A client using API Key authorization may request a - [oneshot token](#generate-a-oneshot-token), applying the result to the - websocket request's query string: - -```http -ws://host:port/websocket?token={32 character base32 string} -``` - -The following startup sequence is recommended for clients which make use of -the websocket: - -1. Attempt to connect to `/websocket` until successful using a timer-like - mechanism -2. Once connected, query `/server/info` (or `server.info`) for the ready - status. - - If the response returns an error (such as 404) then either the client - is not authorized or Moonraker is not running. Direct the user to - SSH into the machine and check `/tmp/moonraker.log`. - - If the response returns success, check the result's `klippy_state` - field: - - `klippy_state == "ready"`: you may proceed to request status of - printer objects make subscriptions, get the file list, etc. - - `klippy_state == "error"`: Klippy has experienced an error - starting up - - `klippy_state == "shutdown"`: Klippy is in a shutdown state. - - `klippy_state == "startup"`: re-request `/server/info` in 2 seconds. - - If `error` or `shutdown` is detected it might be wise to prompt - the user. You can get a description from the `state_message` - field of a `/printer/info` request. -3. Repeat step 2 until Klipper reports ready. -4. Clients should watch for the `notify_klippy_disconnected` event. If - received then Klippy has either been stopped or restarted. In this - state the client should repeat the steps above to determine when - klippy is ready. - -#### Basic Print Status -An advanced client will likely use subscriptions and notifications -to interact with Moonraker, however simple clients such as home automation -software and embedded devices (ie: ESP32) may only wish to monitor the -status of a print. Below is a high level walkthrough for receiving print state -via polling. - -- Set up a timer to poll at the desired interval. Depending on your use - case, 1 to 2 seconds is recommended. -- On each cycle, issue the following request: - - GET http://host/printer/objects/query?webhooks&virtual_sdcard&print_stats - - Or via JSON-RPC 2.0: - - { - "jsonrpc": "2.0", - "method": "printer.objects.query", - "params": { - "objects": { - "webhooks": null, - "virtual_sdcard": null, - "print_stats": null - } - }, - "id": 5664 - } - -- If the request returns an error or the returned `result.status` is an empty - object, then this is an indication that Klippy either experienced an error or - it is not properly configured. Each queried object should be available in - `result.status`. The client should check to make sure that all objects are - received before proceeding. -- Inspect `webhooks.ready`. If the value is not `ready` the printer - is not available. `webhooks.message` contains a message pertaining - to the current state. -- If the printer is ready, inspect `print_stats.state`. It may be one - of the following values: - - `standby`: No print in progress - - `printing`: The printer is currently printing - - `paused`: A print in progress has been paused - - `error`: The print exited with an error. `print_stats.message` - contains a related error message - - `complete`: The last print has completed -- If `print_stats.state` is not `standby` then `print_stats.filename` - will report the name of the currently loaded file. -- `print_stats.filename` can be used to fetch file metadata. It - is only necessary to fetch metadata once per print. - - GET http://host/server/files/metadata?filename= - - Or via JSON-RPC 2.0: - - { - "jsonrpc": "2.0", - "method": "server.files.metadata", - "params": { - "filename": "{filename}" - }, - "id": 5643 - } - - If metadata extraction failed then this request will return an error. - Some metadata fields are only populated for specific slicers, and - unsupported slicers will only return the size and modified date. - -- There are multiple ways to calculate the ETA, this example will use - file progress, as it is possible calculate the ETA with or without - metadata. - - If `metadata.estimated_time` is available, the eta calculation can - be done as: - - // assume "result" is the response from the status query - let vsd = result.status.virtual_sdcard; - let prog_time = vsd.progress * metadata.estimated_time; - let eta = metadata.estimated_time - prog_time - - Alternatively, one can simply subtract the print duration from - the estimated time: - - // assume "result" is the response from the status query - let pstats = result.status.print_status; - let eta = metadata.estimated_time - pstats.print_duration; - if (eta < 0) - eta = 0; - - - If no metadata is available, print duration and progress can be used to - calculate the ETA: - - // assume "result" is the response from the status query - let vsd = result.status.virtual_sdcard; - let pstats = result.status.print_stats; - let total_time = pstats.print_duration / vsd.progress; - let eta = total_time - pstats.print_duration; - -- It is possible to query additional objects if a client wishes to display - more information (ie: temperatures). See the - [Printer Objects](printer_objects.md) documentation for details. - -#### Bed Mesh Coordinates -The [Bed Mesh](printer_objects.md#bed_mesh) printer object may be used -to generate three dimensional coordinates of a probed area (or mesh). -Below is an example (in javascript) of how to transform the data received -from a bed_mesh object query into an array of 3D coordinates. -```javascript -// assume that we have executed an object query for bed_mesh and have the -// result. This example generates 3D coordinates for the probed matrix, -// however it would work with the mesh matrix as well -function process_mesh(result) { - let bed_mesh = result.status.bed_mesh - let matrix = bed_mesh.probed_matrix; - if (!(matrix instanceof Array) || matrix.length < 3 || - !(matrix[0] instanceof Array) || matrix[0].length < 3) - // make sure that the matrix is valid - return; - let coordinates = []; - let x_distance = (bed_mesh.mesh_max[0] - bed_mesh.mesh_min[0]) / - (matrix[0].length - 1); - let y_distance = (bed_mesh.mesh_max[1] - bed_mesh.mesh_min[1]) / - (matrix.length - 1); - let x_idx = 0; - let y_idx = 0; - for (const x_axis of matrix) { - x_idx = 0; - let y_coord = bed_mesh.mesh_min[1] + (y_idx * y_distance); - for (const z_coord of x_axis) { - let x_coord = bed_mesh.mesh_min[0] + (x_idx * x_distance); - x_idx++; - coordinates.push([x_coord, y_coord, z_coord]); - } - y_idx++; - } -} -// Use the array of coordinates visualize the probed area -// or mesh.. -``` - -#### Converting to Unix Time -Some of Moonraker's APIs return a date represented in Unix time. -Most languages have functionality built in to convert Unix -time to a workable object or string. For example, in JavaScript -one might do something like the following: -```javascript -for (let resp of result.gcode_store) { - let date = new Date(resp.time * 1000); - // Do something with date and resp.message ... -} -``` - -#### Announcements - -Moonraker announcements are effectively push notifications that -can be used to notify users of important information related the -development and status of software in the Klipper ecosystem. This -section will provide an overview of how the announcement system -works, how to set up a dev environment, and provide recommendations -on client implementation. - -##### How announcements work - -Moonraker announcements are GitHub issues tagged with the `announcement` -label. GitHub repos may registered with -[moonlight](https://github.com/arksine/moonlight), which is responsible -for generating RSS feeds from GitHub issues using GitHub's REST API. These -RSS feeds are hosted on GitHub Pages, for example Moonraker's feed may be found -[here](https://arksine.github.io/moonlight/assets/moonraker.xml). By -centralizing GitHub API queries in `moonlight` we are able to poll multiple -repos without running into API rate limit issues. Moonlight has has a workflow -that checks all registered repos for new announcements every 30 minutes. In -theory it would be able to check for announcements in up to 500 repos before -exceeding GitHub's API rate limit. - -Moonraker's `[announcements]` component will always check the `klipper` and -`moonraker` RSS feeds. It is possible to configure additional RSS feeds by -adding them to the `subscriptions` option. The component will poll configured -feeds every 30 minutes, resulting in maximum of 1 hour for new announcements -to reach all users. - -When new issues are tagged with `announcement` these entries will be parsed -and added to the RSS feeds. When the issue is closed they will be removed from -the corresponding feed. Moonlight will fetch up to 20 announcements for each -feed, if a repo goes over this limit older announcements will be removed. - -!!! Note - It is also possible for Moonraker to generate announcements itself. For - example, if a Moonraker component needs user feedback it may generate an - announcement and notify all connected clients. From a client's - perspective there is no need to treat announcements differently than - any other announcement. - -##### Setting up the dev environment - -Moonraker provides configuration to parse announcements from a local folder -so that it is possible to manually add and remove entries, allowing client -developers to perform integration tests: - -```ini -# moonraker.conf - -[announcements] -dev_mode: True -``` - -With `dev_mode` enabled, Moonraker will look for`moonraker.xml` and -`klipper.xml` in the following folder: -```shell -~/moonraker/.devel/announcement_xml -``` - -If moonraker is not installed in the home folder then substitute `~` -for the parent folder location. This folder is in a hardcoded location -to so as not to expose users to vulnerabilities associated with parsing XML. - -It is possible to configure Moonraker to search for your own feeds: - -```ini -# moonraker.conf - -[announcements] -subscription: - my_project -dev_mode: True -``` - -The above configuration would look for `my_project.xml` in addition to -`klipper.xml` and `moonraker.xml`. The developer may manually create -the xml feeds or they may clone `moonlight` and leverage its script -to generate a feed from issues created on their test repo. When local -feeds have been modified one may call the [update announcements API](#update-announcements) to have Moonraker fetch the updates and add/remove -entries. - -##### RSS file structure - -Moonlight generates RSS feeds in XML format. Below is an example generated -from moonlight's own issue tracker: - -```xml - - - - arksine/moonlight - https://github.com/Arksine/moonlight - RSS Announcements for Moonraker - Tue, 22 Mar 2022 23:19:04 GMT - f2912192bf0d09cf18d8b8af22b2d3501627043e5afa3ebff0e45e4794937901 - - Test announcement 3 - https://github.com/Arksine/moonlight/issues/3 - Test Description [with a link](https://moonraker.readthedocs.io). - Wed, 16 Mar 2022 19:33:39 GMT - normal - arksine/moonlight/issue/3 - - - Announcement Test Two - https://github.com/Arksine/moonlight/issues/2 - This is a high priority announcement. This line is included in the description. - Wed, 09 Mar 2022 19:52:59 GMT - high - arksine/moonlight/issue/2 - - - Announcement Test One - https://github.com/Arksine/moonlight/issues/1 - This is the description. Anything here should appear in the announcement, up to 512 characters. - Wed, 09 Mar 2022 19:37:58 GMT - normal - arksine/moonlight/issue/1 - - - -``` - -Each xml file may contain only one `` element, and each `` element -may contain only one channel. All items must be present aside from -`moonlight:configHash`, which is used by the workflow to detect changes to -moonlight's configuration. Most elements are self explanatory, developers will -be most interested in adding and removing `` elements, as these are -the basis for entries in Moonraker's announcement database. - -##### Generating announcements from your own repo - -As mentioned previously, its possible to clone moonlight and use its rss -script to generate announcements from issues in your repo: - -```shell -cd ~ -git clone https://github.com/arksine/moonlight -cd moonlight -virtualenv -p /usr/bin/python3 .venv -source .venv/bin/activate -pip install httpx[http2] -deactivate -``` - -To add your repo edit `~/moonlight/src/config.json`: -```json -{ - "moonraker": { - "repo_owner": "Arksine", - "repo_name": "moonraker", - "description": "API Host For Klipper", - "authorized_creators": ["Arksine"] - }, - "klipper": { - "repo_owner": "Klipper3d", - "repo_name": "klipper", - "description": "A 3D Printer Firmware", - "authorized_creators": ["KevinOConnor"] - }, - // Add your test repo info here. It should contain - // fields matching those in "moonraker" and "klipper" - // shown above. -} -``` - -Once your repo is added, create one or more issues on your GitHub -repo tagged with the `announcement` label. Add the `critical` label to -one if you wish to test high priority announcements. You may need to -create these labels in your repo before they can be added. - -Now we can use moonlight to generate the xml files: -```shell -cd ~/moonlight -source .venv/bin/activate -src/update_rss.py -deactivate -``` - -After the script has run it will generate the configured RSS feeds -and store them in `~/moonlight/assets`. If using this method it may -be useful to create a symbolic link to it in Moonraker's devel folder: - -```shell -cd ~/moonraker -mkdir .devel -cd .devel -ln -s ~/moonlight/assets announcement_xml -``` - -If you haven't done so, configure Moonraker to subscribe to your feed -and restart the Moonraker service. Otherwise you may call the -[announcement update](#update-announcements) API to have Moonraker -parse the announcements from your test feed. - - -##### Implementation details and recommendations - -When Moonraker detects a change to one or more feeds it will fire the -[announcement update](#announcement-update-event) notification. It is also -possible to [query the API for announcements](#list-announcements). Both -the notification and the API return a list of announcement entries, where -each entry is an object containing the following fields: - -- `entry_id`: A unique ID derived for each entry. Typically this is in the - form of `{owner}/{repo}/issue/{issue number}`. -- `url`: The url to the full announcement. This is generally a link to - an issue on GitHub. -- `title`: Announcement title, will match the title of the issue on GitHub. -- `description`: The first paragraph of the announcement. Anything over - 512 characters will be truncated. -- `priority`: Can be `normal` or `high`. It is recommended that clients - immediately alert the user when one or more `high` priority announcments - are present. Issued tagged with the `critical` label will be assigned - a `high` priority. -- `date`: The announcement creation date in unix time. -- `dismissed`: If set to `true` this announcement has been previously - dismissed -- `date_dismissed`: The date the announcement was dismissed in unix time. - If the announcement has not been dismissed this value is `null`. -- `dismiss_wake`: If the announcement was dismissed with a `wake_time` - specified this is the time (in unix time) at which the `dismissed` - state will revert. If the announcement is not dismissed or dismissed - indefinitely this value will be `null`. -- `source`: The source from which the announcement was generated. Can - be `moonlight` or `internal`. -- `feed`: The RSS feed for moonlight announcements. For example, this - could be `Moonraker` or `Klipper`. If the announcement was generated - internally this should match the name of the component that generated - the announcement. - -When a client first connects to Moonraker it is recommended that the -[list announcements](#list-announcements) API is called to retrieve -the current list of entries. A client may then watch for the -[announcement update](#announcement-update-event) and -[announcement dismissed](#announcement-dismissed-event) notifications -and update the UI accordingly. - -Client devs should decide how they want to present announcements to users. They could be treated as any other notification, for example a client -may have a notification icon that shows the current number of unread -announcements. Clients can mark an announcement as `read` by calling -the [dismiss announcement](#dismiss-an-announcement) API. Any announcement -entry with `dismissed = true` should be considered read. - -When a `high priority` announcement is detected it is recommended that -clients present the announcement in a format that is immediately visible -to the user. That said, it may be wise to allow users to opt out of -this behavior via configuration. - -!!! Note - If an announcement is dismissed, closed, then reopened the - `dismissed` flag will reset to false. This is expected behavior - as announcements are pruned from the database when they are no - longer present in feeds. It isn't valid for repo maintaners - to re-open a closed announcement. That said, its fine to close - and re-open issues during development and testing using repos - that are not yet registered with moonlight. diff --git a/mkdocs.yml b/mkdocs.yml index 88336a6..21a6397 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,11 +1,28 @@ site_name: Moonraker site_url: https://moonraker.readthedocs.io repo_url: https://github.com/Arksine/moonraker +edit_uri: blob/master/docs/ nav: - Installation: installation.md - Configuration : configuration.md - - 'Developer Documentation': - - Remote API: web_api.md + - Developer Documentation: + - External API: + - Introduction: external_api/introduction.md + - Server Administration: external_api/server.md + - Printer Administration: external_api/printer.md + - System Administration: external_api/machine.md + - File Management: external_api/file_manager.md + - Authorization and Authentication: external_api/authorization.md + - Database Management: external_api/database.md + - Job Queue Management: external_api/job_queue.md + - Job History Management: external_api/history.md + - Announcements: external_api/announcements.md + - Webcam Management: external_api/webcams.md + - Update Management: external_api/update_manager.md + - Switches, Sensors, and Devices: external_api/devices.md + - Third Party Integrations: external_api/integrations.md + - Extensions: external_api/extensions.md + - JSON-RPC Notifications: external_api/jsonrpc_notifications.md - Printer Objects: printer_objects.md - Components: components.md - Contribution Guidelines: contributing.md @@ -58,8 +75,8 @@ markdown_extensions: - pymdownx.caret - pymdownx.details - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg - pymdownx.highlight - pymdownx.inlinehilite - pymdownx.keys @@ -71,6 +88,8 @@ markdown_extensions: - pymdownx.tasklist: custom_checkbox: true - pymdownx.tilde + - pymdownx.saneheaders + - pymdownx.blocks.admonition - pymdownx.blocks.details: types: - name: details-new @@ -103,13 +122,16 @@ markdown_extensions: class: quote - name: api-example-response class: example - title: "Example Response" - - name: api-response-schema + title: "Response Example" + - name: api-response-spec class: info - title: "Response Schema" + title: "Response Specification" - name: api-parameters class: info title: "Parameters" + - name: api-notification-spec + class: info + title: "Notification Parameter Specification" - tables - compact_tables: auto_insert_break: false @@ -117,4 +139,6 @@ markdown_extensions: expand_text: '' collapse_text: '' extra_css: - - src/css/extras.css \ No newline at end of file + - src/css/extra-950ac449d4.css +extra_javascript: + - src/js/compact-tables-qqTQvuZ9.js