From c835795fe94ae33c971b5541e0a150b44b9e38ee Mon Sep 17 00:00:00 2001 From: thelastWallE <12502210+thelastWallE@users.noreply.github.com> Date: Thu, 16 Sep 2021 12:31:51 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20Restart=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -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 --- octoprint_klipper/__init__.py | 86 +++++------ octoprint_klipper/cfgUtils.py | 98 ++++++------ octoprint_klipper/static/clientjs/klipper.js | 110 +++++++------- octoprint_klipper/static/css/klipper.css | 51 ++----- octoprint_klipper/static/js/klipper.js | 42 ++++-- octoprint_klipper/static/js/klipper_backup.js | 9 +- octoprint_klipper/static/js/klipper_editor.js | 139 ++++++++++++------ .../static/js/klipper_settings.js | 73 +++++---- .../templates/klipper_backups_dialog.jinja2 | 4 +- .../templates/klipper_editor.jinja2 | 45 +++--- .../templates/klipper_settings.jinja2 | 13 +- 11 files changed, 374 insertions(+), 296 deletions(-) diff --git a/octoprint_klipper/__init__.py b/octoprint_klipper/__init__.py index 20e84e4..4cc2688 100644 --- a/octoprint_klipper/__init__.py +++ b/octoprint_klipper/__init__.py @@ -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 diff --git a/octoprint_klipper/cfgUtils.py b/octoprint_klipper/cfgUtils.py index 7cf2959..f6bc465 100644 --- a/octoprint_klipper/cfgUtils.py +++ b/octoprint_klipper/cfgUtils.py @@ -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: diff --git a/octoprint_klipper/static/clientjs/klipper.js b/octoprint_klipper/static/clientjs/klipper.js index aedc85d..26c01b6 100644 --- a/octoprint_klipper/static/clientjs/klipper.js +++ b/octoprint_klipper/static/clientjs/klipper.js @@ -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; }); diff --git a/octoprint_klipper/static/css/klipper.css b/octoprint_klipper/static/css/klipper.css index 2b517d1..445c932 100644 --- a/octoprint_klipper/static/css/klipper.css +++ b/octoprint_klipper/static/css/klipper.css @@ -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; diff --git a/octoprint_klipper/static/js/klipper.js b/octoprint_klipper/static/js/klipper.js index 261d8b6..2d3eacb 100644 --- a/octoprint_klipper/static/js/klipper.js +++ b/octoprint_klipper/static/js/klipper.js @@ -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 = "