diff --git a/.gitignore b/.gitignore index 3d25b91..e4b2ccc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ dist .vscode/** thunder-tests -vscode.env \ No newline at end of file +vscode.env +.venv +OctoKlipper.egg-info \ No newline at end of file diff --git a/octoprint_klipper/__init__.py b/octoprint_klipper/__init__.py index d171c3e..7ee3aec 100644 --- a/octoprint_klipper/__init__.py +++ b/octoprint_klipper/__init__.py @@ -22,7 +22,7 @@ import glob import os import sys from octoprint.util.comm import parse_firmware_line -from octoprint.access.permissions import Permissions, ADMIN_GROUP, USER_GROUP +from octoprint.access.permissions import Permissions, ADMIN_GROUP from .modules import KlipperLogAnalyzer import flask from flask_babel import gettext @@ -141,7 +141,7 @@ class KlipperPlugin( configpath = os.path.expanduser( self._settings.get(["configuration", "configpath"]) ) - + data["config"] = "" try: f = open(configpath, "r") data["config"] = f.read() @@ -163,10 +163,8 @@ class KlipperPlugin( ) if "config" in data: - if self.key_exist(data, "configuration", "parse_check"): - check_parse = data["configuration"]["parse_check"] - else: - check_parse = self._settings.get(["configuration", "parse_check"]) + check_parse = self._settings.get(["configuration", "parse_check"]) + self.log_debug("check_parse: {}".format(check_parse)) if sys.version_info[0] < 3: data["config"] = data["config"].encode('utf-8') @@ -181,13 +179,15 @@ class KlipperPlugin( configpath = os.path.expanduser( self._settings.get(["configuration", "configpath"]) ) - if self.file_exist(configpath) and (self._parsing_check_response or not check_parse): + if self.file_exist(configpath): + self.copy_cfg_to_backup(configpath) + + if self._parsing_check_response or not check_parse: try: f = open(configpath, "w") f.write(data["config"]) f.close() - self.log_debug("Writing Klipper config to {}".format(configpath)) except IOError: self.log_error("Error: Couldn't write Klipper config file: {}".format(configpath)) @@ -284,7 +284,7 @@ class KlipperPlugin( settings.set(["config_path"], settings.get(["configPath"])) settings.remove(["configPath"]) - if target is 3 and current is 2: + if current is not None and current < 3: settings = self._settings if settings.has(["configuration", "navbar"]): self.log_info("migrate setting for: configuration/navbar") @@ -414,6 +414,7 @@ class KlipperPlugin( listLogFiles=[], getStats=["logFile"], reloadConfig=[], + reloadCfgBackup=[], checkConfig=["config"] ) @@ -441,32 +442,22 @@ class KlipperPlugin( data["logFile"]) return flask.jsonify(log_analyzer.analyze()) elif command == "reloadConfig": - data = octoprint.plugin.SettingsPlugin.on_settings_load(self) - + self.log_debug("reloadConfig") + return self.reload_cfg() + elif command == "reloadCfgBackup": + self.log_debug("reloadCfgBackup") configpath = os.path.expanduser( - self._settings.get(["configuration", "configpath"]) - ) - - try: - f = open(configpath, "r") - data["config"] = f.read() - f.close() - except IOError: - self.log_error( - "Error: Klipper config file not found at: {}".format( - configpath) + self._settings.get(["configuration", "configpath"]) ) - else: - - self._settings.set(["config"], data["config"]) - # self.send_message("reload", "config", "", data["config"]) - # send the configdata to frontend to update ace editor - if sys.version_info[0] < 3: - data["config"] = data["config"].decode('utf-8') - return flask.jsonify(data=data["config"]) + return self.copy_cfg_from_backup(configpath) elif command == "checkConfig": if "config" in data: - if not self.validate_configfile(data["config"]): + #self.write_cfg_backup(data["config"]) + if self.key_exist(data, "configuration", "parse_check"): + check_parse = data["configuration"]["parse_check"] + else: + check_parse = self._settings.get(["configuration", "parse_check"]) + if check_parse and not self.validate_configfile(data["config"]): self.log_debug("validateConfig not ok") self._settings.set(["configuration", "old_config"], data["config"]) return flask.jsonify(checkConfig="not OK") @@ -501,6 +492,9 @@ class KlipperPlugin( #-- Helpers def send_message(self, type, subtype, title, payload): + """ + Send Message over API to FrontEnd + """ self._plugin_manager.send_plugin_message( self._identifier, dict( @@ -554,63 +548,134 @@ class KlipperPlugin( """ --->SyntaxCheck for a given data<---- """ - - try: - dataToValidated = configparser.RawConfigParser(strict=False) - # - if sys.version_info[0] < 3: - buf = StringIO.StringIO(dataToBeValidated) - dataToValidated.readfp(buf) - else: - dataToValidated.read_string(dataToBeValidated) - - sections_search_list = ["bltouch", - "probe"] - value_search_list = [ "x_offset", - "y_offset", - "z_offset"] + check_parse = self._settings.get(["configuration", "parse_check"]) + if check_parse: try: - # cycle through sections and then values - for y in sections_search_list: - for x in value_search_list: - if dataToValidated.has_option(y, x): - a_float = dataToValidated.getfloat(y, x) - except ValueError as error: + dataToValidated = configparser.RawConfigParser(strict=False) + # + if sys.version_info[0] < 3: + buf = StringIO.StringIO(dataToBeValidated) + dataToValidated.readfp(buf) + else: + dataToValidated.read_string(dataToBeValidated) + + 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: + for x in value_search_list: + if dataToValidated.has_option(y, x): + a_float = dataToValidated.getfloat(y, x) + except ValueError as error: + self.log_error( + "Error: Invalid Value for "+x+" in Section: "+y+"\n" + + "{}".format(str(error)) + ) + self.send_message("PopUp", "warning", "OctoKlipper: Invalid Config\n", + "Config got not saved!\n" + + "You can reload your last changes\n" + + "on the 'Klipper Configuration' tab.\n\n" + + "Invalid Value for "+x+" in Section: "+y+"\n" + "{}".format(str(error))) + self._parsing_check_response = False + return False + except configparser.Error as error: + if sys.version_info[0] < 3: + error.message = error.message.replace("\\n","") + 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("\\n","") + error.message = error.message.replace("file:","Klipper Configuration", 1) + error.message = error.message.replace("'","", 2) self.log_error( - "Error: Invalid Value for "+x+" in Section: "+y+"\n" + + "Error: Invalid Klipper config file:\n" + "{}".format(str(error)) ) - self.send_message("PopUp", "warning", "OctoKlipper: Invalid Config\n", - "Config got not saved!\n" + - "You can reload your last changes\n" + - "on the 'Klipper Configuration' tab.\n\n" + - "Invalid Value for "+x+" in Section: "+y+"\n" + "{}".format(str(error))) + self.send_message("PopUp", "warning", "OctoKlipper: Invalid Config data\n", + "Config got not saved!\n" + + "You can reload your last changes\n" + + "on the 'Klipper Configuration' tab.\n\n" + str(error)) self._parsing_check_response = False return False - except configparser.Error as error: - if sys.version_info[0] < 3: - error.message = error.message.replace("\\n","") - 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("\\n","") - error.message = error.message.replace("file:","Klipper Configuration", 1) - error.message = error.message.replace("'","", 2) + self._parsing_check_response = True + return True + + def reload_cfg(self): + data = octoprint.plugin.SettingsPlugin.on_settings_load(self) + + configpath = os.path.expanduser( + self._settings.get(["configuration", "configpath"]) + ) + + try: + f = open(configpath, "r") + data["config"] = f.read() + f.close() + except IOError: self.log_error( - "Error: Invalid Klipper config file:\n" + - "{}".format(str(error)) + "Error: Klipper config file not found at: {}".format( + configpath) ) - self.send_message("PopUp", "warning", "OctoKlipper: Invalid Config data\n", - "Config got not saved!\n" + - "You can reload your last changes\n" + - "on the 'Klipper Configuration' tab.\n\n" + str(error)) - self._parsing_check_response = False - return False else: - self._parsing_check_response = True - return True + + self._settings.set(["config"], data["config"]) + # self.send_message("reload", "config", "", data["config"]) + # send the configdata to frontend to update ace editor + if sys.version_info[0] < 3: + data["config"] = data["config"].decode('utf-8') + return flask.jsonify(data=data["config"]) + + def copy_cfg_from_backup(self, dst): + """ + Copy the backuped config files in the data folder of OctoKlipper to the given destination + """ + from shutil import copy + + bak_config_path = os.path.join(self.get_plugin_data_folder(), "configs/") + src_files = os.listdir(bak_config_path) + nio_files = [] + self.log_debug("reloadCfgBackupPath:" + src_files) + + for file_name in src_files: + full_file_name = os.path.join(bak_config_path, file_name) + if os.path.isfile(full_file_name): + try: + copy(full_file_name, dst) + except IOError: + self.log_error( + "Error: Klipper config file not found at: {}".format( + full_file_name) + ) + nio_files.append(full_file_name) + else: + self.log_debug("File done: " + full_file_name) + return nio_files + + def copy_cfg_to_backup(self, src): + """ + Copy the config file into the data folder of OctoKlipper + """ + from shutil import copyfile + + filename = os.path.basename(src) + dst = os.path.join(self.get_plugin_data_folder(), "configs", "", filename) + self.log_debug("CopyCfgBackupPath:" + dst) + try: + copyfile(src, dst) + except IOError: + self.log_error( + "Error: Couldn't copy Klipper config file to {}".format( + dst) + ) + else: + self.log_debug("CfgBackup writen") __plugin_name__ = "OctoKlipper" __plugin_pythoncompat__ = ">=2.7,<4" diff --git a/octoprint_klipper/static/js/klipper_settings.js b/octoprint_klipper/static/js/klipper_settings.js index 74b9de1..8c6d4b3 100644 --- a/octoprint_klipper/static/js/klipper_settings.js +++ b/octoprint_klipper/static/js/klipper_settings.js @@ -31,7 +31,7 @@ $(function() { self.apiUrl = OctoPrint.getSimpleApiUrl("klipper"); self.onSettingsBeforeSave = function () { - if (editor.session && self.settings.settings.plugins.klipper.configuration.parse_check() === true) { + if (editor.session) { self.klipperViewModel.consoleMessage("debug", "onSettingsBeforeSave:") var settings = { "crossDomain": true, @@ -152,8 +152,31 @@ $(function() { } $.ajax(settings).done(function (response) { - editor.session.setValue(response["data"]); - editor.clearSelection(); + if (editor.session) { + editor.session.setValue(response["data"]); + editor.clearSelection(); + } + }); + } + } + + self.loadCfgBackup = function () { + if (editor.session) { + var settings = { + "crossDomain": true, + "url": self.apiUrl, + "method": "POST", + "headers": self.header, + "processData": false, + "dataType": "json", + "data": JSON.stringify({command: "reloadCfgBackup"}) + } + + $.ajax(settings).done(function (response) { + if (editor.session) { + editor.session.setValue(response["data"]); + editor.clearSelection(); + } }); } } diff --git a/octoprint_klipper/templates/klipper_settings.jinja2 b/octoprint_klipper/templates/klipper_settings.jinja2 index 6d60d5a..e9f5e6d 100644 --- a/octoprint_klipper/templates/klipper_settings.jinja2 +++ b/octoprint_klipper/templates/klipper_settings.jinja2 @@ -208,9 +208,9 @@