[feature] Parameterized macros (#16)
Adds the option to have parameters in Macros and show a dialog to the user on execution to change them. Here's and example: PID_CALIBRATE HEATER={label:Heater, default:extruder, options:extruder|extruder1} TARGET={label:Temperature, unit:°C, default:190} WRITE_FILE={label:WriteFile, default:0, options:0|1}
This commit is contained in:
parent
33ae991612
commit
769b8a53ed
|
@ -209,6 +209,12 @@ class KlipperPlugin(
|
||||||
name="Performance Graph",
|
name="Performance Graph",
|
||||||
template="klipper_graph_dialog.jinja2",
|
template="klipper_graph_dialog.jinja2",
|
||||||
custom_bindings=True
|
custom_bindings=True
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
type="generic",
|
||||||
|
name="Macro Dialog",
|
||||||
|
template="klipper_param_macro_dialog.jinja2",
|
||||||
|
custom_bindings=True
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -221,6 +227,7 @@ class KlipperPlugin(
|
||||||
"js/klipper_leveling.js",
|
"js/klipper_leveling.js",
|
||||||
"js/klipper_pid_tuning.js",
|
"js/klipper_pid_tuning.js",
|
||||||
"js/klipper_offset.js",
|
"js/klipper_offset.js",
|
||||||
|
"js/klipper_param_macro.js",
|
||||||
"js/klipper_graph.js"
|
"js/klipper_graph.js"
|
||||||
],
|
],
|
||||||
css=["css/klipper.css"],
|
css=["css/klipper.css"],
|
||||||
|
|
|
@ -12,6 +12,7 @@ $(function() {
|
||||||
self.loginState = parameters[1];
|
self.loginState = parameters[1];
|
||||||
self.connectionState = parameters[2];
|
self.connectionState = parameters[2];
|
||||||
self.levelingViewModel = parameters[3];
|
self.levelingViewModel = parameters[3];
|
||||||
|
self.paramMacroViewModel = parameters[4];
|
||||||
|
|
||||||
self.shortStatus = ko.observable();
|
self.shortStatus = ko.observable();
|
||||||
self.logMessages = ko.observableArray();
|
self.logMessages = ko.observableArray();
|
||||||
|
@ -52,8 +53,22 @@ $(function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.executeMacro = function(macro) {
|
self.executeMacro = function(macro) {
|
||||||
OctoPrint.control.sendGcode(macro.macro());
|
var paramObjRegex = /{(.*?)}/g;
|
||||||
|
|
||||||
|
if (macro.macro().match(paramObjRegex) == null) {
|
||||||
|
OctoPrint.control.sendGcode(
|
||||||
|
macro.macro().replace(/(?:\r\n|\r|\n)/g, " ")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.paramMacroViewModel.process(macro);
|
||||||
|
|
||||||
|
var dialog = $("#klipper_macro_dialog");
|
||||||
|
dialog.modal({
|
||||||
|
show: 'true',
|
||||||
|
backdrop: 'static'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onGetStatus = function() {
|
self.onGetStatus = function() {
|
||||||
|
@ -97,7 +112,13 @@ $(function() {
|
||||||
|
|
||||||
OCTOPRINT_VIEWMODELS.push({
|
OCTOPRINT_VIEWMODELS.push({
|
||||||
construct: KlipperViewModel,
|
construct: KlipperViewModel,
|
||||||
dependencies: ["settingsViewModel", "loginStateViewModel", "connectionViewModel", "klipperLevelingViewModel"],
|
dependencies: [
|
||||||
|
"settingsViewModel",
|
||||||
|
"loginStateViewModel",
|
||||||
|
"connectionViewModel",
|
||||||
|
"klipperLevelingViewModel",
|
||||||
|
"klipperMacroDialogViewModel"
|
||||||
|
],
|
||||||
elements: ["#tab_plugin_klipper_main", "#sidebar_plugin_klipper", "#navbar_plugin_klipper"]
|
elements: ["#tab_plugin_klipper_main", "#sidebar_plugin_klipper", "#navbar_plugin_klipper"]
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,76 @@
|
||||||
|
// OctoPrint Klipper Plugin
|
||||||
|
//
|
||||||
|
// Copyright (C) 2018 Martin Muehlhaeuser <github@mmone.de>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
function KlipperMacroDialogViewModel(parameters) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.parameters = ko.observableArray();
|
||||||
|
self.interpolatedCmd;
|
||||||
|
self.macro;
|
||||||
|
self.macroName = ko.observable();
|
||||||
|
|
||||||
|
var paramObjRegex = /{(.*?)}/g;
|
||||||
|
var keyValueRegex = /(\w*)\s*:\s*([\w°"|]*)/g;
|
||||||
|
|
||||||
|
self.process = function(macro) {
|
||||||
|
self.macro = macro.macro();
|
||||||
|
self.macroName(macro.name());
|
||||||
|
|
||||||
|
var matches = self.macro.match(paramObjRegex);
|
||||||
|
var params = [];
|
||||||
|
|
||||||
|
for (var i=0; i < matches.length; i++) {
|
||||||
|
var obj = {};
|
||||||
|
var res = keyValueRegex.exec(matches[i]);
|
||||||
|
|
||||||
|
while (res != null) {
|
||||||
|
if("options" == res[1]) {
|
||||||
|
obj["options"] = res[2].split("|");
|
||||||
|
} else {
|
||||||
|
obj[res[1]] = res[2];
|
||||||
|
}
|
||||||
|
res = keyValueRegex.exec(matches[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!("label" in obj)) {
|
||||||
|
obj["label"] = "Input " + (i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!("unit" in obj)) {
|
||||||
|
obj["unit"] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if("default" in obj) {
|
||||||
|
obj["value"] = obj["default"];
|
||||||
|
}
|
||||||
|
|
||||||
|
params.push(obj);
|
||||||
|
}
|
||||||
|
self.parameters(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.executeMacro = function() {
|
||||||
|
var i=-1;
|
||||||
|
|
||||||
|
function replaceParams(match) {
|
||||||
|
i++;
|
||||||
|
return self.parameters()[i]["value"];
|
||||||
|
}
|
||||||
|
|
||||||
|
expanded = self.macro.replace(paramObjRegex, replaceParams)
|
||||||
|
expanded = expanded.replace(/(?:\r\n|\r|\n)/g, " ");
|
||||||
|
|
||||||
|
OctoPrint.control.sendGcode(expanded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OCTOPRINT_VIEWMODELS.push({
|
||||||
|
construct: KlipperMacroDialogViewModel,
|
||||||
|
dependencies: [],
|
||||||
|
elements: ["#klipper_macro_dialog"]
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div id="klipper_macro_dialog" class="modal hide fade small" tabindex="-1" role="dialog" aria-labelledby="klipper_macro_dialog_label" aria-hidden="true">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h3 id="klipper_pid_tuning_dialog_label">{{ _('Run - ') }}<span data-bind="text: macroName"></span></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="control-group" data-bind="foreach: parameters">
|
||||||
|
<label class="control-label" data-bind="text: label"></label>
|
||||||
|
<div class="controls">
|
||||||
|
<!-- ko if: $data.hasOwnProperty("options") -->
|
||||||
|
<div class="input-append">
|
||||||
|
<select data-bind="options: options, value: value"></select>
|
||||||
|
<span class="add-on" data-bind="text: unit"></span>
|
||||||
|
</div>
|
||||||
|
<!-- /ko -->
|
||||||
|
<!-- ko if: !$data.hasOwnProperty("options") -->
|
||||||
|
<div class="input-append">
|
||||||
|
<input type="text" class="input-block-level span2" data-bind="value: value">
|
||||||
|
<span class="add-on" data-bind="text: unit"></span>
|
||||||
|
</div>
|
||||||
|
<!-- /ko -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<button class="btn btn-block" data-bind="click: executeMacro" data-dismiss="modal">
|
||||||
|
<i class="icon-cross"></i> {{ _('OK') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -6,6 +6,7 @@
|
||||||
<li><a href="#conf" data-toggle="tab">Klipper Configuration</a></li>
|
<li><a href="#conf" data-toggle="tab">Klipper Configuration</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
<!-- Basics -->
|
||||||
<div class="tab-pane active" id="basic">
|
<div class="tab-pane active" id="basic">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{ _('Serial Port') }}</label>
|
<label class="control-label">{{ _('Serial Port') }}</label>
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Macros -->
|
||||||
<div class="tab-pane" id="macros">
|
<div class="tab-pane" id="macros">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
@ -69,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label class="control-label">{{ _('GCODE') }}</label>
|
<label class="control-label">{{ _('Command') }}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
|
@ -86,7 +88,21 @@
|
||||||
<a href='#' data-bind='click: addMacro' class="fa fa-plus-circle"></a> {{ _('Add Macro') }}
|
<a href='#' data-bind='click: addMacro' class="fa fa-plus-circle"></a> {{ _('Add Macro') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<span class="help-block">
|
||||||
|
To show a dialog that asks for parameters you can write your macro like in the following example:<br>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<pre>
|
||||||
|
PID_CALIBRATE
|
||||||
|
HEATER={label:Heater, default:extruder, options:extruder|extruder1}
|
||||||
|
TARGET={label:Temperature, unit:°C, default:190}
|
||||||
|
WRITE_FILE={label:WriteFile, default:0, options:0|1}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Leveling -->
|
||||||
<div class="tab-pane" id="level">
|
<div class="tab-pane" id="level">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<span class="help-block">
|
<span class="help-block">
|
||||||
|
@ -164,6 +180,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Klipper Conf -->
|
||||||
<div class="tab-pane" id="conf">
|
<div class="tab-pane" id="conf">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<textarea id="plugin-klipper-config" rows="31" class="block" data-bind="value: settings.settings.plugins.klipper.config"></textarea>
|
<textarea id="plugin-klipper-config" rows="31" class="block" data-bind="value: settings.settings.plugins.klipper.config"></textarea>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<button class="btn btn-mini pull-right clear-btn" data-bind="click: onClearLog, enable: isActive()"
|
<button class="btn btn-mini pull-right clear-btn" data-bind="click: onClearLog, enable: isActive()"
|
||||||
title="Clear Log">
|
title="Clear Log">
|
||||||
<i class="fa fa-trash"></i> {{ _('Clear') }}
|
<i class="fa fa-trash"></i>{{ _('Clear') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="span4">
|
<div class="span4">
|
||||||
|
|
Loading…
Reference in New Issue