I recently created DynamicMacros, a way to update simple macros without restarting Klipper. It works by having the “dynamic” macros in a separate file, not included in your printer.cfg. Then, a [dynamicmacros] config section references that file. Whenever a dynamic macro is reloaded with the DYNAMIC_MACRO command, it reads the provided configuration files and executes the macro. This way, you can edit your macros without restarting Klipper.
Currently, dynamic macros support the following:
Updating without restarting Klipper
Standard [gcode_macro] syntax (if statements, for loops, etc)
Receiving parameters
Accessing the printer object
Storing variables in the macro
Recursion
Receiving variable updates
Running Python code from within a macro
Dynamic delayed_gcode syntax
The following features are in development:
“Sandboxed” macros for security (preventing running Python and/or accessing the printer object)
Standard delayed_gcode syntax
Tutorial for creating Klippy extras and using DynamicMacros to help development
The Dynamic Macros code is based off the normal macros code to provide most of these features. I would really appreciate any feedback.
Can I ask what is the problem you’re trying to solve here?
I don’t understand why the need to do a Klipper restart is an issue after an update. I’m guessing that this is to keep the printer’s home position, temperature setting with the toolhead at a specific location but I can’t think of a situation, with a macro, where this is a hard requirement.
Where I can definitively see an advantage is during macro development.
At least, the way I do it: Produce 42 Klipper restarts until all bugs are halfway gone in 5 lines of macro.
I agree with @Sineos. The main use case for this would be developing a new macro or modifying one mid-print. Just now, I had to modify my attempt at a M900 macro that was accepting incorrect values, and it was nice not to have to restart, as I already had my printer preheated. While restarting Klipper in an idle state just wastes time, restarting Klipper while printing wastes filament and aborts the print.
Would you think its possible to enable dinamic rendering of a macro that uses jinja templating? That is, to render the commands in a macro only just before they are needed.
The case is that a user wanted to do a probe move, and then use the toolhead’s halt position in the next command of the macro. It seemed tricky because all lines of their were rendered before running any of them.
Could be optional by setting a new ‘dynamic’ parameter:
Unfortunately, the way Jinja2 works (Jinja2 converts the macro into G-Codes), all the variables are evaluated before running the macro. However, this can mostly be worked around by using recursive macros, which Dynamic Macros supports. See here for an example.
Right, but is that a limitation from Jinja2, or from the way Klipper uses it to render the macros? Line-by-line rendering does not seem so hard conceptually, but I really don’t know that part of the codebase.
If the limitation is on Klipper’s side, do you know what part of klippy’s code would need to be modified for this? I would add the changes to a klipper fork where I added some additional CNC-related things.
Both. Jinja has multiline constructs and updates to variables that are persistent for the entire template, and Klipper has always allowed these constructs so you can’t decide to start treating macros as a list of single-line templates without breaking a ton of things.
This is valid templating (and not doing anything particularly fancy):
In order to render this “line by line” you would have to basically rearchitect Jinja so that it could return each line as it was rendered, then wait for Klipper to ask for the next line in case the state changed.
You could do something like redefine a macro as a manually separated list of individual templates that are each rendered after the previous one runs, but you can achieve the same thing without code changes by making each element of that list a separate macro.
You could do something like redefine a macro as a manually separated list of individual templates that are each rendered after the previous one runs
Thank you for the idea. I just implemented that into Dynamic Macros, and some examples of it are available in the Examples, and how to use it are in the Tutorial.
This isn’t the end of development for DynamicMacros, but I have tested it enough to consider it stable enough for developing new macros, and using its additional feature set.
Additional features that standard GCode macros don’t have:
DynamicMacros v1.1.1 is a bugfix release, allowing users to configure where their config path is, which is a problem if multiple Klipper instances are being run.
Ability to sandbox macros to control whether or not Python and/or accessing the printer object is enabled
Traditional delayed_gcode syntax for easier migration from standard GCode macros
I also just finished the first three examples of my Klippy extra tutorial. I would really appreciate any feedback, especially on the tutorial. If anything is unclear or you think needs more explaining, please let me know here or by opening a Documentation Issue on Github.