In the example for the M117 macro, the raw params are parsed in the macro itself to remove comments.
I did not want to parse arguments for every single marlin macro, so I wrote one that does the parsing:
# Translates marlin gcode arguments into klipper arguments.
#
# Usage:
# _PARSE_MARLIN_PARAMS TARGET=[<macro>] ARGS='{rawparams}'
# Parameters:
# [<macro>] The name of the macro that should be called with the parsed arguments
#
# Examples:
# ```
# [gcode_macro _T123]
# gcode:
# { action_respond_info("_T123 called with: params={}, rawparams={}".format(params, rawparams)) }
#
# [gcode_macro T123]
# gcode:
# _PARSE_MARLIN_PARAMS TARGET=_T123 ARGS='{rawparams}'
# ```
#
# Note:
# Klipper gcode_macros expect key value arguments like 'MY_MACRO KEY=VALUE'.
#
# The traditional gcode style 'MY_MACRO H123 B C' is not allowed for all macros.
# If 'MY_MACRO' does not have the correct format, klipper will complain about "malformed command".
#
# The following must be satisfied (based on the current source code):
# - Starts with a character 'c' for which the python expression 'c.upper().isupper()' is True
# (all characters 'A-Z' and 'a-z' satisfy this, numbers or special characters like '_' do not)
# - After that a number must follow
# - The format must be '<letter><float>' where <float> must start with a number and the python
# expression 'float("<float>"")' must not fail
#
# I recommend that your macro has a name in the format '<letter><number>' like for example 'T123'.
#
# There is no boolean filter in jinja, so you can not write
# {% set FLAG = params.F|default(false)|boolean %}
#
# As a workaround, I recommend writing this instead:
# {% set FLAG = params.F|default(false)|lower == "true" %}
[gcode_macro _PARSE_MARLIN_PARAMS]
description: Translates marlin params into klipper params
gcode:
{% set TARGET = params.TARGET|string %}
{% set ARGS = params.ARGS|default("")|string %}
# This parses the rawparams by first removing trailing comments with ; and '\x23' = '#', then splitting
# the result by whitespace (to obtain a list of arguments)
{% set parsed_params = ARGS.split(';', 1)[0].split('\x23', 1)[0].lower().split() %}
# { action_respond_info("ARGS: {}, parsed: {}".format(ARGS, parsed_params)) }
{% set macro_command = [ TARGET ] %}
{% for x in parsed_params %}
{% if x|length > 1 %}
{% set _ = macro_command.append("{}={}".format(x[0:1], x[1:])) %}
{% else %}
# Flags are set to true
{% set _ = macro_command.append("{}=true".format(x)) %}
{% endif %}
{% endfor %}
# { action_respond_info("Command: {}".format(macro_command|join(' '))) }
{ macro_command|join(' ') }
This is especially useful for macros where an argument has a value like H<number>
.
For example the gcode G12 accepts the following arguments
G12 [P<0|1|2>] [R<radius>] [S<count>] [T<count>] [X] [Y] [Z]
[gcode_macro _G12]
gcode:
{ action_respond_info("_G12 called with: params={}, rawparams={}".format(params, rawparams)) }
# G12 [P<0|1|2>] [R<radius>] [S<count>] [T<count>] [X] [Y] [Z]
{% set P = params.P|default(0)|int %}
{% set R = params.R|default(0)|float %}
{% set T = params.T|default(0)|int %}
{% set S = params.S|default(0)|int %}
{% set X = params.X|default(false)|lower == "true" %}
{% set Y = params.Y|default(false)|lower == "true" %}
{% set Z = params.Z|default(false)|lower == "true" %}
{ action_respond_info("_G12 P={} R={} T={} S={} X={} Y={} Z={}".format(P, R, T, S, X, Y, Z)) }
[gcode_macro G12]
gcode:
_PARSE_MARLIN_PARAMS TARGET=_G12 ARGS='{rawparams}'
$ G12 P1 R2.1 T3 S5 X Y
_G12 called with: params={'P': '1', 'R': '2.1', 'T': '3', 'S': '5', 'X': 'true', 'Y': 'true'}, rawparams=p=1 r=2.1 t=3 s=5 x=true y=true
_G12 P=1 R=2.1 T=3 S=5 X=True Y=True Z=False
$ G12
_G12 called with: params={}, rawparams=
_G12 P=0 R=0.0 T=0 S=0 X=False Y=False Z=False
Not having a boolean
filter is a bit of a pain (especially when some might use 0 or 1 to indicate true/false). Jinja supports creating custom filters, but not inside templates: python - Embed custom filter definition into jinja2 template? - Stack Overflow
This would require a PR against the Klipper Repo, which I would be willing to do if it will be merged.