is there no way of accessing standard python modules within a macro? For example time, timestamp, or math?
Unfortunately not. There are some clever things you can do in jinja2 with the “undocumented” native attributes of the python objects that jinja employs, but at least with Klipper’s implementation of it, you can’t simply import python modules.
There’s a pretty good discussion of the issue here, specifically related to time.
And the lack of the math module does create limitations. Depending on your needs in that regard, you might have to see what’s possible within the basic arithmetic supported by jinja. I’m sure it’s not the best example but I was recently working on a macro where I needed to calculate a square root and was disappointed to learn that we don’t have something like a root operand or filter. Then I remembered (somehow) from high school that you can also get a number’s square root by raising the number to the 1/2 power, and we can do that in jinja2 with the ** operand. So depending on your needs, there could be some ugly math hacks you could use to get where you need to be with the limited toolset available.
I recently developed DynamicMacros, which lets you run Python code in a macro, along with several other features. The Python feature lets you import modules.
about the undocumented
ive recently been playing with it a lot myself and was wondering if it was actually undocumented, or if i was really bad at searching the internet.
if theres no guide for it yet, i might make one.
I’ll just leave this here.
[gcode_macro MATH]
gcode:
RESPOND MSG={math.sin(0.5)}`
->>> 0.479425538604203
[gcode_macro _REGISTER_MODULES_AT_START]
variable_val: {}
gcode:
{% if not val %}
{% set wanted = [ 'time','os','sys','math','random',
'itertools','functools','re',
'json','logging','numpy'] %}
{% set env = printer.printer.lookup_object('gcode_macro').env %}
{% set gbl = printer.printer.__class__.__init__.__globals__ %}
{% set ilib = gbl.importlib %}
{% set ok, err = [], [] %}
{% for m in wanted %}
{% set spec = ilib.util.find_spec(m) %}
{% set mod = gbl.get(m) if m in gbl else (ilib.import_module(m) if spec else None) %}
{% if mod is not none %}
{% set _ = env.globals.update({m: mod}) %}
{% set _ = ok.append(' - \'' ~ m ~ '\'') %}
{% else %}
{% set _ = err.append(' - \'' ~ m ~ '\'') %}
{% endif %}
{% endfor %}
{% set va = {'ok': ok, 'err': err}|string|replace('"', '\\"') %}
SET_GCODE_VARIABLE MACRO=_REGISTER_MODULES_AT_START VARIABLE=val VALUE="{va}"
UPDATE_DELAYED_GCODE ID=_REGISTER_MODULES_AT_START DURATION=1
{% else %}
{% set html = "<details><summary>registered: " ~ val.ok|length ~ "</summary>"
~ val.ok|join("<br>") ~ "</details>"
~ (val.err and "<details><summary>failed: " ~ val.err|length ~ "</summary>"
~ val.err|join('<br>') ~ "</details>" or "") %}
{action_respond_info(html)}
{% endif %}
[delayed_gcode _REGISTER_MODULES_AT_START]
initial_duration: 0.01
gcode:
_REGISTER_MODULES_AT_START
…is that allowed?
honestly… no idea. but its clearly the path of least resistance to get stuff like math into your context.