From d1c740b90017171450b8653b247bc5a3ae49e618 Mon Sep 17 00:00:00 2001 From: Arksine Date: Wed, 1 Jul 2020 21:21:35 -0400 Subject: [PATCH] moonraker: add initial source Signed-off-by: Eric Callahan --- README.md | 11 +- docs/dev_changelog.md | 294 ++++++ docs/installation.md | 209 ++++ docs/plugins.md | 3 + docs/web_api.md | 654 ++++++++++++ moonraker/app.py | 410 ++++++++ moonraker/authorization.py | 219 ++++ moonraker/moonraker.py | 460 +++++++++ moonraker/plugins/__init__.py | 6 + moonraker/plugins/file_manager.py | 285 ++++++ moonraker/plugins/gcode_apis.py | 67 ++ moonraker/plugins/machine.py | 34 + moonraker/plugins/paneldue.py | 685 +++++++++++++ moonraker/plugins/shell_command.py | 88 ++ moonraker/plugins/temperature_store.py | 73 ++ moonraker/utils.py | 31 + moonraker/websockets.py | 234 +++++ scripts/extract_metadata.py | 355 +++++++ scripts/install-moonraker.sh | 108 ++ scripts/moonraker-requirements.txt | 3 + scripts/moonraker-start.sh | 55 + scripts/uninstall-moonraker.sh | 63 ++ test/client/index.html | 78 ++ test/client/js/json-rpc.js | 228 +++++ test/client/js/main.js | 1297 ++++++++++++++++++++++++ 25 files changed, 5948 insertions(+), 2 deletions(-) create mode 100644 docs/dev_changelog.md create mode 100644 docs/installation.md create mode 100644 docs/plugins.md create mode 100644 docs/web_api.md create mode 100644 moonraker/app.py create mode 100644 moonraker/authorization.py create mode 100644 moonraker/moonraker.py create mode 100644 moonraker/plugins/__init__.py create mode 100644 moonraker/plugins/file_manager.py create mode 100644 moonraker/plugins/gcode_apis.py create mode 100644 moonraker/plugins/machine.py create mode 100644 moonraker/plugins/paneldue.py create mode 100644 moonraker/plugins/shell_command.py create mode 100644 moonraker/plugins/temperature_store.py create mode 100644 moonraker/utils.py create mode 100644 moonraker/websockets.py create mode 100644 scripts/extract_metadata.py create mode 100755 scripts/install-moonraker.sh create mode 100644 scripts/moonraker-requirements.txt create mode 100755 scripts/moonraker-start.sh create mode 100755 scripts/uninstall-moonraker.sh create mode 100644 test/client/index.html create mode 100644 test/client/js/json-rpc.js create mode 100644 test/client/js/main.js diff --git a/README.md b/README.md index 9bc4d5c..e98e6fe 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -# moonraker - Web API Server for Klipper +# Moonraker - API Web Server for Klipper + +Moonraker is a Python 3 based web server that exposes APIs with which +client applications may use interact with Klipper. Communcation between +the Klippy host and Moonraker is done over a Unix Domain Socket. + +Moonraker depends on Tornado for its server functionality. Moonraker +does not come bundled with a client, you will need to install one, +such as [Mainsail](https://github.com/meteyou/mainsail). diff --git a/docs/dev_changelog.md b/docs/dev_changelog.md new file mode 100644 index 0000000..b6ac07e --- /dev/null +++ b/docs/dev_changelog.md @@ -0,0 +1,294 @@ +### Moonraker Version .08-alpha - 7/2/2020 +- Moonraker has moved to its own repo. +- Python 3 support has been added. +- API Key management has moved from Klippy to Moonraker +- File Management has moved from Klippy to Moonraker. All static files are now + located in the the `/server/files` root path: + - klippy.log - `/server/files/klippy.log` + - moonraker.log - `/server/files/moonraker.log` + - gcode files - `/server/files/gcodes/(.*)` + Note that the new file manager will be capable of serving and listing files + in directories aside from "gcodes". +- Added basic plugin support +- Added metadata support for SuperSlicer +- 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. + They should instead use the new API which exposes this functionality. +- New APIs: + - `POST /printer/emergency_stop` - `post_printer_emergency_stop` + - `GET /server/files/metadata` - `get_metadata` + - `GET /server/files/directory` + - `POST /server/files/directory` + - `DELETE /server/files/directory` +- The following API changes have been made: + | Previous URI | New URI | Previous JSON_RPC method | New JSON_RPC method | + |--------------|---------|--------------------------| --------------------| + | GET /printer/objects | GET /printer/objects/list | get_printer_objects | get_printer_objects_list | + | GET /printer/subscriptions | GET /printer/objects/subscription | get_printer_subscriptions | get_printer_objects_subscription | + | POST /printer/subscriptions | POST /printer/objects/subscription | post_printer_subscriptions | post_printer_objects_subscription | + | GET /printer/status | GET /printer/objects/status | get_printer_status | get_printer_objects_status | + | POST /printer/gcode | POST /printer/gcode/script | post_printer_gcode | post_printer_gcode_script | + | GET /printer/klippy.log | GET /server/files/klippy.log | | | + | GET /server/moonraker.log | GET /server/files/moonraker.log | | | + | GET /printer/files | GET /server/files/list | get_printer_files | get_file_list | + | POST /printer/files/upload | POST /server/files/upload | | | + | GET /printer/files/ | GET /server/files/gcodes/ | | | + | DELETE /printer/files/ | DELETE /server/files/ | | | + | GET /printer/endstops | GET /printer/query_endstops/status | get_printer_endstops | get_printer_query_endstops_status | + +### Moonraker Version .07-alpha - 5/7/2020 +- The server process is no longer managed directly by Klippy. It has moved + into its own process dubbed Moonraker. Please see README.md for + installation instructions. +- API Changes: + - `/printer/temperature_store` is now `/server/temperature_store`, or + `get_server_temperature_store` via the websocket + - `/printer/log` is now `/printer/klippy.log` + - `/server/moonraker.log` has been added to fetch the server's log file +- Klippy Changes: + - The remote_api directory has been removed. There is now a single + 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 + when an error is generated attempting to upload or delete a file +- The nginx configuration will need the following additional section: + ``` + location /server { + proxy_pass http://apiserver/server; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Scheme $scheme; + } + ``` + +### Version .06-alpha - 5/4/2020 +- Add `/machine/reboot` and `/machine/shutdown` endpoints. These may be used + to reboot or shutdown the host machine +- Fix issue where websocket was blocked on long transactions, resulting in the + connection being closed +- Log all client requests over the websocket +- Add `/printer/temperature_store` endpoint. Clients may use this to fetch + stored temperature data. By default the store for each temperature sensor + is updated every 1s, with the store holding 20 minutes of data. + +### Version .05-alpha - 04/23/2020 +- The `[web_server]` module has been renamed to `[remote_api]`. Please update + printer.cfg accordingly +- Static files no longer served by the API server. As a result, there is + no `web_path` option in `[remote_api]`. +- The server process now now forwards logging requests back to the Klippy + Host, thus all logging is done in klippy.log. The temporary endpoint serving + klippy_server.log has been removed. +- `/printer/info` now includes two additional keys: + - `error_detected` - Boolean value set to true if a host error has been + detected + - `message` - The current Klippy State message. If an error is detected this + message may be presented to the user. This is the same message returned + when by the STATUS gcode. +- The server process is now launched immediately after the config file is read. + 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: + - `/websocket` + - `/printer/info` + - `/printer/restart` + - `/printer/firmware_restart` + - `/printer/log` + - `/printer/gcode` + - `/access/api_key` + - `/access/oneshot_token` + The following startup sequence is recommened 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 + check `error_detected`. If not ready and no error, continue querying on + a timer until the printer is either ready or an error is detected. + - After the printer has identified itself as ready make subscription requests, + get the current file list, etc + - If the websocket disconnects the client can assume that the server is shutdown. + It should consider the printer's state to be NOT ready and try reconnecting to + the websocket until successful. + +### Version .04-alpha - 04/20/2020 +- Add `/printer/gcode/help` endpoint to gcode.py +- Allow the clients to fetch .json files in the root web directory +- Add support for detailed print tracking to virtual_sdcard.py. This + includes filament usage and print time tracking +- Add new file_manager.py module for advanced gcode file management. Gcode + files may exist in subdirectories. This module also supports extracting + metadata from gcode files. +- Clean up API registration. All endpoints are now registered by Klippy + host modules outside of static files and `/api/version`, which is used for + compatibility with Octoprint's legacy file upload API. +- The server now runs in its own process. Communication between the Host and + the server is done over a duplex pipe. Currently this results in a second + log file being generated specifically for the server at + `/tmp/klippy_server.log`. This is likely a temporary solution, and as such + a temporary endpoint has been added at `/printer/klippy_server.log`. Users + can use the browser to download the log by navigating to + `http:///printer/klippy_server.log`. + +### Version .03-alpha - 03/09/2020 +- Require that the configured port be above 1024. +- Fix hard crash if the webserver fails to start. +- Fix file uploads with names containing whitespace +- Serve static files based on their relative directory, ie a request + for "/js/main.js" will now look for the files in "/js/main.js". +- 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 + changes: + - The `cancel_gcode`, `pause_gcode`, and `resume_gcode` options have + been removed from the [web_server] section. + - The `/printer/pause` and `/printer/resume` endpoints will run the "PAUSE" + and "RESUME" gcodes respectively. These gcodes can be overridden by a + gcode_macro to run custom PAUSE and RESUME commands. For example: + ``` + [gcode_macro PAUSE] + rename_existing: BASE_PAUSE + gcode: + {% if not printer.pause_resume.is_paused %} + M600 + {% endif %} + + [gcode_macro M600] + default_parameter_X: 50 + default_parameter_Y: 0 + default_parameter_Z: 10 + gcode: + SET_IDLE_TIMEOUT TIMEOUT=18000 + {% if not printer.pause_resume.is_paused %} + BASE_PAUSE + {% endif %} + G1 E-.8 F2700 + G91 + G1 Z{Z} + G90 + G1 X{X} Y{Y} F3000 + ``` + If you are calling "PAUSE" in any other macro of config section, please + remember that it will execute the macro. If that is not your intention, + change "PAUSE" in those sections to the renamed version, in the example + above it is BASE_PAUSE. + - The cancel endpoint runs a "CANCEL_PRINT" gcode. Users will need to + define their own gcode macro for this + - Remove "notify_paused_state_changed" and "notify_printer_state_changed" + events. The data from these events can be fetched via status + subscriptions. + - "idle_timeout" and "pause_resume" now default to tier 1 status updates, + which sets their default refresh time is 250ms. + - Some additional status attributes have been added to virtual_sdcard.py. At + the moment they are experimental and subject to change: + - 'is_active' - returns true when the virtual_sdcard is processing. Note + that this will return false when the printer is paused + - 'current_file' - The name of the currently loaded file. If no file is + loaded returns an empty string. + - 'print_duration' - The approximate duration (in seconds) of the current + print. This value does not include time spent paused. Returns 0 when + no file is loaded. + - 'total_duration' - The total duration of the current print, including time + spent paused. This can be useful for approximating the local time the + print started Returns 0 when no file is loaded. + - 'filament_used' - The approximate amount of filament used. This does not + include changes to flow rate. Returns 0 when no file is loaded. + - 'file_position' - The current position (in bytes) of the loaded file + Returns 0 when no file is loaded. + - 'progress' - This attribute already exists, however it has been changed + to retain its value while the print is paused. Previously it would reset + to 0 when paused. Returns 0 when no file is loaded. + +### Version .02-alpha - 02/27/2020 +- Migrated Framework and Server from Bottle/Eventlet to Tornado. This + resolves an issue where the server hangs for a period of time if the + network connection abruptly drops. +- A `webhooks` host module has been created. Other modules can use this + the webhooks to register endpoints, even if the web_server is not + configured. +- Two modules have been renamed, subscription_handler.py is now + status_handler.py and ws_handler.py is now ws_manager.py. These names + more accurately reflect their current functionality. +- Tornado Websockets support string encoded frames. Thus it is no longer + necessary for clients to use a FileReader object to convert incoming + 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: + - `GET /printer/klippy_info` is now `GET /printer/info`. This endpoint no + longer returns host information, as that can be retreived direct via the + `location` object in javascript. Instead it returns CPU information. + - `GET /printer/objects` is no longer used to accomodate 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 + - `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 + add the specified objects and attributes to the list of subscribed objects + - Requests that query the Klippy host with additional parameters can no + longer use variable paths. For example, `POST /printer/gcode/` is no + longer valid. Parameters must be added to the query string. This currently + affects two endpoints: + - `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 + endpoints. Each method name is now generated from its comparable HTTP + request. The new method names are listed below: + | new method | old method | + |------------|------------| + | get_printer_files | get_file_list | + | get_printer_info | get_klippy_info | + | get_printer_objects | get_object_info | + | get_printer_subscriptions | get_subscribed | + | get_printer_status | get_status | + | post_printer_subscriptions | add_subscription | + | post_printer_gcode | run_gcode | + | post_printer_print_start | start_print | + | post_printer_print_pause | pause_print | + | post_printer_print_resume | resume_print | + | post_printer_print_cancel | cancel_print | + | post_printer_restart | restart | + | 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 + 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 + means that it is no longer necessary to wrap the Object in an Array. The + gcode and start print requests now look for named parameters, ie: + - gcode requests - `{jsonrpc: "2.0", method: "post_printer_gcode", + params: {script: "M117 FooBar"}, id: }` + - start print - `{jsonrpc: "2.0", method: "post_printer_print_start", + params: {filename: "my_file.gcode"}, id:}` + + +### Version .01-alpha - 02/14/2020 +- The api.py module has been refactored to contain the bottle application and + all routes within a class. Bottle is now imported and patched dynamically + within this class's constructor. This resolves an issue where the "request" + context was lost when the Klippy host restarts. +- Change the Websocket API to use the JSON-RPC 2.0 protocol. See the test + client (main.js and json-rpc.js) for an example client side implementation. +- Remove file transfer support from the websocket. Use the HTTP for all file + transfer requests. +- Add support for Klippy Host modules to register their own urls. + Query_endstops.py has been updated with an example. As a result of this + change, the endpoint for endstop query has been changed to + `/printer/extras/endstops`. +- Add support for "paused", "resumed", and "cleared" pause events. +- Add routes for downloading klippy.log, restart, and firmware_restart. +- Remove support for trailing slashes in HTTP API routes. +- Support "start print after upload" requests +- Add support for user configured request timeouts +- The test client has been updated to work with the new changes diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..d1cbaba --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,209 @@ +## Installation + +This document provides a guide on how to install Moonraker on a Raspberry +Pi running Raspian/Rasperry Pi OS. Other SBCs and/or linux distributions +may work, however they may need a custom install script. + +Klipper should be installed prior to installing Moonraker. Please see +[Klipper's Documention](https://github.com/KevinOConnor/klipper/blob/master/docs/Installation.md) +for instructions on how to do this. + +Moonraker is still in alpha development, and thus some of its dependencies +in Klipper have yet to be merged. Until this has been done it will be +necessary to add a remote and work off a developmental branch of Klipper +to correctly run Moonraker. + +``` + cd ~/klipper + git remote add arksine https://github.com/Arksine/klipper.git +``` + +Now fetch and checkout: +``` +git fetch arksine +git checkout arksine/dev-moonraker-testing +``` +Note that you are now in a detached head state and you cannot pull. Any +time you want to update to the latest version of this branch you must +repeat the two commands above. + + +For reference, if you want to switch back to the clone of the official repo: +``` +git checkout master +``` +Note that the above command is NOT part of the Moonraker install procedure. + +You can now install the Moonraker application: +``` +cd ~ +git clone https://github.com/Arksine/moonraker.git +``` + +If you have an older version of moonraker installed, it must be removed: +``` +cd ~/moonraker/scripts +./uninstall_moonraker.sh +``` + +Finally, run moonraker's install script: +``` +cd ~/moonraker/scripts +./install_moonraker.sh +``` + +When the script completes it should start both Moonraker and Klipper. In +`klippy.log` you should find the following entry:\ +`Moonraker: server connection detected` + +Currently Moonraker is responsible for creating the Unix Domain Socket, +so so it must be started first for Klippy to connect. In any instance +where Klipper was started first simply restart the klipper service. +``` +sudo service klipper restart +``` +After the connection is established Klippy will register API endpoints and +send configuration to the server. Once the initial configuration is sent +to Moonraker its configuration will be retained when Klippy disconnects +(either through a restart or by stopping the service), and updated when +Klippy reconnects. + +# Configuration +The host, port, log file location, socket file location and api key file +are all specified via command arguments: +``` +usage: moonraker.py [-h] [-a
] [-p ] [-s ] + [-l ] [-k ] + +Moonraker - Klipper API Server + +optional arguments: + -h, --help show this help message and exit + -a
, --address
+ host name or ip to bind to the Web Server + -p , --port + port the Web Server will listen on + -s , --socketfile + file name and location for the Unix Domain Socket + -l , --logfile + log file name and location + -k , --apikey + API Key file location +``` + +The default configuration is: +- address = 0.0.0.0 (Bind to all interfaces) +- port = 7125 +- socketfile = /tmp/moonraker +- logfile = /tmp/moonraker.log +- apikeyfile = ~/.moonraker_api_key + +It is recommended to use the defaults, however one may change these +arguments by editing `/etc/default/moonraker`. + +All other configuration is sent to the server via Klippy, thus it is done in +printer.cfg. A basic configuration that authorizes clients on a range from +192.168.1.1 - 192.168.1.254 is as follows: +``` +[moonraker] +trusted_clients: + 192.168.1.0/24 +``` + +Below is a detailed explanation of all options currently available: +``` +#[moonraker] +#require_auth: True +# Enables Authorization. When set to true, only trusted clients and +# requests with an API key are accepted. +#enable_cors: False +# Enables CORS support. If serving static files from a different http +# server then CORS will need to be enabled. +#trusted_clients: +# A list of new line separated ip addresses, or ip ranges, that are trusted. +# Trusted clients are given full access to the API. Note that ranges must +# be expressed in 24-bit CIDR notation, where the last segment is zero: +# 192.168.1.0/24 +# The above example will allow 192.168.1.1 - 192.168.1-254. Note attempting +# to use a non-zero value for the last IP segement or different bit value will +# result in a configuration error. +#request_timeout: 5. +# The amount of time (in seconds) a client request has to process before the +# server returns an error. This timeout does NOT apply to gcode requests. +# Default is 5 seconds. +#long_running_gcodes: +# BED_MESH_CALIBRATE, 120. +# M104, 200. +# A list of gcodes that will be assigned their own timeout. The list should +# be in the format presented above, where the first item is the gcode name +# and the second item is the timeout (in seconds). Each pair should be +# separated by a newline. The default is an empty list where no gcodes have +# a unique timeout. +#long_running_requests: +# gcode/script, 60. +# pause_resume/pause, 60. +# pause_resume/resume, 60. +# pause_resume/cancel, 60. +# A list of requests that will be assigned their own timeout. The list +# should be formatted in the same manner as long_running_gcodes. The +# default is matches the example shown above. +#status_tier_1: +# toolhead +# gcode +#status_tier_2: +# fan +#status_tier_3: +# extruder +# virtual_sdcard +# Subscription Configuration. By default items in tier 1 are polled every +# 250 ms, tier 2 every 500 ms, tier 3 every 1s, tier 4 every 2s, tier +# 5 every 4s, tier 6 every 8s. +#tick_time: .25 +# This is the base interval used for status tier 1. All other status tiers +# are calculated using the value defined by tick_time (See below for more +# information). Default is 250ms. +``` + +The "status tiers" are used to determine how fast each klippy object is allowed +to be polled. Each tier is calculated using the `tick_time` option. There are +6 tiers, `tier_1 = tick_time` (.25s), `tier_2 = tick_time*2` (.5s), +`tier_3 = tick_time*4` (1s), `tier_4 = tick_time*8` (2s), +`tier_5 = tick_time*16` (4s), and `tier_6 = tick_time*16` (8s). This method +was chosen to provide some flexibility for slower hosts while making it easy to +batch subscription updates together. + +## Plugin Configuration +The core plugins are configured via the primary configuration above. Optional +plugins each need their own configuration. Currently the only optional plugin +available is the `paneldue` plugin, which can be configured as follows: + +``` +[moonraker_plugin paneldue] +serial: /dev/ttyAMA0 +baud: 57600 +machine_name: Voron 2 +macros: + LOAD_FILAMENT + UNLOAD_FILAMENT + PREHEAT_CHAMBER + TURN_OFF_MOTORS + TURN_OFF_HEATERS + PANELDUE_BEEP FREQUENCY=500 DURATION=1 +``` + +Most options above are self explanatory. The "macros" option can be used +to specify commands (either built in or gcode_macros) that will show up +in the PanelDue's "macro" menu. + +Note that buzzing the piezo requires the following gcode_macro: +``` +[gcode_macro PANELDUE_BEEP] +# Beep frequency +default_parameter_FREQUENCY: 300 +# Beep duration in seconds +default_parameter_DURATION: 1. +gcode: + { printer.moonraker.action_call_remote_method( + "paneldue_beep", frequency=FREQUENCY|int, + duration=DURATION|float) } +``` \ No newline at end of file diff --git a/docs/plugins.md b/docs/plugins.md new file mode 100644 index 0000000..5f9055e --- /dev/null +++ b/docs/plugins.md @@ -0,0 +1,3 @@ +## Plugins + +Documentation Forthcoming diff --git a/docs/web_api.md b/docs/web_api.md new file mode 100644 index 0000000..199fbe7 --- /dev/null +++ b/docs/web_api.md @@ -0,0 +1,654 @@ +# API + +Most API methods are supported over both the Websocket and HTTP transports. +File Transfer and "/access" requests are only available over HTTP. The +Websocket is required to receive printer 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. + +Note that all HTTP responses are returned as a json encoded object in the form +of: + +`{result: }` + +The command matches the original command request, the result is the return +value generated from the request. + +Websocket requests are returned in JSON-RPC format: +`{jsonrpc: "2.0", "result": , id: }` + +HTML requests will recieve a 500 status code on error, accompanied by +the specific error message. + +Websocket requests that result in an error will receive a properly formatted +JSON-RPC response: +`{jsonrpc: "2.0", "error": {code: , message: }, id: }` +Note that under some circumstances it may not be possible for the server to +return a request ID, such as an improperly formatted json request. + +The `test\client` folder 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-rcp.js). + +## Printer Administration + +### Get Klippy host information: +- HTTP command:\ + `GET /printer/info` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_printer_info", id: }` + +- Returns:\ + An object containing the build version, cpu info, and if the Klippy + process is ready for operation. The latter is useful when a client connects + after the klippy state event has been broadcast. + + `{version: "", cpu: "", is_ready: , + hostname: "", error_detected: , + message: ""}` + +### Emergency Stop +- HTTP command:\ + `POST /printer/emergency_stop` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_emergency_stop", id: }` + +- Returns:\ + `ok` + +### Restart the host +- HTTP command:\ + `POST /printer/restart` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_restart", id: }` + +- Returns:\ + `ok` + +### Restart the firmware (restarts the host and all connected MCUs) +- HTTP command:\ + `POST /printer/firmware_restart` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_firmware_restart", id: }` + +- Returns:\ + `ok` + +## Printer Status + +### Request available printer objects and their attributes: +- HTTP command:\ + `GET /printer/objects/list` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_printer_objects_list", id: }` + +- Returns:\ + An object containing key, value pairs, where the key is the name of the + Klippy module available for status query, and the value is an array of + strings containing that module's available attributes. + + ```json + { gcode: ["busy", "gcode_position", ...], + toolhead: ["position", "status"...], ...} + ``` + +### Request currently subscribed objects: +- HTTP command: + `GET /printer/objects/subscription` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_printer_objects_subscription", id: }` + +- Returns:\ + An object of the similar that above, however the format of the `result` + value is changed to include poll times: + + ```json + { objects: { + gcode: ["busy", "gcode_position", ...], + toolhead: ["position", "status"...], + ...}, + poll_times: { + gcode: .25, + toolhead: .25, + ...} + } + ``` + +### Request the a status update for an object, or group of objects: +- HTTP command:\ + `GET /printer/objects/status?gcode` + + The above will fetch a status update for all gcode attributes. The query + string can contain multiple items, and specify individual attributes: + + `?gcode=gcode_position,busy&toolhead&extruder=target` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_printer_objects_status", params: + {gcode: [], toolhead: ["position", "status"]}, id: }` + + Note that an empty array will fetch all available attributes for its key. + +- Returns:\ + An object where the top level keys are the requested Klippy objects, as shown + below: + + ```json + { gcode: { + busy: true, + gcode_position: [0, 0, 0 ,0], + ...}, + toolhead: { + position: [0, 0, 0, 0], + status: "Ready", + ...}, + ...} + ``` +### Subscribe to a status request or a batch of status requests: +- HTTP command:\ + `POST /printer/objects/subscription?gcode=gcode_position,bus&extruder=target` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_objects_subscription", params: + {gcode: [], toolhead: ["position", "status"]}, id: }` + +- Returns:\ + An acknowledgement that the request has been received: + + `ok` + + The actual status updates will be sent asynchronously over the websocket. + +### Query Endstops +- HTTP command:\ + `GET /printer/query_endstops/status` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_printer_query_endstops_status", id: }` + +- Returns:\ + An object containing the current endstop state, with each attribute in the + format of `endstop:`, where "state" can be "open" or "TRIGGERED", for + example: + +```json + {x: "TRIGGERED", + y: "open", + z: "open"} +``` + +### Fetch stored temperature data +- HTTP command:\ + `GET /server/temperature_store` + +- Websocket command: + `{jsonrpc: "2.0", method: "get_temperature_store", id: }` + +- 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. + +## Gcode Controls + +### Run a gcode: +- HTTP command:\ + `POST /printer/gcode/script?script=` + + For example,\ + `POST /printer/gcode/script?script=RESPOND MSG=Hello`\ + Will echo "Hello" to the terminal. + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_gcode_script", + params: {script: }, id: }` + +- Returns:\ + An acknowledgement that the gcode has completed execution: + + `ok` + +### Get GCode Help +- HTTP command:\ + `GET /printer/gcode/help` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_printer_gcode_help", + params: {script: }, id: }` + +- Returns:\ + An object where they keys are gcode handlers and values are the associated + help strings. Note that help strings are not available for basic gcode + handlers such as G1, G28, etc. + +## Print Management + +### Print a file +- HTTP command:\ + `POST /printer/print/start?filename=` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_print_start", + params: {filename: , id:}` + +- Returns:\ + `ok` on success + +### Pause a print +- HTTP command:\ + `POST /printer/print/pause` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_print_pause", id: }` + +- Returns:\ + `ok` + +### Resume a print +- HTTP command:\ + `POST /printer/print/resume` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_print_resume", id: }` + +- Returns:\ + `ok` + +### Cancel a print +- HTTP command:\ + `POST /printer/print/cancel` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_printer_print_cancel", id: }` + +- Returns:\ + `ok` + +## Machine Commands + +### Shutdown the Operating System +- HTTP command:\ + `POST /machine/shutdown` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_machine_shutdown", id: }` + +- Returns:\ + No return value as the server will shut down upon execution + +### Reboot the Operating System +- HTTP command:\ + `POST /machine/reboot` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "post_machine_reboot", id: }` + +- Returns:\ + No return value as the server will shut down upon execution + + +## File Operations + +While all file transfer operations are available via the HTTP API, only +"get_file_list" and "get_metadata" are available over the websocket. Aside from +the log files, currently the only root available is "gcodes" (at `http:\\host\server\files\gcodes\*`), however support for other "root" +directories may be added in the future. File upload, file delete, and +directory manipulation(mkdir and rmdir) will only be available on the "gcodes" +root. + +### List Available Files +Walks through a directory and fetches all files. All file names include a +path relative to the specified "root". Note that if the query st + +- HTTP command:\ + `GET /server/files/list?root=gcodes` + + If the query string is omitted then the command will return + the "gcodes" file list by default. + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_file_list", params: {root: "gcodes"} + , id: }` + + If `params` are are omitted then the command will return the "gcodes" + file list. + +- Returns:\ + A list of objects containing file data in the following format: + +```json +[ + {filename: "file name", + size: , + modified: "last modified date", + ...] +``` + +### Get GCode Metadata + Get file metadata for a specified gcode file. If the file is located in + a subdirectory, then the file name should include the path relative to + the "gcodes" root. For example, if the file is located at:\ + `http://host/server/files/gcodes/my_sub_dir/my_print.gcode` + Then the filename should be `my_sub_dir/my_print.gcode`. + +- HTTP command:\ + `GET /server/files/metadata?filename=` + +- Websocket command:\ + `{jsonrpc: "2.0", method: "get_metadata", params: {filename: "filename"} + , id: }` + +- 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 + { + filename: "file name", + size: , + modified: "last modified date", + slicer: "Slicer Name", + first_layer_height: , + layer_height: , + object_height: , + estimated_time: