mirror of
https://github.com/dw-0/kiauh.git
synced 2025-12-14 02:54:28 +05:00
new function: install shell command extension by Arksine
This commit is contained in:
46
docs/Shell Command Extension.md
Normal file
46
docs/Shell Command Extension.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# 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.
|
||||||
|
# [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:
|
||||||
|
|
||||||
|
```
|
||||||
|
[shell_command hello_world]
|
||||||
|
command: echo hello world
|
||||||
|
timeout: 2.
|
||||||
|
verbose: True
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute with:
|
||||||
|
`RUN_SHELL_COMMAND CMD=hello_world`
|
||||||
|
|
||||||
|
## 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!
|
||||||
83
resources/shell_command.py
Executable file
83
resources/shell_command.py
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
# Run a shell command via gcode
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
#
|
||||||
|
# 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., above=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
|
||||||
|
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]
|
||||||
|
self.gcode.respond_info(data)
|
||||||
|
|
||||||
|
cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command"
|
||||||
|
def cmd_RUN_SHELL_COMMAND(self, params):
|
||||||
|
reactor = self.printer.get_reactor()
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
self.command, 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 + .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)
|
||||||
@@ -331,3 +331,76 @@ remove_branding(){
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_extension_shell_command(){
|
||||||
|
echo
|
||||||
|
top_border
|
||||||
|
echo -e "| You are about to install the shell command extension. |"
|
||||||
|
echo -e "| Please make sure to read the instructions before you |"
|
||||||
|
echo -e "| continue and remember that there are potential risks! |"
|
||||||
|
bottom_border
|
||||||
|
while true; do
|
||||||
|
read -p "${cyan}###### Do you want to continue? (Y/n):${default} " yn
|
||||||
|
case "$yn" in
|
||||||
|
Y|y|Yes|yes|"")
|
||||||
|
if [ -d $KLIPPER_DIR/klippy/extras ] && [ ! -f $KLIPPER_DIR/klippy/extras/shell_command.py ] ; then
|
||||||
|
status_msg "Installing shell command extension ..."
|
||||||
|
stop_klipper
|
||||||
|
cp ${HOME}/kiauh/resources/shell_command.py $KLIPPER_DIR/klippy/extras
|
||||||
|
status_msg "Creating example macro ..."
|
||||||
|
create_shell_command_example
|
||||||
|
ok_msg "Example macro created!"
|
||||||
|
ok_msg "Shell command extension installed!"
|
||||||
|
restart_klipper
|
||||||
|
else
|
||||||
|
if [ ! -d $KLIPPER_DIR/klippy/extras ]; then
|
||||||
|
ERROR_MSG="Folder ~/klipper/klippy/extras not found!"
|
||||||
|
fi
|
||||||
|
if [ -f $KLIPPER_DIR/klippy/extras/shell_command.py ]; then
|
||||||
|
ERROR_MSG="Extension already installed!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
break;;
|
||||||
|
N|n|No|no)
|
||||||
|
break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
create_shell_command_example(){
|
||||||
|
unset SC_ENTRY
|
||||||
|
unset write_entries
|
||||||
|
#check for a SAVE_CONFIG entry
|
||||||
|
SC="#*# <---------------------- SAVE_CONFIG ---------------------->"
|
||||||
|
if [[ $(grep "$SC" ${HOME}/printer.cfg) ]]; then
|
||||||
|
SC_LINE=$(grep -n "$SC" $PRINTER_CFG | cut -d ":" -f1)
|
||||||
|
PRE_SC_LINE=$(expr $SC_LINE - 1)
|
||||||
|
SC_ENTRY="true"
|
||||||
|
else
|
||||||
|
SC_ENTRY="false"
|
||||||
|
fi
|
||||||
|
#example shell command
|
||||||
|
write_entries+=("[shell_command hello_world]\ncommand: echo hello world\ntimeout: 2.\nverbose: True")
|
||||||
|
#example macro
|
||||||
|
write_entries+=("[gcode_macro HELLO_WORLD]\ngcode:\n RUN_SHELL_COMMAND CMD=hello_world")
|
||||||
|
if [ "${#write_entries[@]}" != "0" ]; then
|
||||||
|
write_entries+=("\\\n############################\n##### CREATED BY KIAUH #####\n############################")
|
||||||
|
write_entries=("############################\n" "${write_entries[@]}")
|
||||||
|
fi
|
||||||
|
#execute writing
|
||||||
|
status_msg "Writing to printer.cfg ..."
|
||||||
|
if [ "$SC_ENTRY" = "true" ]; then
|
||||||
|
PRE_SC_LINE="$(expr $SC_LINE - 1)a"
|
||||||
|
for entry in "${write_entries[@]}"
|
||||||
|
do
|
||||||
|
sed -i "$PRE_SC_LINE $entry" $PRINTER_CFG
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [ "$SC_ENTRY" = "false" ]; then
|
||||||
|
LINE_COUNT="$(wc -l < $PRINTER_CFG)a"
|
||||||
|
for entry in "${write_entries[@]}"
|
||||||
|
do
|
||||||
|
sed -i "$LINE_COUNT $entry" $PRINTER_CFG
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user