diff --git a/docs/Command_Templates.md b/docs/Command_Templates.md index 47f7755e..26271b75 100644 --- a/docs/Command_Templates.md +++ b/docs/Command_Templates.md @@ -396,3 +396,38 @@ gcode: ``` UPDATE_DELAYED_GCODE ID=report_temp DURATION=0 ``` + +### Save Variables to disk + +If a +[save_variables config section](Config_Reference.md#save_variables) +has been enabled, `SAVE_VARIABLE VARIABLE= VALUE=` can be +used to save the variable to disk so that it can be used across +restarts. All stored variables are loaded into the +`printer.save_variables.variables` dict at startup and can be used in +gcode macros. to avoid overly long lines you can add the following at +the top of the macro: +``` +{% set svv = printer.save_variables.variables %} +``` + +As an example, it could be used to save the state of 2-in-1-out hotend +and when starting a print ensure that the active extruder is used, +instead of T0: + +``` +[gcode_macro T1] +gcode: + ACTIVATE_EXTRUDER extruder=extruder1 + SAVE_VARIABLE VARIABLE=currentextruder VALUE='"extruder1"' + +[gcode_macro T0] +gcode: + ACTIVATE_EXTRUDER extruder=extruder + SAVE_VARIABLE VARIABLE=currentextruder VALUE='"extruder"' + +[gcode_macro START_GCODE] +gcode: + {% set svv = printer.save_variables.variables %} + ACTIVATE_EXTRUDER extruder={svv.currentextruder} +``` diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 54156c68..5b0a8b54 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -1182,6 +1182,20 @@ gcode: # Default is 0. ``` +## [save_variables] + +Support saving variables to disk so that they are retained across +restarts. See +[command templates](Command_Templates.md#save-variables-to-disk) and +[G-Code reference](G-Codes.md#save-variables) for further information. + +``` +[save_variables] +filename: +# Required - provide a filename that would be used to save the +# variables to disk e.g. ~/variables.cfg +``` + ## [idle_timeout] Idle timeout. An idle timeout is automatically enabled - add an diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 9d9c0619..1a944f0c 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -647,6 +647,17 @@ been enabled (also see the for gcode execution. A value of 0 will cancel a pending delayed gcode from executing. +## Save Variables + +The following command is enabled if a +[save_variables config section](Config_Reference.md#save_variables) +has been enabled: +- `SAVE_VARIABLE VARIABLE= VALUE=`: Saves the variable to + disk so that it can be used across restarts. All stored variables + are loaded into the `printer.save_variables.variables` dict at + startup and can be used in gcode macros. The provided VALUE is + parsed as a Python literal. + ## Resonance compensation The following command is enabled if an diff --git a/klippy/extras/save_variables.py b/klippy/extras/save_variables.py new file mode 100644 index 00000000..8a6e9bec --- /dev/null +++ b/klippy/extras/save_variables.py @@ -0,0 +1,63 @@ +# Save arbitrary variables so that values can be kept across restarts. +# +# Copyright (C) 2020 Dushyant Ahuja +# Copyright (C) 2016-2020 Kevin O'Connor +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import os, logging, ast, ConfigParser + +class SaveVariables: + def __init__(self, config): + self.printer = config.get_printer() + self.filename = os.path.expanduser(config.get('filename')) + self.allVariables = {} + try: + self.loadVariables() + except self.printer.command_error as e: + raise config.error(str(e)) + gcode = self.printer.lookup_object('gcode') + gcode.register_command('SAVE_VARIABLE', self.cmd_SAVE_VARIABLE, + desc=self.cmd_SAVE_VARIABLE_help) + def loadVariables(self): + allvars = {} + varfile = ConfigParser.ConfigParser() + try: + varfile.read(self.filename) + if varfile.has_section('Variables'): + for name, val in varfile.items('Variables'): + allvars[name] = ast.literal_eval(val) + except: + msg = "Unable to parse existing variable file" + logging.exception(msg) + raise self.printer.command_error(msg) + self.allVariables = allvars + cmd_SAVE_VARIABLE_help = "Save arbitrary variables to disk" + def cmd_SAVE_VARIABLE(self, gcmd): + varname = gcmd.get('VARIABLE') + value = gcmd.get('VALUE') + try: + value = ast.literal_eval(value) + except ValueError as e: + raise gcmd.error("Unable to parse '%s' as a literal" % (value,)) + newvars = dict(self.allVariables) + newvars[varname] = value + # Write file + varfile = ConfigParser.ConfigParser() + varfile.add_section('Variables') + for name, val in sorted(newvars.items()): + varfile.set('Variables', name, repr(val)) + try: + f = open(self.filename, "w") + varfile.write(f) + f.close() + except: + msg = "Unable to save variable" + logging.exception(msg) + raise gcmd.error(msg) + gcmd.respond_info("Variable Saved") + self.loadVariables() + def get_status(self, eventtime): + return {'variables': dict(self.allVariables)} + +def load_config(config): + return SaveVariables(config)