From 67a90d345018776317ce8466c9433aa01ecf30ed Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Thu, 19 Oct 2023 14:14:01 +0100 Subject: [PATCH 1/3] adds Armchair-Engineering discord to readme (#72) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d2036295c..42baca2ea 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,5 @@ To begin using Klipper start by Klipper is Free Software. See the [license](COPYING) or read the [documentation](https://DangerKlippers.github.io/danger-klipper/Overview.html). + +[![Join me on Discord](https://discord.com/api/guilds/1029426383614648421/widget.png?style=banner2)](https://discord.gg/armchairengineeringsux) From c65595af4bb1ed3d39a2eca156d952f6772ec37e Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Thu, 19 Oct 2023 14:14:21 +0100 Subject: [PATCH 2/3] pOmelchenko - virtual_sdcard: add support subdirectories in menu and M'commands (#68) --- README.md | 2 + docs/Config_Reference.md | 10 ++-- klippy/extras/display/menu.py | 2 +- klippy/extras/virtual_sdcard.py | 7 +-- test/klippy/virtual_sdcard.cfg | 85 +++++++++++++++++++++++++++++++++ test/klippy/virtual_sdcard.test | 7 +++ 6 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 test/klippy/virtual_sdcard.cfg create mode 100644 test/klippy/virtual_sdcard.test diff --git a/README.md b/README.md index 42baca2ea..29184004e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ Features merged into the master branch: - [homing: sensorless minimum home distance](https://github.com/DangerKlippers/danger-klipper/pull/65) +- [virtual_sdcard: scanning of subdirectories](https://github.com/DangerKlippers/danger-klipper/pull/68) ([klipper#6327](https://github.com/Klipper3d/klipper/pull/6327)) + "Dangerous Klipper for dangerous users" Klipper is a 3d-Printer firmware. It combines the power of a general diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 814ca3294..31aa013a3 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -82,14 +82,14 @@ A collection of DangerKlipper-specific system options # if False, will warn but allow klipper to still run error_on_unused_config_options: True -# If statistics should be logged +# If statistics should be logged # (helpful for keeping the log clean during development) -log_statistics: True +log_statistics: True # If the config file should be logged at startup -log_config_file_at_startup: True +log_config_file_at_startup: True -# If the bed mesh should be logged on startup +# If the bed mesh should be logged on startup # (helpful for keeping the log clean during development) log_bed_mesh_at_startup: True @@ -1521,6 +1521,8 @@ path: # be provided. #on_error_gcode: # A list of G-Code commands to execute when an error is reported. +#with_subdirs: False +# Enable scanning of subdirectories for the menu and for the M20 and M23 commands. The default is False. ``` diff --git a/klippy/extras/display/menu.py b/klippy/extras/display/menu.py index 7414fcdf7..da82e5bff 100644 --- a/klippy/extras/display/menu.py +++ b/klippy/extras/display/menu.py @@ -691,7 +691,7 @@ def _populate(self): super(MenuVSDList, self)._populate() sdcard = self.manager.printer.lookup_object("virtual_sdcard", None) if sdcard is not None: - files = sdcard.get_file_list() + files = sdcard.get_file_list(sdcard.with_subdirs) for fname, fsize in files: self.insert_item( self.manager.menuitem_from( diff --git a/klippy/extras/virtual_sdcard.py b/klippy/extras/virtual_sdcard.py index 0f8bc78d0..d61ea3d70 100644 --- a/klippy/extras/virtual_sdcard.py +++ b/klippy/extras/virtual_sdcard.py @@ -16,6 +16,7 @@ def __init__(self, config): ) # sdcard state sd = config.get("path") + self.with_subdirs = config.getboolean("with_subdirs", False) self.sdcard_dirname = os.path.normpath(os.path.expanduser(sd)) self.current_file = None self.file_position = self.file_size = 0 @@ -185,7 +186,7 @@ def cmd_SDCARD_PRINT_FILE(self, gcmd): def cmd_M20(self, gcmd): # List SD card - files = self.get_file_list() + files = self.get_file_list(self.with_subdirs) gcmd.respond_raw("Begin file list") for fname, fsize in files: gcmd.respond_raw("%s %d" % (fname, fsize)) @@ -203,7 +204,7 @@ def cmd_M23(self, gcmd): filename = gcmd.get_raw_command_parameters().strip() if filename.startswith("/"): filename = filename[1:] - self._load_file(gcmd, filename) + self._load_file(gcmd, filename, self.with_subdirs) def _load_file(self, gcmd, filename, check_subdirs=False): files = self.get_file_list(check_subdirs) @@ -221,7 +222,7 @@ def _load_file(self, gcmd, filename, check_subdirs=False): except: logging.exception("virtual_sdcard file open") raise gcmd.error("Unable to open file") - gcmd.respond_raw("File opened:%s Size:%d" % (filename, fsize)) + gcmd.respond_raw("File opened: %s Size: %d" % (filename, fsize)) gcmd.respond_raw("File selected") self.current_file = f self.file_position = 0 diff --git a/test/klippy/virtual_sdcard.cfg b/test/klippy/virtual_sdcard.cfg new file mode 100644 index 000000000..7a839f9c2 --- /dev/null +++ b/test/klippy/virtual_sdcard.cfg @@ -0,0 +1,85 @@ +# Test config for sdcard_loop +[virtual_sdcard] +path: test/klippy/sdcard_loop +with_subdirs: True + +[display_status] + +# Override to support unlimited belt size +# (homing Z simply resets its virtual position to 0.0) +[homing_override] +axes: xyz +set_position_x: 0 +set_position_y: 0 +set_position_z: 0 +gcode: + G92 X0 Y0 Z0 + + +[stepper_x] +step_pin: PF0 +dir_pin: PF1 +enable_pin: !PD7 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PE5 +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +[stepper_y] +step_pin: PF6 +dir_pin: !PF7 +enable_pin: !PF2 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PJ1 +position_endstop: 0 +position_max: 200 +homing_speed: 50 + +[stepper_z] +step_pin: PL3 +dir_pin: PL1 +enable_pin: !PK0 +microsteps: 16 +rotation_distance: 8 +endstop_pin: ^PD3 +position_endstop: 0.5 +position_max: 200000000 + +[extruder] +step_pin: PA4 +dir_pin: PA6 +enable_pin: !PA2 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.500 +filament_diameter: 3.500 +heater_pin: PB4 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK5 +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 210 + +[heater_bed] +heater_pin: PH5 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK6 +control: watermark +min_temp: 0 +max_temp: 110 + +[mcu] +serial: /dev/ttyACM0 + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 diff --git a/test/klippy/virtual_sdcard.test b/test/klippy/virtual_sdcard.test new file mode 100644 index 000000000..2dc8bf484 --- /dev/null +++ b/test/klippy/virtual_sdcard.test @@ -0,0 +1,7 @@ +; Virtual SD card unit tests + +DICTIONARY atmega2560.dict +CONFIG virtual_sdcard.cfg + +G28 +M20 From f2ca654415a273b61c1faa5bf507343420a2d0d5 Mon Sep 17 00:00:00 2001 From: Rogerio Goncalves Date: Thu, 19 Oct 2023 14:14:42 +0100 Subject: [PATCH 3/3] arksine/dw-0 gcode_shell_command (#71) --- README.md | 2 + docs/G-Code_Shell_Command.md | 77 ++++++++++++++++++++++ klippy/extras/gcode_shell_command.py | 96 ++++++++++++++++++++++++++++ test/klippy/gcode_shell_command.cfg | 13 ++++ test/klippy/gcode_shell_command.test | 5 ++ 5 files changed, 193 insertions(+) create mode 100644 docs/G-Code_Shell_Command.md create mode 100644 klippy/extras/gcode_shell_command.py create mode 100644 test/klippy/gcode_shell_command.cfg create mode 100644 test/klippy/gcode_shell_command.test diff --git a/README.md b/README.md index 29184004e..c8fa6c5e0 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Features merged into the master branch: - [gcode: Jinja2.ext.do extension](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#5149](https://github.com/Klipper3d/klipper/pull/5149)) +- [gcode: gcode_shell_command](https://github.com/DangerKlippers/danger-klipper/pull/26) ([klipper#2173](https://github.com/Klipper3d/klipper/pull/2173) / [kiuah](https://github.com/dw-0/kiauh/blob/master/resources/gcode_shell_command.py) ) + - [probe: Dockable Probe](https://github.com/DangerKlippers/danger-klipper/pull/43) ([klipper#4328](https://github.com/Klipper3d/klipper/pull/4328)) - [probe: Drop the first result](https://github.com/DangerKlippers/danger-klipper/pull/2) ([klipper#3397](https://github.com/Klipper3d/klipper/issues/3397)) diff --git a/docs/G-Code_Shell_Command.md b/docs/G-Code_Shell_Command.md new file mode 100644 index 000000000..d8e9d506b --- /dev/null +++ b/docs/G-Code_Shell_Command.md @@ -0,0 +1,77 @@ +# G-Code Shell Command Extension + +### Creator of this extension is [Arksine](https://github.com/Arksine). + +This is a brief explanation of how to use the shell command extension for Klipper, which you can install with KIAUH. + +After installing the extension you can execute linux commands or even scripts from within Klipper with custom commands defined in your printer.cfg. + +#### How to configure a shell command: + +```shell +# Runs a linux command or script from within klipper. Note that sudo commands +# that require password authentication are disallowed. All executable scripts +# should include a shebang. +# [gcode_shell_command my_shell_cmd] +#command: +# The linux shell command/script to be executed. This parameter must be +# provided +#timeout: 2. +# The timeout in seconds until the command is forcably terminated. Default +# is 2 seconds. +#verbose: True +# If enabled, the command's output will be forwarded to the terminal. Its +# recommended to set this to false for commands that my run in quick +# succession. Default is True. +``` + +Once you have set up a shell command with the given parameters from above in your printer.cfg you can run the command as follows: +`RUN_SHELL_COMMAND CMD=name` + +Example: + +``` +[gcode_shell_command hello_world] +command: echo hello world +timeout: 2. +verbose: True +``` + +Execute with: +`RUN_SHELL_COMMAND CMD=hello_world` + +### Passing parameters: + +As of commit [f231fa9](https://github.com/dw-0/kiauh/commit/f231fa9c69191f23277b4e3319f6b675bfa0ee42) it is also possible to pass optional parameters to a `gcode_shell_command`. +The following short example shows storing the extruder temperature into a variable, passing that value with a parameter to a `gcode_shell_command`, which then, +once the gcode_macro runs and the gcode_shell_command gets called, executes the `script.sh`. The script then echoes a message to the console (if `verbose: True`) +and writes the value of the parameter into a textfile called `test.txt` located in the home directory. + +Content of the `gcode_shell_command` and the `gcode_macro`: + +``` +[gcode_shell_command print_to_file] +command: sh /home/pi/klipper_config/script.sh +timeout: 30. +verbose: True + +[gcode_macro GET_TEMP] +gcode: + {% set temp = printer.extruder.temperature %} + { action_respond_info("%s" % (temp)) } + RUN_SHELL_COMMAND CMD=print_to_file PARAMS={temp} +``` + +Content of `script.sh`: + +```shell +#!/bin/sh + +echo "temp is: $1" +echo "$1" >> "${HOME}/test.txt" +``` + +## Warning + +This extension may have a high potential for abuse if not used carefully! Also, depending on the command you execute, high system loads may occur and can cause system instabilities. +Use this extension at your own risk and only if you know what you are doing! diff --git a/klippy/extras/gcode_shell_command.py b/klippy/extras/gcode_shell_command.py new file mode 100644 index 000000000..05dd478d2 --- /dev/null +++ b/klippy/extras/gcode_shell_command.py @@ -0,0 +1,96 @@ +# Run a shell command via gcode +# +# Copyright (C) 2019 Eric Callahan +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import os +import shlex +import subprocess +import logging + + +class ShellCommand: + def __init__(self, config): + self.name = config.get_name().split()[-1] + self.printer = config.get_printer() + self.gcode = self.printer.lookup_object("gcode") + cmd = config.get("command") + cmd = os.path.expanduser(cmd) + self.command = shlex.split(cmd) + self.timeout = config.getfloat("timeout", 2.0, above=0.0) + self.verbose = config.getboolean("verbose", True) + self.proc_fd = None + self.partial_output = "" + self.gcode.register_mux_command( + "RUN_SHELL_COMMAND", + "CMD", + self.name, + self.cmd_RUN_SHELL_COMMAND, + desc=self.cmd_RUN_SHELL_COMMAND_help, + ) + + def _process_output(self, eventime): + if self.proc_fd is None: + return + try: + data = os.read(self.proc_fd, 4096) + except Exception: + pass + data = self.partial_output + data.decode() + if "\n" not in data: + self.partial_output = data + return + elif data[-1] != "\n": + split = data.rfind("\n") + 1 + self.partial_output = data[split:] + data = data[:split] + else: + self.partial_output = "" + self.gcode.respond_info(data) + + cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command" + + def cmd_RUN_SHELL_COMMAND(self, params): + gcode_params = params.get("PARAMS", "") + gcode_params = shlex.split(gcode_params) + reactor = self.printer.get_reactor() + try: + proc = subprocess.Popen( + self.command + gcode_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + except Exception: + logging.exception( + "shell_command: Command {%s} failed" % (self.name) + ) + raise self.gcode.error("Error running command {%s}" % (self.name)) + if self.verbose: + self.proc_fd = proc.stdout.fileno() + self.gcode.respond_info("Running Command {%s}...:" % (self.name)) + hdl = reactor.register_fd(self.proc_fd, self._process_output) + eventtime = reactor.monotonic() + endtime = eventtime + self.timeout + complete = False + while eventtime < endtime: + eventtime = reactor.pause(eventtime + 0.05) + if proc.poll() is not None: + complete = True + break + if not complete: + proc.terminate() + if self.verbose: + if self.partial_output: + self.gcode.respond_info(self.partial_output) + self.partial_output = "" + if complete: + msg = "Command {%s} finished\n" % (self.name) + else: + msg = "Command {%s} timed out" % (self.name) + self.gcode.respond_info(msg) + reactor.unregister_fd(hdl) + self.proc_fd = None + + +def load_config_prefix(config): + return ShellCommand(config) diff --git a/test/klippy/gcode_shell_command.cfg b/test/klippy/gcode_shell_command.cfg new file mode 100644 index 000000000..5c6c15c52 --- /dev/null +++ b/test/klippy/gcode_shell_command.cfg @@ -0,0 +1,13 @@ +# Test case for gcode_shell_command +[mcu] +serial: /dev/ttyACM0 + +[printer] +kinematics: none +max_velocity: 300 +max_accel: 3000 + +[gcode_shell_command HELLO_WORLD] +command: echo hello world +timeout: 2. +verbose: True diff --git a/test/klippy/gcode_shell_command.test b/test/klippy/gcode_shell_command.test new file mode 100644 index 000000000..d564d01cd --- /dev/null +++ b/test/klippy/gcode_shell_command.test @@ -0,0 +1,5 @@ +# Test case for gcode_shell_command +CONFIG gcode_shell_command.cfg +DICTIONARY atmega2560.dict + +HELLO_WORLD