🦄 refactor: Restart command

-change restart command setting to work with new editor
-add migrate settings docstrings
-refactor statement for check_parse  in cfgUtils.py>save_cfg
-change knockout binding for ace editor
-change calculating for height of editor modal-body
-moved editorbuttons to modal_footer
-set focus to editor after modal shown
-load printer.cfg on start for the editor
-show Confirmation on dirty editor closing
This commit is contained in:
thelastWallE 2021-09-16 12:31:51 +02:00
parent ef6c846f56
commit c835795fe9
11 changed files with 374 additions and 296 deletions

View File

@ -139,6 +139,7 @@ class KlipperPlugin(
old_config="",
logpath="/tmp/klippy.log",
reload_command="RESTART",
restart_onsave=False,
shortStatus_navbar=True,
shortStatus_sidebar=True,
parse_check=False,
@ -146,40 +147,7 @@ class KlipperPlugin(
)
)
def on_settings_load(self):
data = octoprint.plugin.SettingsPlugin.on_settings_load(self)
configpath = os.path.expanduser(
self._settings.get(["configuration", "configpath"])
)
standardconfigfile = os.path.join(configpath, "printer.cfg")
data["config"] = cfgUtils.get_cfg(self, standardconfigfile)
util.send_message(self, "reload", "config", "", data["config"])
# send the configdata to frontend to update ace editor
return data
def on_settings_save(self, data):
if "config" in data:
if cfgUtils.save_cfg(self, data["config"], "printer.cfg"):
#load the reload command from changed data if it is not existing load the saved setting
if util.key_exist(data, "configuration", "reload_command"):
reload_command = os.path.expanduser(
data["configuration"]["reload_command"]
)
else:
reload_command = self._settings.get(["configuration", "reload_command"])
if reload_command != "manually":
# Restart klippy to reload config
self._printer.commands(reload_command)
logger.log_info(self, "Restarting Klipper.")
# we dont want to write the klipper conf to the octoprint settings
else:
# save not sure. saving to the octoprintconfig:
self._settings.set(["configuration", "temp_config"], data)
data.pop("config", None)
# save the rest of changed settings into config.yaml of octoprint
old_debug_logging = self._settings.get_boolean(["configuration", "debug_logging"])
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
@ -205,6 +173,10 @@ class KlipperPlugin(
)
def get_settings_version(self):
# Settings_Versionhistory:
# 3 = add shortstatus on navbar. migrate the navbar setting for this
# 4 = -change of configpath to only path without filename
# -add setting for restart checkbox on editor save
return 4
@ -213,35 +185,43 @@ class KlipperPlugin(
settings = self._settings
if current is None:
self.migrate_old_settings(settings)
if current is not None and current < 3:
self.migrate_configuration(
self.migrate_settings_configuration(
settings,
"shortStatus_navbar",
"navbar",
)
if current is not None and current < 4:
self.migrate_configuration(
self.migrate_settings_configuration(
settings,
"old_config",
"temp_config",
)
setting_path = settings.get(["configuration", "configpath"])
if setting_path.find("printer.cfg")!=-1:
new_setting_path = setting_path.replace("printer.cfg","")
logger.log_info(self, "migrate setting for 'configuration/configpath': " + setting_path + " -> " + new_setting_path)
settings.set(["configuration", "configpath"], new_setting_path)
cfg_path = settings.get(["configuration", "configpath"])
if cfg_path.find("printer.cfg") != -1:
new_cfg_path = cfg_path.replace("printer.cfg","")
logger.log_info(self, "migrate setting for 'configuration/configpath': " + cfg_path + " -> " + new_cfg_path)
settings.set(["configuration", "configpath"], new_cfg_path)
if settings.get(["configuration", "reload_command"]) != "manually" :
logger.log_info(self, "migrate setting for 'configuration/restart_onsave': False -> True")
settings.set(["configuration", "restart_onsave"], True)
def migrate_old_settings(self, settings):
'''
For Old settings
'''
self.migrate_settings(settings, "serialport", "connection", "port")
self.migrate_settings(settings, "replace_connection_panel", "connection", "replace_connection_panel")
self.migrate_settings(settings, "probeHeight", "probe", "height")
self.migrate_settings(settings, "probeLift", "probe", "lift")
self.migrate_settings(settings, "probeSpeedXy", "probe", "speed_xy")
self.migrate_settings(settings, "probeSpeedZ", "probe", "speed_z")
self.migrate_settings(settings, "configPath", "configpath")
self.migrate_settings(settings, "configPath", "configuration", "configpath")
if settings.has(["probePoints"]):
points = settings.get(["probePoints"])
@ -250,6 +230,14 @@ class KlipperPlugin(
settings.remove(["probePoints"])
def migrate_settings(self, settings, old, new, new2="") -> None:
"""migrate setting to setting with additional group
Args:
settings (any): instance of self._settings
old (str): the old setting to migrate
new (str): group or only new setting if there is no new2
new2 (str, optional): the new setting to migrate to. Defaults to "".
""" ''''''
if settings.has([old]):
if new2 != "":
logger.log_info(self, "migrate setting for '" + old + "' -> '" + new + "/" + new2 + "'")
@ -574,6 +562,20 @@ class KlipperPlugin(
if saved == True:
util.send_message(self, "reload", "configlist", "", "")
return flask.jsonify(saved = saved)
# restart klipper
@octoprint.plugin.BlueprintPlugin.route("/restart", methods=["POST"])
@restricted_access
@Permissions.PLUGIN_KLIPPER_CONFIG.require(403)
def restart_klipper(self):
reload_command = self._settings.get(["configuration", "reload_command"])
if reload_command != "manually":
# Restart klippy to reload config
self._printer.commands(reload_command)
logger.log_info(self, "Restarting Klipper.")
return NO_CONTENT
# APIs end

View File

@ -113,34 +113,35 @@ def save_cfg(self, content, filename="printer.cfg"):
if sys.version_info[0] < 3:
content = content.encode('utf-8')
check_parse = self._settings.get(["configuration", "parse_check"])
logger.log_debug(self, "check_parse on filesave: {}".format(check_parse))
configpath = os.path.expanduser(self._settings.get(["configuration", "configpath"]))
if filename[-4:] != ".cfg":
filename += ".cfg"
filepath = os.path.join(configpath, filename)
logger.log_debug(self, "save filepath: {}".format(filepath))
self._settings.set(["configuration", "temp_config"], content)
check = True
if check_parse:
check=check_cfg(self, content)
if check == True:
try:
logger.log_debug(self, "Writing Klipper config to {}".format(filepath))
with open(filepath, "w") as f:
f.write(content)
except IOError:
logger.log_error(self, "Error: Couldn't open Klipper config file: {}".format(filepath))
return False
else:
logger.log_debug(self, "Writen Klipper config to {}".format(filepath))
return True
finally:
f.close()
copy_cfg_to_backup(self, filepath)
else:
check_parse = self._settings.get(["configuration", "parse_check"])
logger.log_debug(self, "check_parse on filesave: {}".format(check_parse))
if check_parse and not check_cfg(self, content):
return False
try:
logger.log_debug(self, "Writing Klipper config to {}".format(filepath))
with open(filepath, "w") as f:
f.write(content)
except IOError:
logger.log_error(self, "Error: Couldn't open Klipper config file: {}".format(filepath))
return False
else:
logger.log_debug(self, "Writen Klipper config to {}".format(filepath))
return True
finally:
f.close()
copy_cfg_to_backup(self, filepath)
def check_cfg(self, data):
"""Checks the given data on parsing errors.
@ -158,37 +159,46 @@ def check_cfg(self, data):
dataToValidated.readfp(buf)
else:
dataToValidated.read_string(data)
is_float_ok(self, dataToValidated)
except configparser.Error as error:
error.message = error.message.replace("\\n","")
if sys.version_info[0] < 3:
error.message = error.message.replace("file: u","Klipper Configuration", 1)
error.message = error.message.replace("'","", 2)
error.message = error.message.replace("u'","'", 1)
else:
error.message = error.message.replace("file:","Klipper Configuration", 1)
error.message = error.message.replace("'","", 2)
logger.log_error(
self,
"Error: Invalid Klipper config file:\n"
+ "{}".format(str(error))
)
util.send_message(self, "PopUp", "warning", "Invalid Config data\n",
"\n"
+ str(error))
show_error_message(self, error)
logger.log_debug(self, 'check_cfg: NOK!')
return False
else:
if not is_float_ok(self, dataToValidated):
logger.log_debug(self, "check_cfg: NOK!")
return False
logger.log_debug(self, "check_cfg: OK")
return True
def show_error_message(self, error):
error.message = error.message.replace('\\n', '')
if sys.version_info[0] < 3:
error.message = error.message.replace('file: u', 'Klipper Configuration', 1)
error.message = error.message.replace("'", '', 2)
error.message = error.message.replace("u'", "'", 1)
else:
error.message = error.message.replace('file:', 'Klipper Configuration', 1)
error.message = error.message.replace("'", '', 2)
logger.log_error(
self,
('Error: Invalid Klipper config file:\n' + '{}'.format(str(error))),
)
util.send_message(
self, 'PopUp', 'warning', 'Invalid Config data\n', ('\n' + str(error))
)
def is_float_ok(self, dataToValidated):
sections_search_list = ["bltouch",
"probe"]
value_search_list = ["x_offset",
"y_offset",
"z_offset"]
sections_search_list = [
"bltouch",
"probe"
]
value_search_list = [
"x_offset",
"y_offset",
"z_offset"
]
try:
# cycle through sections and then values
for y in sections_search_list:

View File

@ -1,72 +1,76 @@
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["OctoPrintClient"], factory);
} else {
factory(global.OctoPrintClient);
}
if (typeof define === "function" && define.amd) {
define(["OctoPrintClient"], factory);
} else {
factory(global.OctoPrintClient);
}
})(this, function (OctoPrintClient) {
var OctoKlipperClient = function (base) {
this.base = base;
this.url = this.base.getBlueprintUrl("klipper");
};
var OctoKlipperClient = function (base) {
this.base = base;
this.url = this.base.getBlueprintUrl("klipper");
};
OctoKlipperClient.prototype.getCfg = function (config, opts) {
return this.base.get(this.url + "config/" + config, opts);
};
OctoKlipperClient.prototype.restartKlipper = function (opts) {
return this.base.post(this.url + "restart", opts);
};
OctoKlipperClient.prototype.getCfgBak = function (backup, opts) {
return this.base.get(this.url + "backup/" + backup, opts);
};
OctoKlipperClient.prototype.getCfg = function (config, opts) {
return this.base.get(this.url + "config/" + config, opts);
};
OctoKlipperClient.prototype.listCfg = function (opts) {
return this.base.get(this.url + "config/list", opts);
};
OctoKlipperClient.prototype.getCfgBak = function (backup, opts) {
return this.base.get(this.url + "backup/" + backup, opts);
};
OctoKlipperClient.prototype.listCfgBak = function (opts) {
return this.base.get(this.url + "backup/list", opts);
};
OctoKlipperClient.prototype.listCfg = function (opts) {
return this.base.get(this.url + "config/list", opts);
};
OctoKlipperClient.prototype.checkCfg = function (content, opts) {
content = content || [];
OctoKlipperClient.prototype.listCfgBak = function (opts) {
return this.base.get(this.url + "backup/list", opts);
};
var data = {
DataToCheck: content,
};
OctoKlipperClient.prototype.checkCfg = function (content, opts) {
content = content || [];
return this.base.postJson(this.url + "config/check", data, opts);
};
var data = {
DataToCheck: content,
};
OctoKlipperClient.prototype.saveCfg = function (content, filename, opts) {
content = content || [];
filename = filename || [];
return this.base.postJson(this.url + "config/check", data, opts);
};
var data = {
DataToSave: content,
filename: filename,
};
OctoKlipperClient.prototype.saveCfg = function (content, filename, opts) {
content = content || [];
filename = filename || [];
return this.base.postJson(this.url + "config/save", data, opts);
};
var data = {
DataToSave: content,
filename: filename,
};
OctoKlipperClient.prototype.deleteCfg = function (config, opts) {
return this.base.delete(this.url + "config/" + config, opts);
};
return this.base.postJson(this.url + "config/save", data, opts);
};
OctoKlipperClient.prototype.deleteBackup = function (backup, opts) {
return this.base.delete(this.url + "backup/" + backup, opts);
};
OctoKlipperClient.prototype.deleteCfg = function (config, opts) {
return this.base.delete(this.url + "config/" + config, opts);
};
OctoKlipperClient.prototype.restoreBackup = function (backup, opts) {
return this.base.get(this.url + "backup/restore/" + backup, opts);
};
OctoKlipperClient.prototype.deleteBackup = function (backup, opts) {
return this.base.delete(this.url + "backup/" + backup, opts);
};
OctoKlipperClient.prototype.restoreBackupFromUpload = function (file, data) {
data = data || {};
OctoKlipperClient.prototype.restoreBackup = function (backup, opts) {
return this.base.get(this.url + "backup/restore/" + backup, opts);
};
var filename = data.filename || undefined;
return this.base.upload(this.url + "restore", file, filename, data);
};
OctoKlipperClient.prototype.restoreBackupFromUpload = function (file, data) {
data = data || {};
OctoPrintClient.registerPluginComponent("klipper", OctoKlipperClient);
return OctoKlipperClient;
var filename = data.filename || undefined;
return this.base.upload(this.url + "restore", file, filename, data);
};
OctoPrintClient.registerPluginComponent("klipper", OctoKlipperClient);
return OctoKlipperClient;
});

View File

@ -256,47 +256,40 @@ div#settings_plugin_klipper div.tab-content div#conf.tab-pane div.control-group
@media (max-width: 979px) {
div#klipper_editor.modal {
/* div#klipper_editor.modal {
height: unset !important;
} */
div#klipper_editor .modal-footer .btn {
margin-bottom: 1px;
margin-left: 5px;
}
}
#klipper_editor {
display: flex;
flex-flow: column;
}
.octoprint-container.UICMainCont.container-fluid {
margin-top:0px !important;
}
div#klipper_editor .modal-header div button.btn + button.btn {
margin-right: 12px;
}
div#klipper_editor .modal-body {
overflow: auto;
display: flex;
flex-flow: column;
}
.klipper-btn-group {
display: inline-block;
}
div#klipper_editor .modal-body label.checkbox {
display: unset;
}
div#klipper_editor .modal-body input[type="text"] {
div#klipper_editor .modal-footer input[type="text"] {
margin-bottom: 0px !important;
}
div#klipper_editor .modal-body input[type="checkbox"] {
float: unset;
margin-left: unset;
vertical-align: unset;
}
div#klipper_editor .modal-body .editor-controls {
div#klipper_editor .modal-footer .editor-controls {
flex: 0 auto;
display: flex;
align-items: center;
@ -308,29 +301,17 @@ div#klipper_editor div.conf-editor {
width: calc(100% - 4px);
margin-top: 5px;
overflow: auto;
flex-grow : 1;
}
div#klipper_editor div.conf-editor div#plugin-klipper-config {
font-family: monospace;
overflow: auto;
}
.ace_editor {
margin: auto;
height: 200px;
width: 100%;
margin: auto;
}
/* div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div#conf.tab-pane.active button.btn.btn-small ,div#klipper_editor button.btn.btn-small{
width: 30%;
display: inline-block;
margin: 0px 2px 2px 2px;
} */
/*checkboxes*/
div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div.tab-pane.active input.inline-checkbox,
div#klipper_editor input.inline-checkbox {
div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div.tab-pane.active input.inline-checkbox {
vertical-align: -0.2em;
}
@ -343,10 +324,6 @@ div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content
margin-top: 8px;
}
div#settings_plugin_klipper div#basic label.checkbox {
padding-left: 0px;
}
/*macros*/
div#settings_plugin_klipper div#macros label.control-label {
width: 80px;

View File

@ -51,27 +51,12 @@ $(function () {
});
};
self.onStartupComplete = function () {
var klipper_editor = $('#klipper_editor')
var modalOverflow = $(window).height() - 10 < klipper_editor.height();
klipper_editor.css('display', 'none');
if (modalOverflow) {
klipper_editor
.css('margin-top', 0)
.addClass('modal-overflow');
} else {
klipper_editor
.css('margin-top', 0 - klipper_editor.height() / 2)
.removeClass('modal-overflow');
}
};
self.showEditorDialog = function () {
if (!self.hasRight("CONFIG")) return;
var editorDialog = $("#klipper_editor");
editorDialog.modal({
show: "true",
width: "90%",
backdrop: "static",
});
}
@ -162,6 +147,8 @@ $(function () {
case "PopUp":
self.showPopUp(data.subtype, data.title, data.payload);
break;
case "start":
break;
case "reload":
break;
case "console":
@ -245,6 +232,25 @@ $(function () {
}
};
self.requestRestart = function () {
if (!self.loginState.hasPermission(self.access.permissions.PLUGIN_KLIPPER_CONFIG)) return;
var request = function () {
OctoPrint.plugins.klipper.restartKlipper().done(function () {
self.consoleMessage("debug", "requestRestart");
});
};
var html = "<h4>" + gettext("All ongoing Prints will be stopped!") + "</h4>";
showConfirmationDialog({
title: gettext("Klipper restart?"),
html: html,
proceed: gettext("Proceed"),
onproceed: request,
});
};
// OctoKlipper settings link
self.openOctoKlipperSettings = function (profile_type) {
if (!self.hasRight("CONFIG")) return;
@ -256,6 +262,10 @@ $(function () {
$(query).click();
}
};
self.sleep = function (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
};
}
OCTOPRINT_VIEWMODELS.push({

View File

@ -79,11 +79,10 @@ $(function () {
);
self.listBakFiles = function () {
self.klipperViewModel.consoleMessage("debug", "listBakFiles:");
self.klipperViewModel.consoleMessage("debug", "listBakFiles");
OctoPrint.plugins.klipper.listCfgBak()
.done(function (response) {
self.klipperViewModel.consoleMessage("debug", "listBakFilesdone: " + response);
self.backups.updateItems(response.files);
self.backups.resetPage();
});
@ -132,7 +131,7 @@ $(function () {
var restore = function () {
OctoPrint.plugins.klipper.restoreBackup(backup).done(function (response) {
self.klipperViewModel.consoleMessage("debug", "restoreCfg: " + response.text);
self.klipperViewModel.consoleMessage("debug", "restoreCfg: " + backup + " / " + response.restored);
});
};
@ -142,7 +141,7 @@ $(function () {
title: gettext("Are you sure you want to restore now?"),
html: html,
proceed: gettext("Proceed"),
onproceed: restore(),
onproceed: restore,
});
};
@ -207,7 +206,7 @@ $(function () {
}),
true
);
self.klipperViewModel.consoleMessage("debug", "restoreCfg: " + response.text);
self.klipperViewModel.consoleMessage("debug", "restoreCfg: " + filename + " / " + response);
self.markedForFileRestore.remove(function (item) {
return item.name == filename;
});

View File

@ -18,6 +18,7 @@ $(function () {
var self = this;
var obKlipperConfig = null;
var editor = null;
var editor_dirty = false;
self.settings = parameters[0];
self.klipperViewModel = parameters[1];
@ -31,56 +32,122 @@ $(function () {
"cache-control": "no-cache",
});
self.process = function (config) {
self.config = config;
self.CfgFilename(config.file);
self.CfgContent(config.content);
$('#klipper_editor').on('shown.bs.modal', function () {
editor.focus();
self.setEditorDivSize();
$(window).on('resize', function(){
self.klipperViewModel.sleep(500).then(
function () {
self.setEditorDivSize();
}
);
});
});
self.closeEditor = function () {
if (editor_dirty===true) {
showConfirmationDialog({
message: gettext(
"Your file seems to have changed."
),
question: gettext("Do you really want to close it?"),
cancel: gettext("No"),
proceed: gettext("Yes"),
onproceed: function () {
var dialog = $("#klipper_editor");
dialog.modal('hide');
},
nofade: true
});
} else {
var dialog = $("#klipper_editor");
dialog.modal('hide');
}
}
self.addStyleAttribute = function ($element, styleAttribute) {
$element.attr('style', $element.attr('style') + '; ' + styleAttribute);
}
self.setEditorDivSize = function () {
var klipper_modal_body= $('#klipper_editor .modal-body');
var klipper_config= $('#plugin-klipper-config');
var height = $(window).height() - $('#klipper_editor .modal-header').outerHeight() - $('#klipper_editor .modal-footer').outerHeight() - 118;
self.addStyleAttribute(klipper_modal_body, 'height: ' + height + 'px !important;');
//self.addStyleAttribute(klipper_config, 'height: ' + height + 'px !important;');
klipper_config.css('height', height);
if (editor) {
editor.session.setValue(config.content);
self.settings.settings.plugins.klipper.configuration.old_config(config.content);
editor.clearSelection();
editor.resize();
}
};
self.process = function (config) {
return new Promise(function (resolve) {
self.config = config;
self.CfgFilename(config.file);
self.CfgContent(config.content);
if (editor) {
editor.session.setValue(self.CfgContent());
editor_dirty=false;
editor.setFontSize(self.settings.settings.plugins.klipper.configuration.fontsize());
self.settings.settings.plugins.klipper.configuration.old_config(config.content);
editor.clearSelection();
self.klipperViewModel.sleep(500).then(
function() {
self.setEditorDivSize();
resolve("done");
}
);
}
});
}
self.checkSyntax = function () {
if (editor.session) {
self.klipperViewModel.consoleMessage("debug", "checkSyntax:");
self.klipperViewModel.consoleMessage("debug", "checkSyntax started");
OctoPrint.plugins.klipper.checkCfg(editor.session.getValue())
.done(function (response) {
var msg = ""
if (response.is_syntax_ok == true) {
self.klipperViewModel.showPopUp("success", gettext("SyntaxCheck"), gettext("SyntaxCheck OK"));
self.editorFocusDelay(1000);
} else {
msg = gettext('Syntax NOK')
showMessageDialog(
msg,
{
title: gettext("SyntaxCheck")
title: gettext("SyntaxCheck"),
onclose: function () { self.editorFocusDelay(1000); }
}
)
);
}
});
};
};
self.saveCfg = function () {
if (editor.session) {
self.klipperViewModel.consoleMessage("debug", "Save:");
self.klipperViewModel.consoleMessage("debug", "SaveCfg start");
OctoPrint.plugins.klipper.saveCfg(editor.session.getValue(), self.CfgFilename())
.done(function (response) {
var msg = ""
if (response.saved === true) {
self.klipperViewModel.showPopUp("success", gettext("Save Config"), gettext("File saved."));
editor_dirty = false;
if (self.settings.settings.plugins.klipper.configuration.restart_onsave()==true) {
self.klipperViewModel.requestRestart();
}
} else {
msg = gettext('File not saved!')
showMessageDialog(
msg,
{
title: gettext("Save Config")
title: gettext("Save Config"),
onclose: function () { self.editorFocusDelay(1000); }
}
)
}
@ -138,7 +205,7 @@ $(function () {
OctoPrint.plugins.klipper.getCfg(self.CfgFilename())
.done(function (response) {
self.klipperViewModel.consoleMessage("debug", "reloadFromFile: " + response);
self.klipperViewModel.consoleMessage("debug", "reloadFromFile done");
if (response.response.text != "") {
var msg = response.response.text
showMessageDialog(
@ -152,6 +219,7 @@ $(function () {
if (editor) {
editor.session.setValue(response.response.config);
editor.clearSelection();
editor.focus();
}
}
})
@ -166,32 +234,6 @@ $(function () {
});
};
self.configBound = function (config) {
config.withSilence = function () {
this.notifySubscribers = function () {
if (!this.isSilent) {
ko.subscribable.fn.notifySubscribers.apply(this, arguments);
}
}
this.silentUpdate = function (newValue) {
this.isSilent = true;
this(newValue);
this.isSilent = false;
};
return this;
}
obKlipperConfig = config.withSilence();
if (editor) {
editor.setValue(obKlipperConfig());
editor.setFontSize(self.settings.settings.plugins.klipper.configuration.fontsize());
editor.resize();
editor.clearSelection();
}
return obKlipperConfig;
};
self.onStartup = function () {
ace.config.set("basePath", "plugin/klipper/static/js/lib/ace/");
@ -205,18 +247,23 @@ $(function () {
vScrollBarAlwaysVisible: false,
autoScrollEditorIntoView: true,
showPrintMargin: false,
maxLines: "Infinity",
minLines: 100
//maxLines: "Infinity"
});
editor.session.on('change', function (delta) {
if (obKlipperConfig) {
obKlipperConfig.silentUpdate(editor.getValue());
editor.resize();
}
self.CfgContent(editor.getValue());
editor_dirty = true;
editor.resize();
});
};
self.editorFocusDelay = function (delay) {
self.klipperViewModel.sleep(delay).then(
function () {
editor.focus();
}
);
};
}
OCTOPRINT_VIEWMODELS.push({

View File

@ -38,6 +38,7 @@ $(function () {
self.settings.settings.plugins.klipper.configuration.fontsize(9)
}
}
// initialize list helper
self.configs = new ItemListHelper(
"klipperCfgFiles",
@ -70,18 +71,31 @@ $(function () {
self.onStartupComplete = function () {
self.listCfgFiles();
self.loadBaseConfig();
};
self.listCfgFiles = function () {
self.klipperViewModel.consoleMessage("debug", "listCfgFiles:");
self.klipperViewModel.consoleMessage("debug", "listCfgFiles started");
OctoPrint.plugins.klipper.listCfg().done(function (response) {
self.klipperViewModel.consoleMessage("debug", "listCfgFiles: " + response);
self.klipperViewModel.consoleMessage("debug", "listCfgFiles done");
self.configs.updateItems(response.files);
self.configs.resetPage();
});
};
self.loadBaseConfig = function () {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
OctoPrint.plugins.klipper.getCfg("printer.cfg").done(function (response) {
var config = {
content: response.response.config,
file: "printer.cfg",
};
self.klipperEditorViewModel.process(config).then();
});
};
self.removeCfg = function (config) {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
@ -190,7 +204,7 @@ $(function () {
};
self.showBackupsDialog = function () {
self.klipperViewModel.consoleMessage("debug", "showBackupsDialog:");
self.klipperViewModel.consoleMessage("debug", "showBackupsDialog");
self.klipperBackupViewModel.listBakFiles();
var dialog = $("#klipper_backups_dialog");
dialog.modal({
@ -198,21 +212,29 @@ $(function () {
});
};
self.showEditor = function () {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
var editorDialog = $("#klipper_editor");
editorDialog.modal({
show: "true",
width: "90%",
backdrop: "static",
});
}
self.newFile = function () {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
var config = {
content: "",
file: "Change Filename",
};
self.klipperEditorViewModel.process(config);
var editorDialog = $("#klipper_editor");
editorDialog.modal({
show: "true",
backdrop: "static",
});
self.klipperEditorViewModel.process(config).then(
function() { self.showEditor(); }
);
};
self.showEditUserDialog = function (file) {
self.openConfig = function (file) {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
OctoPrint.plugins.klipper.getCfg(file).done(function (response) {
@ -220,23 +242,11 @@ $(function () {
content: response.response.config,
file: file,
};
self.klipperEditorViewModel.process(config);
self.klipperEditorViewModel.process(config).then(
function() { self.showEditor(); }
);
var editorDialog = $("#klipper_editor");
editorDialog.modal({
show: "true",
backdrop: "static",
});
});
};
self.showEditor = function () {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
var editorDialog = $("#klipper_editor");
editorDialog.modal({
show: "true",
backdrop: "static",
});
};
@ -302,8 +312,21 @@ $(function () {
if (plugin == "klipper" && data.type == "reload" && data.subtype == "configlist") {
self.klipperViewModel.consoleMessage("debug", "onDataUpdaterPluginMessage klipper reload configlist");
self.listCfgFiles();
} else if (plugin == "klipper" && data.type == "start" && data.subtype == "config") {
self.klipperViewModel.consoleMessage("debug", "onDataUpdaterPluginMessage klipper start config");
self.startConfig(data.title, data.payload);
}
};
self.startConfig = function (file, content) {
if (!self.klipperViewModel.hasRight("CONFIG")) return;
filename = file || "";
var config = {
content: content,
file: filename,
};
self.klipperEditorViewModel.process(config).then();
};
}
OCTOPRINT_VIEWMODELS.push({

View File

@ -84,7 +84,7 @@
<a href="javascript:void(0)" class="fas fa-download" title="{{ _('Download') }}"
data-bind="css: {disabled: !$root.klipperViewModel.hasRightKo('CONFIG')()}, attr: { href: ($root.klipperViewModel.hasRightKo('CONFIG')()) ? $data.url : 'javascript:void(0)'}"></a>
&nbsp;|&nbsp;
<a href="javascript:void(0)" class="fas fa-camera" title="{{ _('Preview') }}"
<a href="javascript:void(0)" class="fas fa-eye" title="{{ _('Preview') }}"
data-bind="css: {disabled: !$root.klipperViewModel.hasRightKo('CONFIG')()}, click: function() { $parent.showCfg($data.name); }"></a>
</td>
</tr>
@ -104,7 +104,7 @@
</ul>
</div>
<div class="textarea">
<textarea readonly data-bind="value: CfgContent" id="klipper_bak_text"></textarea>
<textarea rows="21" readonly data-bind="value: CfgContent" id="klipper_bak_text"></textarea>
</div>
</div>
</div>

View File

@ -1,41 +1,46 @@
<div id="klipper_editor" class="modal hide fade large" tabindex="-1" role="dialog" aria-labelledby="klipper_editor_label" aria-hidden="true">
<div id="klipper_editor" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="klipper_editor_label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h3 id="klipper_dialog_label">{{ _('Editor') }}</h3>
<button type="button" class="close" data-bind="click: closeEditor">&times;</button>
<h3 id="klipper_dialog_label">Editor
<div class="pull-right">
<button data-bind="click: minusFontsize" title="{{ _('Decrease Fontsize') }}" class="btn">
<i class="fas fa-search-minus"></i>
</button>
<button data-bind="click: plusFontsize" title="{{ _('Increase Fontsize') }}" class="btn">
<i class="fas fa-search-plus"></i>
</button>
</div>
</h3>
</div>
<div class="modal-body">
<script src="plugin/klipper/static/js/lib/ace/ace.min.js" type="text/javascript" charset="utf-8"></script>
<script src="plugin/klipper/static/js/lib/ace/theme-monokai.min.js" type="text/javascript" charset="utf-8"></script>
<script src="plugin/klipper/static/js/lib/ace/mode-klipper_config.js" type="text/javascript"></script>
<div class="full-sized-box">
<div class="conf-editor" id="conf_editor">
<input id="hdnLoadKlipperConfig" type="hidden" data-bind="value: CfgContent">
<div id="plugin-klipper-config"></div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="editor-controls">
<span class="control-label">{{ _('Filename') }}:</span>
<input type="text" data-bind="value: CfgFilename">
<div class="klipper-btn-group klipper-fluid-item-2">
<button class="btn btn-small" data-bind="click: saveCfg" title="{{ _('Save Config') }}">
<i class="fas fa-save"></i> {{ _('Save Config') }}
<button class="btn btn-small" data-bind="click: reloadFromFile" title="{{ _('Reload from file') }}">
<i class="fas fa-upload"></i> {{ _('Reload from file') }}
</button>
<button class="btn btn-small" data-bind="click: loadLastSession" title="{{ _('Reload last version') }}">
<i class="fas fa-redo"></i> {{ _('Reload last version') }}
</button>
<button class="btn btn-small" data-bind="click: reloadFromFile" title="{{ _('Reload from file') }}">
<i class="fas fa-upload"></i> {{ _('Reload from file') }}
</button>
<button class="btn btn-small" data-bind="click: checkSyntax" title="{{ _('Check Syntax') }}">
<i class="fas fa-spell-check"></i> {{ _('Check Syntax') }}
</button>
<button class="btn btn-small btn-primary" data-bind="click: saveCfg" title="{{ _('Save Config') }}">
<i class="fas fa-save"></i> {{ _('Save Config') }}
</button>
</div>
<div class="pull-right">
<a href='#' style="text-decoration: none;" data-bind="click: minusFontsize" title="{{ _('Decrease Fontsize') }}">
<i class="fas fa-search-minus"></i>
</a>
<a href='#' style="text-decoration: none;" data-bind="click: plusFontsize" title="{{ _('Increase Fontsize') }}">
<i class="fas fa-search-plus"></i>
</a>
</div>
</div>
<div class="conf-editor" id="conf_editor">
<input id="hdnLoadKlipperConfig" type="hidden" data-bind="value: configBound(CfgContent)">
<div id="plugin-klipper-config"></div>
</div>
</div>
</div>

View File

@ -67,12 +67,13 @@
<select data-bind="value: settings.settings.plugins.klipper.configuration.reload_command">
<option value="RESTART">RESTART</option>
<option value="FIRMWARE_RESTART">FIRMWARE_RESTART</option>
<option value="manually">Manually</option>
</select>
<span class="help-block">
{{ _('The command that is executed when the Klipper configuration changed and needs to be reloaded.<br />
Set this to "Manually" if you don\'t want to immediately restart klipper.') }}
<span class="help-block" style="margin-top:5px;">
{{ _('The command that is executed if you want to restart klipper.') }}
</span>
<label class="checkbox" title="{{ _('Reload klipper on editor save?') }}"><input type="checkbox"
data-bind="checked: settings.settings.plugins.klipper.configuration.restart_onsave" /> {{ _('Reload klipper on editor save?') }}
</label>
</div>
</div>
<div class="control-group">
@ -246,7 +247,7 @@
<i class="far fa-file"></i> {{ _('New File') }}
</button>
<button class="btn btn-small" data-bind="click: showEditor" title="{{ _('Open last config') }}">
<i class="fas fa-file-code"></i> {{ _('Show Editor') }}
<i class="fas fa-file-code"></i> {{ _('Open last Editor') }}
</button>
<button class="btn btn-small" data-bind="click: listCfgFiles" title="{{ _('Refresh file list') }}">
<i class="icon-refresh"></i> {{ _('Refresh Files') }}
@ -297,7 +298,7 @@
data-bind="css: {disabled: !$root.klipperViewModel.hasRightKo('CONFIG')()}, attr: { href: ($root.klipperViewModel.hasRightKo('CONFIG')()) ? $data.url : 'javascript:void(0)'}"></a>
&nbsp;|&nbsp;
<a href="javascript:void(0)" class="fas fa-pencil-alt" title="{{ _('Edit') }}"
data-bind="css: {disabled: !$root.klipperViewModel.hasRightKo('CONFIG')()}, click: function() { $parent.showEditUserDialog($data.name);}"></a>
data-bind="css: {disabled: !$root.klipperViewModel.hasRightKo('CONFIG')()}, click: function() { $parent.openConfig($data.name);}"></a>
</td>
</tr>
</tbody>