mirror of
https://github.com/Tengo10/fabaccess_klipper.git
synced 2025-03-12 15:01:43 +01:00
init
This commit is contained in:
parent
3107a35b55
commit
fd5cfcc052
56
fabaccess_klipper.py
Normal file
56
fabaccess_klipper.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class FabCon:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = printer = config.get_printer()
|
||||||
|
self.api_ip = config.get('ip')
|
||||||
|
self.api_key = config.get('key', '')
|
||||||
|
self.machine = config.get('machine')
|
||||||
|
self.in_use = False
|
||||||
|
|
||||||
|
|
||||||
|
self.printer.register_event_handler("action:cancel",
|
||||||
|
self._handle_free)
|
||||||
|
self.printer.register_event_handler("action:start",
|
||||||
|
self._handle_check)
|
||||||
|
self.printer.register_event_handler("action:complete",
|
||||||
|
self._handle_free)
|
||||||
|
self.printer.register_event_handler("klippy:ready",
|
||||||
|
self._handle_startup)
|
||||||
|
|
||||||
|
def _handle_check(self):
|
||||||
|
logging.info("FABACCESS CHECK")
|
||||||
|
try:
|
||||||
|
req = requests.get("http://" + self.api_ip + "/in_use/" + self.machine, timeout=1)
|
||||||
|
if req.text == "in_use":
|
||||||
|
logging.info("FABACCESS Machine in Use, Proceeding")
|
||||||
|
self.in_use = True
|
||||||
|
elif req.text == "free":
|
||||||
|
logging.info("FABACCESS Machine not registered to User, Canceling")
|
||||||
|
self.printer.invoke_shutdown("FABACCESS Machine not registered to User")
|
||||||
|
except:
|
||||||
|
logging.warning("FABACCESS ERROR")
|
||||||
|
|
||||||
|
def _handle_free(self):
|
||||||
|
logging.info("FABACCESS FREE")
|
||||||
|
try:
|
||||||
|
req = requests.get("http://" + self.api_ip + "/free/" + self.machine, timeout=1)
|
||||||
|
logging.info("FABACCESS Machine freed")
|
||||||
|
self.in_use = False
|
||||||
|
except:
|
||||||
|
logging.warning("FABACCESS ERROR")
|
||||||
|
|
||||||
|
def _handle_startup(self):
|
||||||
|
logging.info("FABACCESS LOADED")
|
||||||
|
|
||||||
|
def get_status(self, eventtime):
|
||||||
|
data = {
|
||||||
|
'in_use': self.in_use
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return FabCon(config)
|
178
install.sh
Executable file
178
install.sh
Executable file
@ -0,0 +1,178 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
# Copied from the install.sh script the klipper_z_calibration project by protoloft
|
||||||
|
# https://github.com/protoloft/klipper_z_calibration/tree/master
|
||||||
|
|
||||||
|
|
||||||
|
SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/ && pwd )"
|
||||||
|
KLIPPER_PATH="${HOME}/klipper"
|
||||||
|
SYSTEMDDIR="/etc/systemd/system"
|
||||||
|
MOONRAKER_CONFIG="${HOME}/printer_data/config/moonraker.conf"
|
||||||
|
MOONRAKER_FALLBACK="${HOME}/klipper_config/moonraker.conf"
|
||||||
|
NUM_INSTALLS=0
|
||||||
|
|
||||||
|
# Force script to exit if an error occurs
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Step 1: Check for root user
|
||||||
|
verify_ready()
|
||||||
|
{
|
||||||
|
# check for root user
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
echo "This script must not run as root"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
# output used number of installs
|
||||||
|
if [[ $NUM_INSTALLS == 0 ]]; then
|
||||||
|
echo "Defaulted to one klipper install, if more than one instance, use -n"
|
||||||
|
else
|
||||||
|
echo "Number of Installs Selected: $NUM_INSTALLS"
|
||||||
|
fi
|
||||||
|
# Fall back to old config
|
||||||
|
if [ ! -f "$MOONRAKER_CONFIG" ]; then
|
||||||
|
echo "${MOONRAKER_CONFIG} does not exist. Falling back to ${MOONRAKER_FALLBACK}"
|
||||||
|
MOONRAKER_CONFIG="$MOONRAKER_FALLBACK"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 2: Verify Klipper has been installed
|
||||||
|
check_klipper()
|
||||||
|
{
|
||||||
|
if [[ $NUM_INSTALLS == 0 ]]; then
|
||||||
|
if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper.service")" ]; then
|
||||||
|
echo "Klipper service found!"
|
||||||
|
else
|
||||||
|
echo "Klipper service not found, please install Klipper first"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
for (( klip = 1; klip<=$NUM_INSTALLS; klip++ )); do
|
||||||
|
if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper-$klip.service")" ]; then
|
||||||
|
echo "klipper-$klip.service found!"
|
||||||
|
else
|
||||||
|
echo "klipper-$klip.service NOT found, please ensure you've entered the correct number of klipper instances you're running!"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 3: Check folders
|
||||||
|
check_requirements()
|
||||||
|
{
|
||||||
|
if [ ! -d "${KLIPPER_PATH}/klippy/extras/" ]; then
|
||||||
|
echo "Error: Klipper not found in directory: ${KLIPPER_PATH}. Exiting.."
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
echo "Klipper found at ${KLIPPER_PATH}"
|
||||||
|
|
||||||
|
if [ ! -f "$MOONRAKER_CONFIG" ]; then
|
||||||
|
echo "Error: Moonraker configuration not found: ${MOONRAKER_CONFIG}. Exiting.."
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
echo "Moonraker configuration found at ${MOONRAKER_CONFIG}"
|
||||||
|
|
||||||
|
apt install python3-requests
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 4: Link extension to Klipper
|
||||||
|
link_extension()
|
||||||
|
{
|
||||||
|
|
||||||
|
echo -n "Linking extension to Klipper... "
|
||||||
|
ln -sf "${SRCDIR}/fabaccess_klipper.py" "${KLIPPER_PATH}/klippy/extras/fabaccess_klipper.py"
|
||||||
|
mv "${KLIPPER_PATH}/klippy/extras/print_stats.py" "${KLIPPER_PATH}/klippy/extras/print_stats.py.bak"
|
||||||
|
ln -sf "${SRCDIR}/print_stats.py" "${KLIPPER_PATH}/klippy/extras/print_stats.py"
|
||||||
|
echo "[OK]"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Step 6: Add updater to moonraker.conf
|
||||||
|
add_updater()
|
||||||
|
{
|
||||||
|
echo -n "Adding update manager to moonraker.conf... "
|
||||||
|
update_section=$(grep -c '\[update_manager[a-z ]* fabaccess_klipper\]' $MOONRAKER_CONFIG || true)
|
||||||
|
if [ "$update_section" -eq 0 ]; then
|
||||||
|
echo -e "\n[update_manager fabaccess_klipper]" >> "$MOONRAKER_CONFIG"
|
||||||
|
echo "type: git_repo" >> "$MOONRAKER_CONFIG"
|
||||||
|
echo "path: ${SRCDIR}" >> "$MOONRAKER_CONFIG"
|
||||||
|
echo "origin: https://github.com/Tengo10/fabaccess_klipper.git" >> "$MOONRAKER_CONFIG"
|
||||||
|
echo "managed_services: klipper" >> "$MOONRAKER_CONFIG"
|
||||||
|
echo -e "\n" >> "$MOONRAKER_CONFIG"
|
||||||
|
echo "[OK]"
|
||||||
|
|
||||||
|
echo -n "Restarting Moonraker... "
|
||||||
|
sudo systemctl restart moonraker
|
||||||
|
echo "[OK]"
|
||||||
|
else
|
||||||
|
echo "[SKIPPED]"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 7: Restarting Klipper
|
||||||
|
restart_klipper()
|
||||||
|
{
|
||||||
|
if [[ $NUM_INSTALLS == 0 ]]; then
|
||||||
|
echo -n "Restarting Klipper... "
|
||||||
|
sudo systemctl restart klipper
|
||||||
|
echo "[OK]"
|
||||||
|
else
|
||||||
|
for (( klip = 1; klip<=$NUM_INSTALLS; klip++)); do
|
||||||
|
echo -n "Restarting Klipper-$klip... "
|
||||||
|
sudo systemctl restart klipper-$klip
|
||||||
|
echo "[OK]"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uinstall()
|
||||||
|
{
|
||||||
|
if [ -f "${KLIPPER_PATH}/klippy/extras/fabaccess_klipper.py" ]; then
|
||||||
|
echo -n "Uninstalling fabaccess_klipper... "
|
||||||
|
rm -f "${KLIPPER_PATH}/klippy/extras/fabaccess_klipper.py"
|
||||||
|
rm -f "${KLIPPER_PATH}/klippy/extras/fabaccess_klipper.pyc"
|
||||||
|
echo "[OK]"
|
||||||
|
echo "You can now remove the \"[update_manager fabaccess_klipper]\" section in your moonraker.conf and delete this directory."
|
||||||
|
echo "You also need to remove the \"[fabaccess_klipper]\" section in your Klipper configuration..."
|
||||||
|
else
|
||||||
|
echo -n "${KLIPPER_PATH}/klippy/extras/fabaccess_klipper.py not found. Is it installed? "
|
||||||
|
echo "[FAILED]"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
echo "Usage: $(basename $0) [-k <Klipper path>] [-m <Moonraker config file>] [-n <number klipper instances>] [-u]" 1>&2;
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Command parsing
|
||||||
|
while getopts ":k:m:n:uh" OPTION; do
|
||||||
|
case "$OPTION" in
|
||||||
|
k) KLIPPER_PATH="$OPTARG" ;;
|
||||||
|
m) MOONRAKER_CONFIG="$OPTARG" ;;
|
||||||
|
n) NUM_INSTALLS="$OPTARG" ;;
|
||||||
|
u) UNINSTALL=1 ;;
|
||||||
|
h | ?) usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Fall back to old config
|
||||||
|
if [ ! -f "$MOONRAKER_CONFIG" ]; then
|
||||||
|
echo "${MOONRAKER_CONFIG} does not exist. Falling back to ${MOONRAKER_FALLBACK}"
|
||||||
|
MOONRAKER_CONFIG="$MOONRAKER_FALLBACK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run steps
|
||||||
|
verify_ready
|
||||||
|
check_klipper
|
||||||
|
check_requirements
|
||||||
|
remove_service
|
||||||
|
if [ ! $UNINSTALL ]; then
|
||||||
|
link_extension
|
||||||
|
add_updater
|
||||||
|
else
|
||||||
|
uinstall
|
||||||
|
fi
|
||||||
|
restart_klipper
|
128
print_stats.py
Normal file
128
print_stats.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# Virtual SDCard print stat tracking
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
|
||||||
|
# Modified to send events action:start, action:complete, action:cancel
|
||||||
|
|
||||||
|
|
||||||
|
class PrintStats:
|
||||||
|
def __init__(self, config):
|
||||||
|
printer = config.get_printer()
|
||||||
|
self.printer = printer
|
||||||
|
self.gcode_move = printer.load_object(config, 'gcode_move')
|
||||||
|
self.reactor = printer.get_reactor()
|
||||||
|
self.reset()
|
||||||
|
# Register commands
|
||||||
|
self.gcode = printer.lookup_object('gcode')
|
||||||
|
self.gcode.register_command(
|
||||||
|
"SET_PRINT_STATS_INFO", self.cmd_SET_PRINT_STATS_INFO,
|
||||||
|
desc=self.cmd_SET_PRINT_STATS_INFO_help)
|
||||||
|
def _update_filament_usage(self, eventtime):
|
||||||
|
gc_status = self.gcode_move.get_status(eventtime)
|
||||||
|
cur_epos = gc_status['position'].e
|
||||||
|
self.filament_used += (cur_epos - self.last_epos) \
|
||||||
|
/ gc_status['extrude_factor']
|
||||||
|
self.last_epos = cur_epos
|
||||||
|
def set_current_file(self, filename):
|
||||||
|
self.reset()
|
||||||
|
self.filename = filename
|
||||||
|
def note_start(self):
|
||||||
|
curtime = self.reactor.monotonic()
|
||||||
|
if self.print_start_time is None:
|
||||||
|
self.print_start_time = curtime
|
||||||
|
elif self.last_pause_time is not None:
|
||||||
|
# Update pause time duration
|
||||||
|
pause_duration = curtime - self.last_pause_time
|
||||||
|
self.prev_pause_duration += pause_duration
|
||||||
|
self.last_pause_time = None
|
||||||
|
# Reset last e-position
|
||||||
|
gc_status = self.gcode_move.get_status(curtime)
|
||||||
|
self.last_epos = gc_status['position'].e
|
||||||
|
self.printer.send_event("action:start")
|
||||||
|
self.state = "printing"
|
||||||
|
self.error_message = ""
|
||||||
|
def note_pause(self):
|
||||||
|
if self.last_pause_time is None:
|
||||||
|
curtime = self.reactor.monotonic()
|
||||||
|
self.last_pause_time = curtime
|
||||||
|
# update filament usage
|
||||||
|
self._update_filament_usage(curtime)
|
||||||
|
if self.state != "error":
|
||||||
|
self.state = "paused"
|
||||||
|
def note_complete(self):
|
||||||
|
self.printer.send_event("action:complete")
|
||||||
|
self._note_finish("complete")
|
||||||
|
def note_error(self, message):
|
||||||
|
self._note_finish("error", message)
|
||||||
|
def note_cancel(self):
|
||||||
|
self.printer.send_event("action:cancel")
|
||||||
|
self._note_finish("cancelled")
|
||||||
|
def _note_finish(self, state, error_message = ""):
|
||||||
|
if self.print_start_time is None:
|
||||||
|
return
|
||||||
|
self.state = state
|
||||||
|
self.error_message = error_message
|
||||||
|
eventtime = self.reactor.monotonic()
|
||||||
|
self.total_duration = eventtime - self.print_start_time
|
||||||
|
if self.filament_used < 0.0000001:
|
||||||
|
# No positive extusion detected during print
|
||||||
|
self.init_duration = self.total_duration - \
|
||||||
|
self.prev_pause_duration
|
||||||
|
self.print_start_time = None
|
||||||
|
cmd_SET_PRINT_STATS_INFO_help = "Pass slicer info like layer act and " \
|
||||||
|
"total to klipper"
|
||||||
|
def cmd_SET_PRINT_STATS_INFO(self, gcmd):
|
||||||
|
total_layer = gcmd.get_int("TOTAL_LAYER", self.info_total_layer, \
|
||||||
|
minval=0)
|
||||||
|
current_layer = gcmd.get_int("CURRENT_LAYER", self.info_current_layer, \
|
||||||
|
minval=0)
|
||||||
|
if total_layer == 0:
|
||||||
|
self.info_total_layer = None
|
||||||
|
self.info_current_layer = None
|
||||||
|
elif total_layer != self.info_total_layer:
|
||||||
|
self.info_total_layer = total_layer
|
||||||
|
self.info_current_layer = 0
|
||||||
|
|
||||||
|
if self.info_total_layer is not None and \
|
||||||
|
current_layer is not None and \
|
||||||
|
current_layer != self.info_current_layer:
|
||||||
|
self.info_current_layer = min(current_layer, self.info_total_layer)
|
||||||
|
def reset(self):
|
||||||
|
self.filename = self.error_message = ""
|
||||||
|
self.state = "standby"
|
||||||
|
self.prev_pause_duration = self.last_epos = 0.
|
||||||
|
self.filament_used = self.total_duration = 0.
|
||||||
|
self.print_start_time = self.last_pause_time = None
|
||||||
|
self.init_duration = 0.
|
||||||
|
self.info_total_layer = None
|
||||||
|
self.info_current_layer = None
|
||||||
|
def get_status(self, eventtime):
|
||||||
|
time_paused = self.prev_pause_duration
|
||||||
|
if self.print_start_time is not None:
|
||||||
|
if self.last_pause_time is not None:
|
||||||
|
# Calculate the total time spent paused during the print
|
||||||
|
time_paused += eventtime - self.last_pause_time
|
||||||
|
else:
|
||||||
|
# Accumulate filament if not paused
|
||||||
|
self._update_filament_usage(eventtime)
|
||||||
|
self.total_duration = eventtime - self.print_start_time
|
||||||
|
if self.filament_used < 0.0000001:
|
||||||
|
# Track duration prior to extrusion
|
||||||
|
self.init_duration = self.total_duration - time_paused
|
||||||
|
print_duration = self.total_duration - self.init_duration - time_paused
|
||||||
|
return {
|
||||||
|
'filename': self.filename,
|
||||||
|
'total_duration': self.total_duration,
|
||||||
|
'print_duration': print_duration,
|
||||||
|
'filament_used': self.filament_used,
|
||||||
|
'state': self.state,
|
||||||
|
'message': self.error_message,
|
||||||
|
'info': {'total_layer': self.info_total_layer,
|
||||||
|
'current_layer': self.info_current_layer}
|
||||||
|
}
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return PrintStats(config)
|
Loading…
x
Reference in New Issue
Block a user