WIP: streamlining and documenting custom controls

This commit is contained in:
Gina Häußge 2015-03-09 08:51:49 +01:00
parent dee205fc2f
commit 8f6784d5d2
7 changed files with 352 additions and 14 deletions

View file

@ -0,0 +1,135 @@
.. _sec-configuration-custom_controls:
Custom Controls
===============
OctoPrint allows you to add custom controls to the "Control" tab of its interface. Control types reach from simple
buttons which trigger sending of one or more lines of GCODE to the printer over more complex controls allowing
parameterization of these commands with values entered by the user to full blown GCODE script templates backed by
`Jinja2 <http://jinja.pocoo.org/>`_.
.. note::
At the current time, the configuration of custom controls is only possible through manually editing OctoPrint's
``config.yaml`` file.
To make sure that your ``config.yaml`` stays valid when adding custom controls, don't hesitate to take a look at the
:ref:`YAML Primer <sec-configuration-yaml>`.
.. _sec-configuration-custom_controls-configyaml:
config.yaml
-----------
Custom controls are configured within ``config.yaml`` in a ``controls`` section which basically represents a hierarchical
structure of all configured custom controls of various types.
The following example defines a control for enabling the cooling fan with a variable speed defined by the user
(default 255 and selectable through a slider UI element) and a control for disabling the fan, all within a section named
"Fan", two example controls with multiple commands in a section "Example for multiple commands" and a command with printer
feedback evaluation for the result of the M114 "Get Position" gcode inside a section named "Reporting".
.. sourcecode:: yaml
controls:
- name: Fan
type: section
children:
- name: Enable Fan
type: parametric_command
command: M106 S%(speed)s
input:
- name: Speed (0-255)
parameter: speed
default: 255
slider:
min: 0
max: 255
- name: Disable Fan
type: command
command: M107
- name: Example for multiple commands
type: section
children:
- name: Move X (static)
type: commands
commands:
- G91
- G1 X10 F3000
- G90
- name: Move X (parametric)
type: parametric_commands
commands:
- G91
- G1 X%(distance)s F%(speed)s
- G90
input:
- default: 10
name: Distance
parameter: distance
- default: 3000
name: Speed
parameter: speed
- name: Reporting
type: section
children:
- command: M114
name: Get Position
type: feedback_command
regex: "X:([0-9.]+) Y:([0-9.]+) Z:([0-9.]+) E:([0-9.]+)"
template: "Position: X={0}, Y={1}, Z={2}, E={3}"
Adding this to ``config.yaml``, restarting the OctoPrint server and switching to the "Control" tab within its
interface yields the following visual representation:
.. _fig-configuration-custom_controls-example:
.. figure:: ../images/configuration-custom_controls-example.png
:align: center
:alt: The rendered output created through the example configuration
As you can see there are quite a number of different custom controls already in this small example, each with their own
attributes: :ref:`sections <sec-configuration-custom_controls-types-section>`, :ref:`commands <sec-configuration-custom_controls-commands>`,
:ref:`parametric commands <sec-configuration-custom_controls-types-parametric_command>` and
:ref:`feedback commands <sec-configuration-custom_controls-types-feedback_command>`. Two attributes are common for all
of the types: ``name`` and ``type``.
.. _sec-configuration-custom_controls-types:
Types
-----
.. _sec-configuration-custom_controls-types:
Sections
........
.. _sec-configuration-custom_controls-types:
Rows
....
.. _sec-configuration-custom_controls-types:
Section rows
............
.. _sec-configuration-custom_controls-types:
Commands
........
.. _sec-configuration-custom_controls-types-parametric_command:
Parametric commands
...................
.. _sec-configuration-custom_controls-types-script:
Scripts
.......
.. _sec-configuration-custom_controls-types-feedback_command:
Feedback commands
.................

View file

@ -0,0 +1,11 @@
.. _sec-configuration:
#############
Configuration
#############
.. toctree::
:maxdepth: 2
custom_controls.rst
yaml.rst

175
docs/configuration/yaml.rst Normal file
View file

@ -0,0 +1,175 @@
.. _sec-configuration-yaml:
A YAML Primer
=============
Most of OctoPrint's configuration is done under the hood through `YAML <https://en.wikipedia.org/wiki/YAML>`_ files,
which is why it makes sense to shed some light on the basics of this data serialization format.
YAML is a text based format which excels at representing the most common of data structures in an easy and very human
readable way, which is why it was chosen for OctoPrint's configuration files. A text editor is all you need in order
to write YAML configuration files.
.. _sec-configuration-yaml-basic:
Basic Rules
-----------
First of all some basic things to know about working with YAML files:
* Never use tabs outside of quoted strings, especially not for indentation. The tab characters is illegal within
YAML files.
* Whitespace and indentation matters and plays an important part in structuring the data, so take special care
to stay consistent here.
* YAML's comments start with a `#` and go until the end of the line.
.. _sec-configuration-yaml-types:
Interesting data types
----------------------
You will probably only come across the three most basic types of data within OctoPrint's YAML files: scalars
(such as strings, integers, ...), lists and associated arrays (aka key-value-paris, aka maps, aka dictionaries).
.. _sec-configuration-yaml-types-scalar:
Scalars
.......
Scalars are the most basic of all data types and are simple string, integer, float or boolean values.
For most scalars you don't need any quotes at all, but if you need to define some piece of data which contains characters
that could be mistaken with YAML syntax you need to quote it in either double ``"`` or single ``'`` quotes for the
YAML file to stay valid. As simple rule of thumb, if your data contains any of these characters ``:-{}[]!#|>&%@`` better
quote it. Also quote it if you want a string but it could be mistaken for a valid number (integer or float) or if
it consists only of "Yes", "No", "yes", "no", "true" or "false", which would be converted to a boolean without quotes.
In double quoted strings if you need to include a literal double quote in your string you can escape it by prefixing
it with a backslash ``\`` (which you can in turn escape by itself). In single quoted strings the single quote character
can be escaped by prefixing it with another single quote, basically doubling it. Backslashes in single quoted strings
do not need to be escaped.
Quoted strings can also span across multiple lines, just indent the following lines. Note that you'll need to add a
completely empty line in order for force a line break, the data will not be actually wrapped across multiple lines
just because you spread its representation across multiple lines.
.. list-table::
- * **Data type**
* **Examples**
- * int
* .. sourcecode:: yaml
23
42
- * float
* .. sourcecode:: yaml
23.5
100.0
- * boolean
* .. sourcecode:: yaml
true
false
Yes
No
yes
no
- * string
* .. sourcecode:: yaml
a string
"some quoted string with a : colon and a { bracket and a quote \" and a backslash \\ - phew"
'some single quoted string with a single quote '' and a backslash \ - yay'
"and a multiline string - just because we can we'll make it span
across not two but four YAML lines!
Including this paragraph. But in fact it will only be two lines :)"
"23"
"42.3"
"Yes"
"No"
"true"
"false"
yes and no
true or false
.. _sec-configuration-yaml-types-lists:
Lists
.....
Lists allow to "collect" a number of similar things into one data structure. They are created by prefixing one or more
consecutive lines with a ``-``:
.. sourcecode:: yaml
- item 1
- 23.42
- 57
- true
Take special care to have all of your list items at the same indentation level!
.. _sec-configuration-yaml-types-dicts:
Dictionaries
............
Dictionaries (aka associative arrays aka maps) allow organizing the data in key value pairs, with the key and the value
being separated through a colon ``:``:
.. sourcecode:: yaml
key: value
anotherkey: another value
.. _sec-configuration-yaml-examples:
Examples
--------
Based on the three types explained above, quite complex data structures are possible:
.. sourcecode:: yaml
general:
some_setting: some_value
a_list:
- item 1
- 23.42
- 57
- true
some_flag: true
quoted_string: "This string is quoted because {we have this here} and also > this and : that"
specific:
setting1: value1
setting2: value2
subsetting21: value11
subsetting22:
- subsubsetting221
- subsubsetting222
- subsubsetting223
the_end: yes

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -16,6 +16,7 @@ Contents
.. toctree::
:maxdepth: 2
configuration/index.rst
api/index.rst
events/index.rst
plugins/index.rst

View file

@ -466,7 +466,15 @@ class Settings(object):
def _migrate_config(self):
dirty = False
for migrate in (self._migrate_event_config, self._migrate_reverse_proxy_config, self._migrate_printer_parameters, self._migrate_gcode_scripts):
migrators = (
self._migrate_event_config,
self._migrate_reverse_proxy_config,
self._migrate_printer_parameters,
self._migrate_gcode_scripts
)
for migrate in migrators:
dirty = migrate() or dirty
if dirty:
self.save(force=True)

View file

@ -122,18 +122,20 @@ $(function() {
};
self._processControl = function (control) {
if (_.startsWith(control.type, "parametric_")) {
if (control.type == "feedback_command" || control.type == "feedback") {
control.output = ko.observable("");
self.feedbackControlLookup[control.name] = control.output;
} else if (control.type == "section" || control.type == "row" || control.type == "section_row") {
control.children = self._processControls(control.children);
}
if (control.hasOwnProperty("input")) {
for (var i = 0; i < control.input.length; i++) {
control.input[i].value = ko.observable(control.input[i].default);
if (!control.input[i].hasOwnProperty("slider")) {
control.input[i].slider = false;
}
}
} else if (control.type == "feedback_command" || control.type == "feedback") {
control.output = ko.observable("");
self.feedbackControlLookup[control.name] = control.output;
} else if (control.type == "section" || control.type == "row" || control.type == "section_row") {
control.children = self._processControls(control.children);
}
var js;
@ -275,25 +277,31 @@ $(function() {
})
};
var data = undefined;
if (command.type == "command" || command.type == "parametric_command" || command.type == "feedback_command") {
if (command.hasOwnProperty("command")) {
// single command
data = {"command": command.command};
} else if (command.type == "commands" || command.type == "parametric_commands") {
} else if (command.hasOwnProperty("commands")) {
// multi command
data = {"commands": command.commands};
} else if (command.type == "script" || command.type == "parametric_script") {
} else if (command.hasOwnProperty("script")) {
data = {"script": command.script};
if (command.hasOwnProperty("context")) {
data["context"] = command.context;
}
} else {
return;
}
if (command.type == "parametric_command" || command.type == "parametric_commands" || command.type == "parametric_script") {
if (command.hasOwnProperty("input")) {
// parametric command(s)
data["parameters"] = {};
for (var i = 0; i < command.input.length; i++) {
data["parameters"][command.input[i].parameter] = command.input[i].value();
}
_.each(command.input, function(input) {
if (!input.hasOwnProperty("parameter") || !input.hasOwnProperty("value")) {
return;
}
data["parameters"][input.parameter] = input.value();
});
}
if (command.confirm) {