diff --git a/.github/workflows/fakeaction.yml b/.github/workflows/fakeaction.yml deleted file mode 100644 index 7100b21..0000000 --- a/.github/workflows/fakeaction.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: FakeAction - -on: - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Run a one-line script - run: echo ping diff --git a/.github/workflows/staleissues.yml b/.github/workflows/staleissues.yml index 8272e8a..1006d9c 100644 --- a/.github/workflows/staleissues.yml +++ b/.github/workflows/staleissues.yml @@ -9,10 +9,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Close Stale Issues - uses: actions/stale@v3.0.18 + uses: actions/stale@v4.0.0 with: # Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`. # repo-token: ${{ github.token }} stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' days-before-stale: 30 days-before-close: 5 + exempt-all-pr-milestones: true + exempt-all-assignees: true diff --git a/README.md b/README.md index cb7f60b..c0dc053 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This plugin assists in managing and monitoring the [Klipper](https://github.com/ - PID Tuning Dialog. - Dialog to set a coordinate offset for future GCODE move commands. - Message log displaying messages from Klipper prepended with "//" and "!!". -- Basic Klipper configuration editor +- Klipper configuration editor - Performance graph displaying key parameters extracted from the Klipper logs. ## Installation @@ -46,7 +46,7 @@ Also for the moment this plugin does what I wanted it to do, it is far from fini * The [devel](https://github.com/thelastWallE/OctoprintKlipperPlugin/tree/devel) branch is the branch to merge new features and bugfixes to. * The [rc](https://github.com/thelastWallE/OctoprintKlipperPlugin/tree/rc) branch is for Release Candidates and bugfixing them. -* The [master](https://github.com/thelastWallE/OctoprintKlipperPlugin/tree/master) branch is for Stable Releases. +* The [master](https://github.com/thelastWallE/OctoprintKlipperPlugin/tree/master) branch is for Stable Releases. ## Screenshots diff --git a/octoprint_klipper/__init__.py b/octoprint_klipper/__init__.py index cc71929..a3ea949 100644 --- a/octoprint_klipper/__init__.py +++ b/octoprint_klipper/__init__.py @@ -25,7 +25,8 @@ import sys from octoprint.server import NO_CONTENT from octoprint.util import is_hidden_path from octoprint.util import get_formatted_size -from . import util, cfgUtils, logger +from octoprint_klipper import cfgUtils +from octoprint_klipper.util import * from octoprint.util.comm import parse_firmware_line from octoprint.access.permissions import Permissions, ADMIN_GROUP from .modules import KlipperLogAnalyzer @@ -78,7 +79,7 @@ class KlipperPlugin( self._settings.global_set( ["serial", "additionalPorts"], additional_ports) self._settings.save() - logger.log_info( + log_info( self, "Added klipper serial port {} to list of additional ports.".format(klipper_port) ) @@ -106,10 +107,12 @@ class KlipperPlugin( ] def get_settings_defaults(self): + # TODO #69 put some settings on the localStorage return dict( connection=dict( port="/tmp/printer", - replace_connection_panel=True + replace_connection_panel=True, + hide_editor_button=False ), macros=[dict( name="E-Stop", @@ -130,15 +133,17 @@ class KlipperPlugin( ), configuration=dict( debug_logging=False, - configpath="~/", - old_config="", + config_path="~/", + baseconfig="printer.cfg", logpath="/tmp/klippy.log", reload_command="RESTART", - restart_onsave=False, + restart_onsave=True, + confirm_reload=True, shortStatus_navbar=True, shortStatus_sidebar=True, parse_check=False, - fontsize=9 + fontsize=12, + hide_error_popups=False ) ) @@ -158,7 +163,7 @@ class KlipperPlugin( return dict( admin=[ ["connection", "port"], - ["configuration", "configpath"], + ["configuration", "config_path"], ["configuration", "replace_connection_panel"] ], user=[ @@ -170,83 +175,56 @@ 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 + # 4 = -change of configpath to config_path with only path without filename + # -parse configpath into config_path and baseconfig + # -switch setting for 'restart on editor save' to true if it was not set to manually + # -remove old_config + # -remove config on root settingsdirectory return 4 - #migrate Settings def on_settings_migrate(self, target, current): settings = self._settings if current is None: - self.migrate_old_settings(settings) + migrate_old_settings(settings) if current is not None and current < 3: - self.migrate_settings_configuration( - settings, - "shortStatus_navbar", - "navbar", - ) + self.migrate_settings_3(settings) if current is not None and current < 4: - self.migrate_settings_configuration( - settings, - "old_config", - "temp_config", - ) + self.migrate_settings_4(settings) + def migrate_settings_3(self, settings): + migrate_settings_configuration( + settings, + "shortStatus_navbar", + "navbar", + ) + + def migrate_settings_4(self, settings): + if settings.has(["configuration", "configpath"]): 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) + new_cfg_path, baseconfig = os.path.split(cfg_path) + log_info(self, "migrate setting for 'configuration/config_path': " + cfg_path + " -> " + new_cfg_path) + log_info(self, "migrate setting for 'configuration/baseconfig': printer.cfg -> " + baseconfig) + settings.set(["configuration", "config_path"], new_cfg_path) + settings.set(["configuration", "baseconfig"], baseconfig) + settings.remove(["configuration", "configpath"]) + if ( + settings.has(["configuration", "reload_command"]) + and settings.get(["configuration", "reload_command"]) == "manually" + ): + log_info(self, "migrate setting for 'configuration/restart_onsave': True -> False") + settings.set(["configuration", "restart_onsave"], False) + settings.remove(["configuration", "reload_command"]) - 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) + if settings.has(["config"]): + log_info(self, "remove old setting for 'config'") + settings.remove(["config"]) - - 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", "configuration", "configpath") - - if settings.has(["probePoints"]): - points = settings.get(["probePoints"]) - points_new = [dict(name="", x=int(p["x"]), y=int(p["y"]), z=0) for p in points] - settings.set(["probe", "points"], points_new) - 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 + "'") - settings.set([new, new2], settings.get([old])) - else: - logger.log_info(self, "migrate setting for '" + old + "' -> '" + new + "'") - settings.set([new], settings.get([old])) - settings.remove([old]) - - def migrate_settings_configuration(self, settings, new, old): - if settings.has(["configuration", old]): - logger.log_info(self, "migrate setting for 'configuration/" + old + "' -> 'configuration/" + new + "'") - settings.set(["configuration", new], settings.get(["configuration", old])) - settings.remove(["configuration", old]) + if settings.has(["configuration", "old_config"]): + log_info(self, "remove old setting for 'configuration/old_config'") + settings.remove(["configuration", "old_config"]) # -- Template Plugin @@ -339,43 +317,54 @@ class KlipperPlugin( def on_event(self, event, payload): if event == "UserLoggedIn": - util.update_status(self, "info", "Klipper: Standby") + log_info(self, "Klipper: Standby") if event == "Connecting": - util.update_status(self, "info", "Klipper: Connecting ...") + log_info(self, "Klipper: Connecting ...") elif event == "Connected": - util.update_status(self, "info", "Klipper: Connected to host") - logger.log_info( + log_info(self, "Klipper: Connected to host") + log_info( self, "Connected to host via {} @{}bps".format(payload["port"], payload["baudrate"])) elif event == "Disconnected": - util.update_status(self, "info", "Klipper: Disconnected from host") + log_info(self, "Klipper: Disconnected from host") + elif event == "Error": - util.update_status(self, "error", "Klipper: Error") - logger.log_error(self, payload["error"]) + log_error(self, payload["error"]) + + def processAtCommand(self, comm_instance, phase, command, parameters, tags=None, *args, **kwargs): + if command != "SWITCHCONFIG": + return + + config = parameters + log_info(self, "SWITCHCONFIG detected config:{}".format(config)) + return None # -- GCODE Hook + def process_sent_GCODE(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs): + if cmd == "SAVE_CONFIG": + log_info(self, "SAVE_CONFIG detected") + send_message(self, type = "reload", subtype = "config") def on_parse_gcode(self, comm, line, *args, **kwargs): if "FIRMWARE_VERSION" in line: printerInfo = parse_firmware_line(line) if "FIRMWARE_VERSION" in printerInfo: - logger.log_info(self, "Firmware version: {}".format( + log_info(self, "Firmware version: {}".format( printerInfo["FIRMWARE_VERSION"])) elif "// probe" in line or "// Failed to verify BLTouch" in line: msg = line.strip('/') - logger.log_info(self, msg) + log_info(self, msg) self.write_parsing_response_buffer() elif "//" in line: # add lines with // to a buffer self._message = self._message + line.strip('/') if not self._parsing_response: - util.update_status(self, "info", self._message) + update_status(self, "info", self._message) self._parsing_response = True elif "!!" in line: msg = line.strip('!') - util.update_status(self, "error", msg) - logger.log_error(self, msg) + log_error(self, msg) self.write_parsing_response_buffer() else: self.write_parsing_response_buffer() @@ -385,7 +374,7 @@ class KlipperPlugin( # write buffer with // lines after a gcode response without // if self._parsing_response: self._parsing_response = False - logger.log_info(self, self._message) + log_info(self, self._message) self._message = "" def get_api_commands(self): @@ -400,7 +389,7 @@ class KlipperPlugin( logpath = os.path.expanduser( self._settings.get(["configuration", "logpath"]) ) - if util.file_exist(self, logpath): + if file_exist(self, logpath): for f in glob.glob(self._settings.get(["configuration", "logpath"]) + "*"): filesize = os.path.getsize(f) filemdate = time.strftime("%d.%m.%Y %H:%M",time.localtime(os.path.getctime(f))) @@ -423,7 +412,7 @@ class KlipperPlugin( from octoprint.server.util.tornado import LargeResponseHandler, path_validation_factory from octoprint.util import is_hidden_path configpath = os.path.expanduser( - self._settings.get(["configuration", "configpath"]) + self._settings.get(["configuration", "config_path"]) ) bak_path = os.path.join(self.get_plugin_data_folder(), "configs", "") @@ -468,7 +457,7 @@ class KlipperPlugin( raise return NO_CONTENT - # Get a list of all backuped configfiles + # Get a list of all backed up configfiles @octoprint.plugin.BlueprintPlugin.route("/backup/list", methods=["GET"]) @restricted_access @Permissions.PLUGIN_KLIPPER_CONFIG.require(403) @@ -476,13 +465,13 @@ class KlipperPlugin( files = cfgUtils.list_cfg_files(self, "backup") return flask.jsonify(files = files) - # restore a backuped configfile + # restore a backed up configfile @octoprint.plugin.BlueprintPlugin.route("/backup/restore/", methods=["GET"]) @restricted_access @Permissions.PLUGIN_KLIPPER_CONFIG.require(403) def restore_backup(self, filename): configpath = os.path.expanduser( - self._settings.get(["configuration", "configpath"]) + self._settings.get(["configuration", "config_path"]) ) data_folder = self.get_plugin_data_folder() backupfile = os.path.realpath(os.path.join(data_folder, "configs", filename)) @@ -495,7 +484,7 @@ class KlipperPlugin( @Permissions.PLUGIN_KLIPPER_CONFIG.require(403) def get_config(self, filename): cfg_path = os.path.expanduser( - self._settings.get(["configuration", "configpath"]) + self._settings.get(["configuration", "config_path"]) ) full_path = os.path.realpath(os.path.join(cfg_path, filename)) response = cfgUtils.get_cfg(self, full_path) @@ -507,7 +496,7 @@ class KlipperPlugin( @Permissions.PLUGIN_KLIPPER_CONFIG.require(403) def delete_config(self, filename): cfg_path = os.path.expanduser( - self._settings.get(["configuration", "configpath"]) + self._settings.get(["configuration", "config_path"]) ) full_path = os.path.realpath(os.path.join(cfg_path, filename)) if ( @@ -528,7 +517,10 @@ class KlipperPlugin( @Permissions.PLUGIN_KLIPPER_CONFIG.require(403) def list_configs(self): files = cfgUtils.list_cfg_files(self, "") - return flask.jsonify(files = files, max_upload_size = MAX_UPLOAD_SIZE) + path = os.path.expanduser( + self._settings.get(["configuration", "config_path"]) + ) + return flask.jsonify(files = files, path = path, max_upload_size = MAX_UPLOAD_SIZE) # check syntax of a given data @octoprint.plugin.BlueprintPlugin.route("/config/check", methods=["POST"]) @@ -537,7 +529,7 @@ class KlipperPlugin( def check_config(self): data = flask.request.json data_to_check = data.get("DataToCheck", []) - response = cfgUtils.check_cfg(self, data_to_check) + response = cfgUtils.check_cfg_ok(self, data_to_check) return flask.jsonify(is_syntax_ok = response) # save a configfile @@ -555,7 +547,7 @@ class KlipperPlugin( Filecontent = data.get("DataToSave", []) saved = cfgUtils.save_cfg(self, Filecontent, filename) if saved == True: - util.send_message(self, "reload", "configlist", "", "") + send_message(self, type = "reload", subtype = "configlist") return flask.jsonify(saved = saved) # restart klipper @@ -569,8 +561,8 @@ class KlipperPlugin( # Restart klippy to reload config self._printer.commands(reload_command) - logger.log_info(self, "Restarting Klipper.") - return NO_CONTENT + log_info(self, "Restarting Klipper.") + return flask.jsonify(command = reload_command) # APIs end @@ -618,6 +610,8 @@ def __plugin_load__(): __plugin_hooks__ = { "octoprint.server.http.routes": __plugin_implementation__.route_hook, "octoprint.access.permissions": __plugin_implementation__.get_additional_permissions, + "octoprint.comm.protocol.atcommand.sending": __plugin_implementation__.processAtCommand, + "octoprint.comm.protocol.gcode.sent": __plugin_implementation__.process_sent_GCODE, "octoprint.comm.protocol.gcode.received": __plugin_implementation__.on_parse_gcode, "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information } diff --git a/octoprint_klipper/cfgUtils.py b/octoprint_klipper/cfgUtils.py index f6bc465..553cbb1 100644 --- a/octoprint_klipper/cfgUtils.py +++ b/octoprint_klipper/cfgUtils.py @@ -1,9 +1,10 @@ from __future__ import absolute_import, division, print_function, unicode_literals import glob import os, time, sys +import io import flask -from . import util, logger +from octoprint_klipper.util import * from flask_babel import gettext from shutil import copy, copyfile @@ -15,7 +16,8 @@ except ImportError: if sys.version_info[0] < 3: import StringIO -def list_cfg_files(self, path: str) -> list: + +def list_cfg_files(self, path): """Generate list of config files. Args: @@ -30,11 +32,11 @@ def list_cfg_files(self, path: str) -> list: cfg_path = os.path.join(self.get_plugin_data_folder(), "configs", "*") else: cfg_path = os.path.expanduser( - self._settings.get(["configuration", "configpath"]) + self._settings.get(["configuration", "config_path"]) ) cfg_path = os.path.join(cfg_path, "*.cfg") cfg_files = glob.glob(cfg_path) - logger.log_debug(self, "list_cfg_files Path: " + cfg_path) + log_debug(self, "list_cfg_files Path: " + cfg_path) for f in cfg_files: filesize = os.path.getsize(f) @@ -50,9 +52,10 @@ def list_cfg_files(self, path: str) -> list: mdate= time.strftime("%d.%m.%Y %H:%M", filemdate), url= url, )) - logger.log_debug(self, "list_cfg_files " + str(len(files)) + ": " + f) + log_debug(self, "list_cfg_files " + str(len(files)) + ": " + f) return files + def get_cfg(self, file): """Get the content of a configuration file. @@ -71,31 +74,43 @@ def get_cfg(self, file): cfg_path = os.path.expanduser( self._settings.get(["configuration", "configpath"]) ) - file = os.path.join(cfg_path, "printer.cfg") - if util.file_exist(self, file): - logger.log_debug(self, "get_cfg_files Path: " + file) + file = os.path.join(cfg_path, self._settings.get(["configuration", "baseconfig"])) + if file_exist(self, file): + log_debug(self, "get_cfg_files Path: " + file) try: - with open(file, "r") as f: + with io.open(file, "r", encoding='utf-8') as f: response['config'] = f.read() except IOError as Err: - logger.log_error( + log_error( self, - "Error: Klipper config file not found at: {}".format(file) - + "\n IOError: {}".format(Err) + gettext("Error: Klipper config file not found at:") + + " {}".format(file) + + "\n" + + gettext("IOError:") + " {}".format(Err) + ) + response['text'] = Err + return response + except UnicodeDecodeError as Err: + log_error( + self, + gettext("Decode Error:") + +"\n" + + "{}".format(Err) + + "\n\n" + + gettext("Please convert your config files to utf-8!") + + "\n" + + gettext("Or you can also paste your config \ninto the Editor and save it.") ) response['text'] = Err return response else: - if sys.version_info[0] < 3: - response['config'] = response.config.decode('utf-8') return response - finally: - f.close() else: response['text'] = gettext("File not found!") return response -def save_cfg(self, content, filename="printer.cfg"): + +def save_cfg(self, content, filename): """Save the configuration file to given file. Args: @@ -106,43 +121,35 @@ def save_cfg(self, content, filename="printer.cfg"): bool: True if the configuration file was saved successfully. Otherwise False """ - logger.log_debug( + log_debug( self, "Save klipper config" ) - if sys.version_info[0] < 3: - content = content.encode('utf-8') - configpath = os.path.expanduser(self._settings.get(["configuration", "configpath"])) + configpath = os.path.expanduser(self._settings.get(["configuration", "config_path"])) + if filename == "": + filename = self._settings.get(["configuration", "baseconfig"]) 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_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 - + log_debug(self, "Writing Klipper config to {}".format(filepath)) try: - logger.log_debug(self, "Writing Klipper config to {}".format(filepath)) - with open(filepath, "w") as f: + with io.open(filepath, "w", encoding='utf-8') as f: f.write(content) except IOError: - logger.log_error(self, "Error: Couldn't open Klipper config file: {}".format(filepath)) + 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)) + log_debug(self, "Written Klipper config to {}".format(filepath)) return True finally: - f.close() copy_cfg_to_backup(self, filepath) -def check_cfg(self, data): + +def check_cfg_ok(self, data): """Checks the given data on parsing errors. Args: @@ -161,15 +168,16 @@ def check_cfg(self, data): dataToValidated.read_string(data) except configparser.Error as error: show_error_message(self, error) - logger.log_debug(self, 'check_cfg: NOK!') + log_debug(self, 'check_cfg: NOK!') return False else: if not is_float_ok(self, dataToValidated): - logger.log_debug(self, "check_cfg: NOK!") + log_debug(self, "check_cfg: NOK!") return False - logger.log_debug(self, "check_cfg: OK") + 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: @@ -179,14 +187,11 @@ def show_error_message(self, error): else: error.message = error.message.replace('file:', 'Klipper Configuration', 1) error.message = error.message.replace("'", '', 2) - logger.log_error( + 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): @@ -206,24 +211,25 @@ def is_float_ok(self, dataToValidated): if dataToValidated.has_option(y, x): a_float = dataToValidated.getfloat(y, x) except ValueError as error: - logger.log_error( + log_error( self, "Error: Invalid Value for " + x + " in Section: " + y + "\n" + "{}".format(str(error)) ) - util.send_message( + send_message( self, - "PopUp", - "warning", - "Invalid Config data\n", - "\n" - + "Invalid Value for " + x + " in Section: " + y + "\n" - + "{}".format(str(error)) + type = "PopUp", + subtype = "warning", + title = "Invalid Config data\n", + payload = "\n" + + "Invalid Value for " + x + " in Section: " + y + "\n" + + "{}".format(str(error)) ) return False else: return True + def copy_cfg(self, file, dst): """Copy the config file to the destination. @@ -239,13 +245,13 @@ def copy_cfg(self, file, dst): try: copy(file, dst) except IOError: - logger.log_error( + log_error( self, "Error: Klipper config file not found at: {}".format(file) ) return False else: - logger.log_debug( + log_debug( self, "File copied: " + file @@ -253,6 +259,7 @@ def copy_cfg(self, file, dst): return True return False + def copy_cfg_to_backup(self, src): """Copy the config file to backup directory of OctoKlipper. @@ -272,26 +279,23 @@ def copy_cfg_to_backup(self, src): try: os.mkdir(cfg_path) except OSError: - logger.log_error(self, "Error: Creation of the backup directory {} failed".format(cfg_path)) + log_error(self, "Error: Creation of the backup directory {} failed".format(cfg_path)) return False else: - logger.log_debug(self, "Directory {} created".format(cfg_path)) + log_debug(self, "Directory {} created".format(cfg_path)) dst = os.path.join(cfg_path, filename) - logger.log_debug(self, "copy_cfg_to_backup:" + src + " to " + dst) + log_debug(self, "copy_cfg_to_backup:" + src + " to " + dst) if src == dst: return False try: copyfile(src, dst) except IOError: - logger.log_error( + log_error( self, "Error: Couldn't copy Klipper config file to {}".format(dst) ) return False else: - logger.log_debug(self, "CfgBackup " + dst + " writen") + log_debug(self, "CfgBackup " + dst + " written") return True - - - diff --git a/octoprint_klipper/logger.py b/octoprint_klipper/logger.py deleted file mode 100644 index 46ae70f..0000000 --- a/octoprint_klipper/logger.py +++ /dev/null @@ -1,17 +0,0 @@ -from . import util - -def log_info(self, message): - self._octoklipper_logger.info(message) - util.send_message(self, "log", "info", message, message) - -def log_debug(self, message): - self._octoklipper_logger.debug(message) - self._logger.info(message) - # sends a message to frontend(in klipper.js -> self.onDataUpdaterPluginMessage) and write it to the console. - # _mtype, subtype=debug/info, title of message, message) - util.send_message(self, "console", "debug", message, message) - -def log_error(self, error): - self._octoklipper_logger.error(error) - self._logger.error(error) - util.send_message(self, "log", "error", error, error) diff --git a/octoprint_klipper/modules/KlipperLogAnalyzer.py b/octoprint_klipper/modules/KlipperLogAnalyzer.py index 5bce9ff..f7e153f 100644 --- a/octoprint_klipper/modules/KlipperLogAnalyzer.py +++ b/octoprint_klipper/modules/KlipperLogAnalyzer.py @@ -15,7 +15,7 @@ import flask import optparse, datetime -from .. import logger +from octoprint_klipper.util import log_error class KlipperLogAnalyzer(): MAXBANDWIDTH=25000. @@ -82,7 +82,7 @@ class KlipperLogAnalyzer(): out.append(keyparts) f.close() except IOError: - logger.log_error(self, "Couldn't open log file: {}".format(logname)) + log_error(self, "Couldn't open log file: {}".format(logname)) print("Couldn't open log file") return out diff --git a/octoprint_klipper/static/css/klipper.css b/octoprint_klipper/static/css/klipper.css index 445c932..d7b5406 100644 --- a/octoprint_klipper/static/css/klipper.css +++ b/octoprint_klipper/static/css/klipper.css @@ -1,11 +1,13 @@ .plugin-klipper-sidebar { padding: 1px; height: auto; + max-height: 100px; border: 1px solid #aaa; width: 98%; text-align: center; - word-break: break-all; + word-break: break-word; margin: auto; + overflow: auto; } li#navbar_plugin_klipper { @@ -97,6 +99,7 @@ ul#klipper-settings { #tab_plugin_klipper_main #left-side { flex: 3 1; padding-right: 10px; + padding-top: 5px; } #tab_plugin_klipper_main .span8 label { @@ -196,7 +199,7 @@ div#settings_plugin_klipper form { } div#settings_plugin_klipper div.tab-content { - height: calc(100% - 58px); + height: calc(100% - 76px); overflow: auto; } @@ -315,8 +318,7 @@ div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content vertical-align: -0.2em; } -div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div.tab-pane.active label.inline, -div#klipper_editor .inline { +.klipper-inline { display: inline; } @@ -339,10 +341,6 @@ div#settings_plugin_klipper div#macros label.control-label { padding-top: 2px; } -#klipper_graph_dialog { - width: 90%; -} - #klipper_graph_dialog .full-sized-box { width: 100%; margin: 0 auto; diff --git a/octoprint_klipper/static/js/klipper.js b/octoprint_klipper/static/js/klipper.js index 2d3eacb..de85840 100644 --- a/octoprint_klipper/static/js/klipper.js +++ b/octoprint_klipper/static/js/klipper.js @@ -33,22 +33,58 @@ $(function () { self.access = parameters[5]; self.shortStatus_navbar = ko.observable(); + self.shortStatus_navbar_hover = ko.observable(); self.shortStatus_sidebar = ko.observable(); self.logMessages = ko.observableArray(); + self.popup = undefined; + + self._showPopup = function (options) { + self._closePopup(); + self.popup = new PNotify(options); + }; + + self._updatePopup = function (options) { + if (self.popup === undefined) { + self._showPopup(options); + } else { + self.popup.update(options); + } + }; + + self._closePopup = function () { + if (self.popup !== undefined) { + self.popup.remove(); + } + }; + self.showPopUp = function (popupType, popupTitle, message) { var title = "OctoKlipper:
" + popupTitle + "
"; - var hide = false; - if (popupType == "success") { - hide = true - } - new PNotify({ + var options = undefined; + var errorOpts = {}; + + options = { title: title, text: message, type: popupType, - hide: hide, + hide: true, icon: true - }); + }; + + if (popupType == "error") { + + errorOpts = { + mouse_reset: true, + delay: 5000, + animation: "none" + }; + FullOptions = Object.assign(options, errorOpts); + self._showPopup(FullOptions); + } else { + if (options !== undefined) { + new PNotify(options); + } + } }; self.showEditorDialog = function () { @@ -92,6 +128,7 @@ $(function () { var dialog = $("#klipper_graph_dialog"); dialog.modal({ show: "true", + width: "90%", minHeight: "500px", maxHeight: "600px", }); @@ -142,40 +179,57 @@ $(function () { }; self.onDataUpdaterPluginMessage = function (plugin, data) { + if (plugin == "klipper") { switch (data.type) { case "PopUp": self.showPopUp(data.subtype, data.title, data.payload); break; - case "start": - break; case "reload": break; case "console": self.consoleMessage(data.subtype, data.payload); break; case "status": - if (data.payload.length > 36) { - var shortText = data.payload.substring(0, 31) + " [..]"; - self.shortStatus_navbar(shortText); - } else { - self.shortStatus_navbar(data.payload); - } - self.shortStatus_sidebar(data.payload); + self.shortStatus(data.payload, data.subtype); break; default: self.logMessage(data.time, data.subtype, data.payload); + self.shortStatus(data.payload, data.subtype) self.consoleMessage(data.subtype, data.payload); } } }; + + self.shortStatus = function(msg, type) { + + var baseText = gettext("Go to OctoKlipper Tab"); + if (msg.length > 36) { + var shortText = msg.substring(0, 31) + " [..]"; + self.shortStatus_navbar(shortText); + self.shortStatus_navbar_hover(msg); + } else { + self.shortStatus_navbar(msg); + self.shortStatus_navbar_hover(baseText); + } + message = msg.replace(/\n/gi, "
"); + self.shortStatus_sidebar(message); + }; + + self.logMessage = function (timestamp, type = "info", message) { + if (!timestamp) { var today = new Date(); var timestamp = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds(); } + + if (type == "error" && self.settings.settings.plugins.klipper.configuration.hide_error_popups() !== true) { + self.showPopUp(type, "Error:", message); + } + self.logMessages.push({ time: timestamp, type: type, @@ -232,23 +286,67 @@ $(function () { } }; + self.saveOption = function(dir, option, value) { + if (! (_.includes(["fontsize", "confirm_reload", "parse_check"], option)) ) { + return; + } + + if (option && dir) { + let data = { + plugins: { + klipper:{ + [dir]: { + [option]: value + } + } + } + }; + OctoPrint.settings + .save(data); + } else if (option) { + let data = { + plugins: { + klipper:{ + [option]: value + } + } + }; + OctoPrint.settings + .save(data); + } + } + 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 request = function (index) { + OctoPrint.plugins.klipper.restartKlipper().done(function (response) { + self.consoleMessage("debug", "restartingKlipper"); + self.showPopUp("success", gettext("Restarted Klipper"), "command: " + response.command); }); + if (index == 1) { + self.saveOption("configuration", "confirm_reload", false); + } }; - var html = "

" + gettext("All ongoing Prints will be stopped!") + "

"; + var html = "

" + + gettext("All ongoing Prints will be stopped!") + + "

"; - showConfirmationDialog({ - title: gettext("Klipper restart?"), - html: html, - proceed: gettext("Proceed"), - onproceed: request, - }); + if (self.settings.settings.plugins.klipper.configuration.confirm_reload() == true) { + showConfirmationDialog({ + title: gettext("Restart Klipper?"), + html: html, + proceed: [gettext("Restart"), gettext("Restart and don't ask this again.")], + onproceed: function (idx) { + if (idx > -1) { + request(idx); + } + }, + }); + } else { + request(0); + } }; // OctoKlipper settings link diff --git a/octoprint_klipper/static/js/klipper_backup.js b/octoprint_klipper/static/js/klipper_backup.js index dbf1010..1e35c7a 100644 --- a/octoprint_klipper/static/js/klipper_backup.js +++ b/octoprint_klipper/static/js/klipper_backup.js @@ -119,7 +119,7 @@ $(function () { }; showConfirmationDialog( - _.sprintf(gettext('You are about to delete backuped config file "%(name)s".'), { + _.sprintf(gettext('You are about to delete backed config file "%(name)s".'), { name: _.escape(backup), }), perform @@ -165,7 +165,7 @@ $(function () { }; showConfirmationDialog( - _.sprintf(gettext("You are about to restore %(count)d backuped config files."), { + _.sprintf(gettext("You are about to restore %(count)d backed config files."), { count: self.markedForFileRestore().length, }), perform @@ -180,7 +180,7 @@ $(function () { }; showConfirmationDialog( - _.sprintf(gettext("You are about to delete %(count)d backuped config files."), { + _.sprintf(gettext("You are about to delete %(count)d backed config files."), { count: self.markedForFileRestore().length, }), perform @@ -192,7 +192,7 @@ $(function () { title = gettext("Restoring klipper config files"); self.klipperViewModel.consoleMessage("debug", title); - message = _.sprintf(gettext("Restoring %(count)d backuped config files..."), { + message = _.sprintf(gettext("Restoring %(count)d backed config files..."), { count: files.length, }); @@ -246,7 +246,7 @@ $(function () { var title, message, handler; title = gettext("Deleting backup files"); - message = _.sprintf(gettext("Deleting %(count)d backup files..."), { + message = _.sprintf(gettext("Deleting %(count)d backed files..."), { count: files.length, }); diff --git a/octoprint_klipper/static/js/klipper_editor.js b/octoprint_klipper/static/js/klipper_editor.js index bd4d0e2..8e34c34 100644 --- a/octoprint_klipper/static/js/klipper_editor.js +++ b/octoprint_klipper/static/js/klipper_editor.js @@ -16,58 +16,77 @@ $(function () { function KlipperEditorViewModel(parameters) { var self = this; - var obKlipperConfig = null; var editor = null; - var editor_dirty = false; + var editordialog = $("#klipper_editor"); self.settings = parameters[0]; self.klipperViewModel = parameters[1]; self.CfgFilename = ko.observable(""); self.CfgContent = ko.observable(""); - self.config = [] + self.loadedConfig = ""; + self.CfgChangedExtern = false; self.header = OctoPrint.getRequestHeaders({ "content-type": "application/json", "cache-control": "no-cache", }); - $('#klipper_editor').on('shown.bs.modal', function () { - editor.focus(); - self.setEditorDivSize(); - $(window).on('resize', function(){ - self.klipperViewModel.sleep(500).then( - function () { - self.setEditorDivSize(); - } - ); - }); + $(window).on('resize', function() { + self.klipperViewModel.sleep(200).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.onShown = function () { + self.checkExternChange(); + editor.focus(); + self.setEditorDivSize(); + }; + + self.close_selection = function (index) { + switch (index) { + case 0: + editordialog.modal('hide'); + break; + case 1: + self.editorFocusDelay(1000); + break; + case 2: + self.saveCfg({closing: true}); + break; } - } + }; + + self.closeEditor = function () { + self.CfgContent(editor.getValue()); + if (self.loadedConfig != self.CfgContent()) { + + var opts = { + title: gettext("Closing without saving"), + message: gettext("Your file seems to have changed.") + + "
" + + gettext("Do you really want to close it?"), + selections: [gettext("Close"), gettext("Do not close"), gettext("Save & Close")], + maycancel: false, + onselect: function (index) { + if (index > -1) { + self.close_selection(index); + } + }, + }; + + showSelectionDialog(opts); + } else { + editordialog.modal('hide'); + } + }; self.addStyleAttribute = function ($element, styleAttribute) { - $element.attr('style', $element.attr('style') + '; ' + styleAttribute); - } + $element.attr('style', styleAttribute); + }; self.setEditorDivSize = function () { var klipper_modal_body= $('#klipper_editor .modal-body'); @@ -75,24 +94,23 @@ $(function () { 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.resize(); } }; + //initialize the modal window and return done when finished self.process = function (config) { return new Promise(function (resolve) { - self.config = config; + self.loadedConfig = config.content; self.CfgFilename(config.file); self.CfgContent(config.content); if (editor) { editor.session.setValue(self.CfgContent()); - editor_dirty=false; + self.CfgChangedExtern = 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() { @@ -102,56 +120,141 @@ $(function () { ); } }); - } - - self.checkSyntax = function () { - if (editor.session) { - 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"), - onclose: function () { self.editorFocusDelay(1000); } - } - ); - } - }); - }; }; - self.saveCfg = function () { - if (editor.session) { - self.klipperViewModel.consoleMessage("debug", "SaveCfg start"); + self.onDataUpdaterPluginMessage = function (plugin, data) { + //receive from backend after a SAVE_CONFIG + if (plugin == "klipper" && data.type == "reload" && data.subtype == "config") { + self.klipperViewModel.consoleMessage("debug", "onDataUpdaterPluginMessage klipper reload baseconfig"); + self.ConfigChangedAfterSave_Config(); + } + }; - 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"), - onclose: function () { self.editorFocusDelay(1000); } - } - ) - } + //set externally changed config flag if the current file is the base config + self.ConfigChangedAfterSave_Config = function () { + if (!self.klipperViewModel.hasRight("CONFIG")) return; + + if (self.CfgFilename() == self.settings.settings.plugins.klipper.configuration.baseconfig()) { + self.CfgChangedExtern = true; + self.checkExternChange(); + } + }; + + //check if the config was externally changed and ask for a reload + self.checkExternChange = function() { + var baseconfig = self.settings.settings.plugins.klipper.configuration.baseconfig(); + if (self.CfgChangedExtern && self.CfgFilename() == baseconfig) { + if (editordialog.is(":visible")) { + + var perform = function () { + self.reloadFromFile(); + } + + var html = "

" + gettext("Reload Configfile after SAVE_CONFIG?") + "

"; + + showConfirmationDialog({ + title: gettext("Externally changed config") + " " + baseconfig, + html: html, + proceed: gettext("Proceed"), + onproceed: perform, }); + } + } + }; + + self.askSaveFaulty = function () { + return new Promise(function (resolve) { + var html = "
" + + gettext("Your configuration seems to be faulty.") + + "
"; + + showConfirmationDialog({ + title: gettext("Save faulty Configuration?"), + html: html, + cancel: gettext("Do not save!"), + proceed: [gettext("Save anyway!"), gettext("Save anyway and don't ask this again.")], + onproceed: function (idx) { + if (idx == 0) { + resolve(true); + } else { + self.klipperViewModel.saveOption("configuration", "parse_check", false); + resolve(true); + } + }, + oncancel: function () { + resolve(false); + } + }); + }); + }; + + self.checkSyntax = function () { + return new Promise((resolve, reject) => { + if (editor.session) { + self.klipperViewModel.consoleMessage("debug", "checkSyntax started"); + + OctoPrint.plugins.klipper.checkCfg(editor.session.getValue()) + .done(function (response) { + if (response.is_syntax_ok == true) { + self.klipperViewModel.showPopUp("success", gettext("SyntaxCheck"), gettext("SyntaxCheck OK")); + self.editorFocusDelay(1000); + resolve(true); + } else { + self.editorFocusDelay(1000); + resolve(false); + } + }) + .fail(function () { + reject(false); + }); + } else { reject(false); } + }); + }; + + self.saveCfg = function (options) { + var options = options || {}; + var closing = options.closing || false; + + if (self.CfgFilename() != "") { + if (editor.session) { + if (self.settings.settings.plugins.klipper.configuration.parse_check() == true) { + + // check Syntax and wait for response + self.checkSyntax().then((syntaxOK) => { + if (syntaxOK === false) { + + // Ask if we should save a faulty config anyway + self.askSaveFaulty().then((areWeSaving) => { + if (areWeSaving === false) { + // Not saving + showMessageDialog( + gettext('Faulty config not saved!'), + { + title: gettext("Save Config"), + onclose: function () { self.editorFocusDelay(1000); } + } + ); + } else { + // Save anyway + self.saveRequest(closing); + } + }); + } else { + // Syntax is ok + self.saveRequest(closing); + } + }); + } else { + self.saveRequest(closing); + } + } + } else { + showMessageDialog( + gettext("No filename set"), + { + title: gettext("Save Config") + } + ); } }; @@ -159,82 +262,78 @@ $(function () { self.settings.settings.plugins.klipper.configuration.fontsize( self.settings.settings.plugins.klipper.configuration.fontsize() - 1 ); + if (self.settings.settings.plugins.klipper.configuration.fontsize() < 9) { self.settings.settings.plugins.klipper.configuration.fontsize(9); } + + var fontsize = self.settings.settings.plugins.klipper.configuration.fontsize(); if (editor) { - editor.setFontSize( - self.settings.settings.plugins.klipper.configuration.fontsize() - ); + editor.setFontSize(fontsize); editor.resize(); } + + self.klipperViewModel.saveOption("configuration", "fontsize", fontsize); }; self.plusFontsize = function () { self.settings.settings.plugins.klipper.configuration.fontsize( self.settings.settings.plugins.klipper.configuration.fontsize() + 1 ); + if (self.settings.settings.plugins.klipper.configuration.fontsize() > 20) { self.settings.settings.plugins.klipper.configuration.fontsize(20); } + + var fontsize = self.settings.settings.plugins.klipper.configuration.fontsize(); if (editor) { - editor.setFontSize( - self.settings.settings.plugins.klipper.configuration.fontsize() - ); + editor.setFontSize(fontsize); editor.resize(); } - }; - - self.loadLastSession = function () { - if (self.settings.settings.plugins.klipper.configuration.old_config() != "") { - self.klipperViewModel.consoleMessage( - "info", - "lastSession:" + - self.settings.settings.plugins.klipper.configuration.old_config() - ); - if (editor.session) { - editor.session.setValue( - self.settings.settings.plugins.klipper.configuration.old_config() - ); - editor.clearSelection(); - } - } + self.klipperViewModel.saveOption("configuration", "fontsize", fontsize); }; self.reloadFromFile = function () { - - OctoPrint.plugins.klipper.getCfg(self.CfgFilename()) + if (self.CfgFilename() != "") { + OctoPrint.plugins.klipper.getCfg(self.CfgFilename()) .done(function (response) { self.klipperViewModel.consoleMessage("debug", "reloadFromFile done"); if (response.response.text != "") { - var msg = response.response.text showMessageDialog( - msg, + response.response.text, { title: gettext("Reload File") } - ) + ); } else { self.klipperViewModel.showPopUp("success", gettext("Reload Config"), gettext("File reloaded.")); + self.CfgChangedExtern = false; if (editor) { editor.session.setValue(response.response.config); + self.loadedConfig = response.response.config; editor.clearSelection(); editor.focus(); } } }) .fail(function (response) { - var msg = response showMessageDialog( - msg, + response, { title: gettext("Reload File") } - ) + ); }); + } else { + showMessageDialog( + gettext("No filename set"), + { + title: gettext("Reload File") + } + ); + } }; - self.onStartup = function () { ace.config.set("basePath", "plugin/klipper/static/js/lib/ace/"); editor = ace.edit("plugin-klipper-config"); @@ -252,7 +351,6 @@ $(function () { editor.session.on('change', function (delta) { self.CfgContent(editor.getValue()); - editor_dirty = true; editor.resize(); }); }; @@ -264,6 +362,32 @@ $(function () { } ); }; + + self.saveRequest = function (closing) { + self.klipperViewModel.consoleMessage("debug", "SaveCfg start"); + + OctoPrint.plugins.klipper.saveCfg(editor.session.getValue(), self.CfgFilename()) + .done(function (response) { + if (response.saved === true) { + self.klipperViewModel.showPopUp("success", gettext("Save Config"), gettext("File saved.")); + self.loadedConfig = editor.session.getValue(); //set loaded config to current for resetting dirtyEditor + if (closing) { + editordialog.modal('hide'); + } + if (self.settings.settings.plugins.klipper.configuration.restart_onsave() == true) { + self.klipperViewModel.requestRestart(); + } + } else { + showMessageDialog( + gettext('File not saved!'), + { + title: gettext("Save Config"), + onclose: function () { self.editorFocusDelay(1000); } + } + ); + } + }); + }; } OCTOPRINT_VIEWMODELS.push({ diff --git a/octoprint_klipper/static/js/klipper_settings.js b/octoprint_klipper/static/js/klipper_settings.js index 8c2f8a7..29d2b06 100644 --- a/octoprint_klipper/static/js/klipper_settings.js +++ b/octoprint_klipper/static/js/klipper_settings.js @@ -30,6 +30,11 @@ $(function () { }); self.markedForFileRemove = ko.observableArray([]); + self.PathToConfigs = ko.observable(""); + + $(document).on('shown.bs.modal','#klipper_editor', function () { + self.klipperEditorViewModel.onShown(); + }); self.checkFontsize = function () { if (self.settings.settings.plugins.klipper.configuration.fontsize() > 20) { @@ -80,6 +85,7 @@ $(function () { OctoPrint.plugins.klipper.listCfg().done(function (response) { self.klipperViewModel.consoleMessage("debug", "listCfgFiles done"); self.configs.updateItems(response.files); + self.PathToConfigs(gettext("Path: ") + response.path); self.configs.resetPage(); }); }; @@ -87,13 +93,17 @@ $(function () { 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(); - }); + var baseconfig = self.settings.settings.plugins.klipper.configuration.baseconfig(); + if (baseconfig != "") { + self.klipperViewModel.consoleMessage("debug", "loadBaseConfig:" + baseconfig); + OctoPrint.plugins.klipper.getCfg(baseconfig).done(function (response) { + var config = { + content: response.response.config, + file: baseconfig, + }; + self.klipperEditorViewModel.process(config).then(); + }); + } }; self.removeCfg = function (config) { @@ -245,8 +255,6 @@ $(function () { self.klipperEditorViewModel.process(config).then( function() { self.showEditor(); } ); - - }); }; @@ -312,21 +320,8 @@ $(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({ diff --git a/octoprint_klipper/templates/klipper_editor.jinja2 b/octoprint_klipper/templates/klipper_editor.jinja2 index e1907e6..23f131b 100644 --- a/octoprint_klipper/templates/klipper_editor.jinja2 +++ b/octoprint_klipper/templates/klipper_editor.jinja2 @@ -31,14 +31,14 @@ - + diff --git a/octoprint_klipper/templates/klipper_navbar.jinja2 b/octoprint_klipper/templates/klipper_navbar.jinja2 index 91d3c32..2c3524a 100644 --- a/octoprint_klipper/templates/klipper_navbar.jinja2 +++ b/octoprint_klipper/templates/klipper_navbar.jinja2 @@ -1,3 +1,3 @@ - + diff --git a/octoprint_klipper/templates/klipper_settings.jinja2 b/octoprint_klipper/templates/klipper_settings.jinja2 index 92fa69a..2dd5939 100644 --- a/octoprint_klipper/templates/klipper_settings.jinja2 +++ b/octoprint_klipper/templates/klipper_settings.jinja2 @@ -21,6 +21,13 @@ data-bind="checked: settings.settings.plugins.klipper.connection.replace_connection_panel"> +
+ +
+ +
+
@@ -37,6 +44,13 @@ data-bind="checked: settings.settings.plugins.klipper.configuration.debug_logging" />
+
+ +
+ +
+
@@ -52,7 +66,13 @@
- + +
+
+
+ +
+
@@ -62,7 +82,7 @@
- +
{{ _('Reload klipper on editor save?') }} + +
@@ -87,6 +110,12 @@
+
+ + {{ _('These macros are only meant to be used in OctoPrint.') }} + {{ _('They are not the ones that can be defined in the printer.cfg.') }}
+
+
@@ -151,9 +180,7 @@
- {{ _('This feature assists in manually leveling you print bed by moving the head to the defined points in - sequence.
- If you use a piece of paper for leveling, set "Probe Height" to the paper thickness eg. "0.1".') }} + {{ _('This feature assists in manually leveling your print bed by moving the head to the defined points in sequence.
If you use a piece of paper for leveling, set "Probe Height" to the paper thickness eg. "0.1".') }}
@@ -253,7 +280,8 @@ {{ _('Refresh Files') }} + data-bind="click: removeMarkedFiles, enable: markedForFileRemove().length > 0">{{ _('Delete selected') }} +
@@ -272,6 +300,7 @@
+

diff --git a/octoprint_klipper/templates/klipper_sidebar.jinja2 b/octoprint_klipper/templates/klipper_sidebar.jinja2 index bb4ba7f..61f2842 100644 --- a/octoprint_klipper/templates/klipper_sidebar.jinja2 +++ b/octoprint_klipper/templates/klipper_sidebar.jinja2 @@ -3,12 +3,16 @@ - - + + + + -
- +
diff --git a/octoprint_klipper/templates/klipper_tab_main.jinja2 b/octoprint_klipper/templates/klipper_tab_main.jinja2 index c3829b7..4f84903 100644 --- a/octoprint_klipper/templates/klipper_tab_main.jinja2 +++ b/octoprint_klipper/templates/klipper_tab_main.jinja2 @@ -1,6 +1,11 @@
- + +
@@ -21,11 +26,6 @@ title="{{ _('Query Klipper for its current status') }}"> {{ _("Get Status") }} -