From f2691f33d30bfac26591361cdb2295741a401bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20W=C3=BCrthner?= <5859228+crysxd@users.noreply.github.com> Date: Sun, 31 Mar 2024 17:15:47 +0200 Subject: [PATCH] feat: add OctoApp support (#454) * Add OctoApp support * Update scripts/octoapp.sh Co-authored-by: dw-0 * Remove duplicate clone function * Update Readme link * Use "OctoApp for Klipper" in install menu --------- Co-authored-by: dw-0 --- README.md | 4 +- scripts/globals.sh | 4 + scripts/octoapp.sh | 369 +++++++++++++++++++++++++++++++++++++ scripts/ui/install_menu.sh | 9 +- scripts/ui/main_menu.sh | 1 + scripts/ui/remove_menu.sh | 3 + scripts/ui/update_menu.sh | 10 +- 7 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 scripts/octoapp.sh diff --git a/README.md b/README.md index 5928f67..8d8e727 100644 --- a/README.md +++ b/README.md @@ -154,18 +154,20 @@ prompt and confirm by hitting ENTER.

Mobileraker's Companion

OctoEverywhere For Klipper

+

OctoApp For Klipper

OctoEverywhere Logo OctoEverywhere Logo - +OctoApp Logo by Patrick Schmidt by Quinn Damerell +by Christian Würthner diff --git a/scripts/globals.sh b/scripts/globals.sh index 1c5fc4c..9504911 100644 --- a/scripts/globals.sh +++ b/scripts/globals.sh @@ -83,4 +83,8 @@ function set_globals() { MOBILERAKER_DIR="${HOME}/mobileraker_companion" MOBILERAKER_REPO="https://github.com/Clon1998/mobileraker_companion.git" + #=============== OCTOAPP ================# + OCTOAPP_ENV="${HOME}/octoapp-env" + OCTOAPP_DIR="${HOME}/octoapp" + OCTOAPP_REPO="https://github.com/crysxd/OctoApp-Plugin.git" } diff --git a/scripts/octoapp.sh b/scripts/octoapp.sh new file mode 100644 index 0000000..dea6918 --- /dev/null +++ b/scripts/octoapp.sh @@ -0,0 +1,369 @@ +#!/usr/bin/env bash + +#=======================================================================# +# Copyright (C) 2020 - 2024 Dominik Willner # +# # +# This file is part of KIAUH - Klipper Installation And Update Helper # +# https://github.com/dw-0/kiauh # +# # +# This file may be distributed under the terms of the GNU GPLv3 license # +#=======================================================================# + +# +# This file is written and maintained by Christian Würthner from OctoApp +# Please contact me if you need any help! +# hello@octoapp.eu +# + +set -e + +#===================================================# +#============== Install ============# +#===================================================# + +function octoapp_systemd() { + local services + services=$(find "${SYSTEMD}" -maxdepth 1 -regextype posix-extended -regex "${SYSTEMD}/octoapp(-[0-9a-zA-Z]+)?.service") + echo "${services}" +} + +function octoapp_setup_dialog() { + status_msg "Initializing OctoApp for Klipper installation ..." + + # First, check for moonraker service instances. + local moonraker_count + local moonraker_names + moonraker_count=$(moonraker_systemd | wc -w) + if (( moonraker_count == 0 )); then + ### return early if moonraker is not installed + local error="Moonraker not installed! Please install Moonraker first!" + log_error "OctoApp setup started without Moonraker being installed. Aborting setup." + print_error "${error}" && return + elif (( moonraker_count > 1 )); then + # moonraker_names is valid only in case of multi-instance + read -r -a moonraker_names <<< "$(get_multi_instance_names)" + fi + + # Next, check for any existing OctoApp services. + local octoapp_services + local existing_octoapp_count + octoapp_services=$(octoapp_systemd) + existing_octoapp_count=$(echo "${octoapp_services}" | wc -w ) + + # We need to make the moonraker instance count to the OctoApp service count. + local allowed_octoapp_count=$(( moonraker_count - existing_octoapp_count )) + if (( allowed_octoapp_count > 0 )); then + local new_octoapp_count + + ### Step 1: Ask for the number of OctoApp instances to install + if (( moonraker_count == 1 )); then + ok_msg "Moonraker installation found!\n" + new_octoapp_count=1 + elif (( moonraker_count > 1 )); then + top_border + printf "|${green}%-55s${white}|\n" " ${moonraker_count} Moonraker instances found!" + for name in "${moonraker_names[@]}"; do + printf "|${cyan}%-57s${white}|\n" " ● moonraker-${name}" + done + blank_line + if (( existing_octoapp_count > 0 )); then + printf "|${green}%-55s${white}|\n" " ${existing_octoapp_count} OctoApp instances already installed!" + for svc in ${octoapp_services}; do + printf "|${cyan}%-57s${white}|\n" " ● octoapp-$(get_instance_name "${svc}")" + done + fi + blank_line + echo -e "| The setup will apply the same names to OctoApp |" + blank_line + echo -e "| Please select the number of OctoApp instances to |" + echo -e "| install. Usually one OctoApp instance per Moonraker |" + echo -e "| instance is required, but you may not install more |" + echo -e "| OctoApp instances than available Moonraker instances. |" + bottom_border + + ### ask for amount of instances + local re="^[1-9][0-9]*$" + while [[ ! ${new_octoapp_count} =~ ${re} || ${new_octoapp_count} -gt ${allowed_octoapp_count} ]]; do + read -p "${cyan}###### Number of new OctoApp instances to set up:${white} " -i "${allowed_octoapp_count}" -e new_octoapp_count + ### break if input is valid + [[ ${new_octoapp_count} =~ ${re} && ${new_octoapp_count} -le ${allowed_octoapp_count} ]] && break + ### conditional error messages + [[ ! ${new_octoapp_count} =~ ${re} ]] && error_msg "Input not a number" + (( new_octoapp_count > allowed_octoapp_count )) && error_msg "Number of OctoApp instances larger than installed Moonraker instances" + done && select_msg "${new_octoapp_count}" + else + log_error "Internal error. moonraker_count of '${moonraker_count}' not equal or grater than one!" + return 1 + fi # (( moonraker_count == 1 )) + fi # (( allowed_octoapp_count > 0 )) + + # Special case for one moonraker instance with OctoApp already installed. + # If the user selects the install option again, they might be trying to recover the install + # or complete a printer link they didn't finish in the past. + # So in this case, we will allow them to run the install script again, since it's safe to run + # if the service is already installed, it will repair any missing issues. + if (( allowed_octoapp_count == 0 && moonraker_count == 1 )); then + local yn + while true; do + echo "${yellow}OctoApp is already installed.${white}" + echo "It is safe to run the install again to repair any issues or if the printer isn't linked, run the printer linking logic again." + echo "" + local question="Do you want to run the OctoApp recovery or linking logic again?" + read -p "${cyan}###### ${question} (Y/n):${white} " yn + case "${yn}" in + Y|y|Yes|yes|"") + select_msg "Yes" + break;; + N|n|No|no) + select_msg "No" + abort_msg "Exiting OctoApp setup ...\n" + return;; + *) + error_msg "Invalid Input!";; + esac + done + # The user responded yes, allow the install to run again. + allowed_octoapp_count=1 + fi + + # If there's something to install, do it! + if (( allowed_octoapp_count > 0 )); then + + (( new_octoapp_count > 1 )) && status_msg "Installing ${new_octoapp_count} OctoApp instances ..." + (( new_octoapp_count == 1 )) && status_msg "Installing OctoApp ..." + + # Ensure the basic system dependencies are installed. + local dep=(git dfu-util virtualenv python3 python3-pip python3-venv) + dependency_check "${dep[@]}" + + # Close the repo + clone_octoapp "${OCTOAPP_REPO}" + + # Call install with the correct args. + local instance_cfg_dirs + read -r -a instance_cfg_dirs <<< "$(get_instance_folder_path "config")" + echo instance_cfg_dirs[0] + + if (( moonraker_count == 1 )); then + "${OCTOAPP_DIR}/install.sh" "${instance_cfg_dirs[0]}/moonraker.conf" + elif (( moonraker_count > 1 )); then + local j=${existing_octoapp_count} + + for (( i=1; i <= new_octoapp_count; i++ )); do + "${OCTOAPP_DIR}/install.sh" "${instance_cfg_dirs[${j}]}/moonraker.conf" + j=$(( j + 1 )) + done && unset j + fi # (( moonraker_count == 1 )) + fi # (( allowed_octoapp_count > 0 )) +} + +function octoapp_install() { + "${OCTOAPP_DIR}/install.sh" "$@" +} + +#===================================================# +#============= Remove ==============# +#===================================================# + +function remove_octoapp_systemd() { + [[ -z $(octoapp_systemd) ]] && return + status_msg "Removing OctoApp Systemd Services ..." + + for service in $(octoapp_systemd | cut -d"/" -f5); do + status_msg "Removing ${service} ..." + sudo systemctl stop "${service}" + sudo systemctl disable "${service}" + sudo rm -f "${SYSTEMD}/${service}" + ok_msg "Done!" + done + + ### reloading units + sudo systemctl daemon-reload + sudo systemctl reset-failed + ok_msg "OctoApp Services removed!" +} + +function remove_octoapp_logs() { + local files regex="${HOME//\//\\/}\/([A-Za-z0-9_]+)\/logs\/octoapp(-[0-9a-zA-Z]+)?\.log(.*)?" + files=$(find "${HOME}" -maxdepth 3 -regextype posix-extended -regex "${regex}" | sort) + + if [[ -n ${files} ]]; then + for file in ${files}; do + status_msg "Removing ${file} ..." + rm -f "${file}" + ok_msg "${file} removed!" + done + fi +} + +function remove_octoapp_dir() { + [[ ! -d ${OCTOAPP_DIR} ]] && return + + status_msg "Removing OctoApp directory ..." + rm -rf "${OCTOAPP_DIR}" + ok_msg "Directory removed!" +} + +function remove_octoapp_config() { + # Remove the system config but not the main config, so the printer id doesn't get lost. + local files regex="${HOME//\//\\/}\/([A-Za-z0-9_]+)\/config\/octoapp-system(-[0-9a-zA-Z]+)?\.cfg(.*)?" + files=$(find "${HOME}" -maxdepth 4 -regextype posix-extended -regex "${regex}" | sort) + + if [[ -n ${files} ]]; then + for file in ${files}; do + status_msg "Removing ${file} ..." + rm -f "${file}" + ok_msg "${file} removed!" + done + fi +} + +function remove_octoapp_store_dir() { + local files regex="${HOME//\//\\/}\/([A-Za-z0-9_]+)\/octoapp-store" + files=$(find "${HOME}" -maxdepth 2 -type d -regextype posix-extended -regex "${regex}" | sort) + + if [[ -n ${files} ]]; then + for file in ${files}; do + status_msg "Removing ${file} ..." + rm -rf "${file}" + ok_msg "${file} removed!" + done + fi +} + +function remove_octoapp_env() { + [[ ! -d "${HOME}/octoapp-env" ]] && return + + status_msg "Removing octoapp-env directory ..." + rm -rf "${HOME}/octoapp-env" + ok_msg "Directory removed!" +} + +function remove_octoapp() +{ + remove_octoapp_systemd + remove_octoapp_logs + remove_octoapp_dir + remove_octoapp_env + remove_octoapp_config + remove_octoapp_store_dir + + print_confirm "OctoApp was successfully removed!" + return +} + +#===================================================# +#============= UPDATE ==============# +#===================================================# + +function update_octoapp() { + do_action_service "stop" "octoapp" + + if [[ ! -d ${OCTOAPP_DIR} ]]; then + clone_octoapp "${OCTOAPP_REPO}" + else + backup_before_update "octoapp" + status_msg "Updating OctoApp for Klipper ..." + cd "${OCTOAPP_DIR}" && git pull + ### read PKGLIST and install possible new dependencies + install_octoapp_dependencies + ### install possible new python dependencies + "${OCTOAPP_ENV}"/bin/pip install -r "${OCTOAPP_DIR}/requirements.txt" + fi + + ok_msg "Update complete!" + do_action_service "restart" "octoapp" +} + +function clone_octoapp() { + local repo=${1} + + status_msg "Cloning OctoApp from ${repo} ..." + + ### force remove existing octoapp dir and clone into fresh octoapp dir + [[ -d ${OCTOAPP_DIR} ]] && rm -rf "${OCTOAPP_DIR}" + + cd "${HOME}" || exit 1 + if ! git clone "${OCTOAPP_REPO}" "${OCTOAPP_DIR}"; then + print_error "Cloning OctoApp from\n ${repo}\n failed!" + exit 1 + fi +} + +function install_octoapp_dependencies() { + local packages log_name="OctoApp" + local install_script="${OCTOAPP_DIR}/install.sh" + + ### read PKGLIST from official install-script + status_msg "Reading dependencies..." + # shellcheck disable=SC2016 + packages="$(grep "PKGLIST=" "${install_script}" | cut -d'"' -f2 | sed 's/\${PKGLIST}//g' | tr -d '\n')" + + echo "${cyan}${packages}${white}" | tr '[:space:]' '\n' + read -r -a packages <<< "${packages}" + + ### Update system package lists if stale + update_system_package_lists + + ### Install required packages + install_system_packages "${log_name}" "packages[@]" +} + +#===================================================# +#============= STATUS ==============# +#===================================================# + +function get_octoapp_status() { + local status + local service_count + local octoapp_services + + octoapp_services=$(octoapp_systemd) + service_count=$(echo "${octoapp_services}" | wc -w ) + + if (( service_count == 0 )); then + status="Not installed!" + elif [[ ! -d "${OCTOAPP_DIR}" ]]; then + status="Incomplete!" + else + status="Installed!" + fi + + echo "${status}" +} + +function get_local_octoapp_commit() { + [[ ! -d ${OCTOAPP_DIR} || ! -d "${OCTOAPP_DIR}/.git" ]] && return + + local commit + cd "${OCTOAPP_DIR}" + commit="$(git describe HEAD --always --tags | cut -d "-" -f 1,2)" + echo "${commit}" +} + +function get_remote_octoapp_commit() { + [[ ! -d ${OCTOAPP_DIR} || ! -d "${OCTOAPP_DIR}/.git" ]] && return + + local commit + cd "${OCTOAPP_DIR}" && git fetch origin -q + commit=$(git describe origin/release --always --tags | cut -d "-" -f 1,2) + echo "${commit}" +} + +function compare_octoapp_versions() { + local versions local_ver remote_ver + local_ver="$(get_local_octoapp_commit)" + remote_ver="$(get_remote_octoapp_commit)" + + if [[ ${local_ver} != "${remote_ver}" ]]; then + versions="${yellow}$(printf " %-14s" "${local_ver}")${white}" + versions+="|${green}$(printf " %-13s" "${remote_ver}")${white}" + # Add us to the update file, so if the user selects "update all" it includes us. + add_to_application_updates "octoapp" + else + versions="${green}$(printf " %-14s" "${local_ver}")${white}" + versions+="|${green}$(printf " %-13s" "${remote_ver}")${white}" + fi + + echo "${versions}" +} diff --git a/scripts/ui/install_menu.sh b/scripts/ui/install_menu.sh index d318d8d..8ed0410 100755 --- a/scripts/ui/install_menu.sh +++ b/scripts/ui/install_menu.sh @@ -28,9 +28,10 @@ function install_ui() { echo -e "| 4) [Fluidd] | 9) $(obico_install_title) |" echo -e "| | 10) [OctoEverywhere] |" echo -e "| | 11) [Mobileraker] |" - echo -e "| Touchscreen GUI: | |" - echo -e "| 5) [KlipperScreen] | Webcam Streamer: |" - echo -e "| | 12) [Crowsnest] |" + echo -e "| Touchscreen GUI: | 12) [OctoApp for Klipper] |" + echo -e "| 5) [KlipperScreen] | |" + echo -e "| | Webcam Streamer: |" + echo -e "| | 13) [Crowsnest] |" back_footer } @@ -72,6 +73,8 @@ function install_menu() { 11) do_action "install_mobileraker" "install_ui";; 12) + do_action "octoapp_setup_dialog" "install_ui";; + 13) do_action "install_crowsnest" "install_ui";; B|b) clear; main_menu; break;; diff --git a/scripts/ui/main_menu.sh b/scripts/ui/main_menu.sh index e521ba3..f950f27 100755 --- a/scripts/ui/main_menu.sh +++ b/scripts/ui/main_menu.sh @@ -28,6 +28,7 @@ function main_ui() { echo -e "| | Obico: $(print_status "moonraker_obico")|" echo -e "| | OctoEverywhere: $(print_status "octoeverywhere")|" echo -e "| | Mobileraker: $(print_status "mobileraker")|" + echo -e "| | OctoApp: $(print_status "octoapp")|" echo -e "| | |" echo -e "| | Octoprint: $(print_status "octoprint")|" hr diff --git a/scripts/ui/remove_menu.sh b/scripts/ui/remove_menu.sh index ef111b0..f9824b1 100755 --- a/scripts/ui/remove_menu.sh +++ b/scripts/ui/remove_menu.sh @@ -31,6 +31,7 @@ function remove_ui() { echo -e "| 7) [KlipperScreen] | 14) [OctoEverywhere] |" echo -e "| | 15) [Mobileraker] |" echo -e "| | 16) [NGINX] |" + echo -e "| | 17) [OctoApp] |" back_footer } @@ -73,6 +74,8 @@ function remove_menu() { do_action "remove_mobileraker" "remove_ui";; 16) do_action "remove_nginx" "remove_ui";; + 17) + do_action "remove_octoapp" "remove_ui";; B|b) clear; main_menu; break;; *) diff --git a/scripts/ui/update_menu.sh b/scripts/ui/update_menu.sh index cf2bbdc..26a3f11 100755 --- a/scripts/ui/update_menu.sh +++ b/scripts/ui/update_menu.sh @@ -35,8 +35,9 @@ function update_ui() { echo -e "| 9) [OctoEverywhere] |$(compare_octoeverywhere_versions)|" echo -e "| 10) [Mobileraker] |$(compare_mobileraker_versions)|" echo -e "| 11) [Crowsnest] |$(compare_crowsnest_versions)|" + echo -e "| 12) [OctoApp] |$(compare_octoapp_versions)|" echo -e "| |------------------------------|" - echo -e "| 12) [System] | $(check_system_updates) |" + echo -e "| 13) [System] | $(check_system_updates) |" back_footer } @@ -73,6 +74,8 @@ function update_menu() { 11) do_action "update_crowsnest" "update_ui";; 12) + do_action "update_octoapp" "update_ui";; + 13) do_action "upgrade_system_packages" "update_ui";; a) do_action "update_all" "update_ui";; @@ -128,7 +131,10 @@ function update_all() { echo -e "| ${cyan}● OctoEverywhere${white} |" [[ "${update_arr[*]}" =~ "mobileraker" ]] && \ - echo -e "| ${cyan}● Mobileraker${white} |" + echo -e "| ${cyan}● Mobileraker${white} |" + + [[ "${update_arr[*]}" =~ "octoapp" ]] && \ + echo -e "| ${cyan}● OctoApp${white} |" [[ "${update_arr[*]}" =~ "system" ]] && \ echo -e "| ${cyan}● System${white} |"