From cd8003add9680e20601e74c2debad1e5837fd034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20W=C3=BCrthner?= <5859228+crysxd@users.noreply.github.com> Date: Thu, 3 Oct 2024 08:51:38 +0200 Subject: [PATCH] feat(extension): add OctoApp (#554) * Add OctoApp to v6 * fix: set correct index to new extension Signed-off-by: Dominik Willner --------- Signed-off-by: Dominik Willner Co-authored-by: dw-0 --- kiauh/extensions/octoapp/__init__.py | 29 +++ kiauh/extensions/octoapp/metadata.json | 17 ++ kiauh/extensions/octoapp/octoapp.py | 75 +++++++ kiauh/extensions/octoapp/octoapp_extension.py | 191 ++++++++++++++++++ kiauh/utils/instance_type.py | 2 + 5 files changed, 314 insertions(+) create mode 100644 kiauh/extensions/octoapp/__init__.py create mode 100644 kiauh/extensions/octoapp/metadata.json create mode 100644 kiauh/extensions/octoapp/octoapp.py create mode 100644 kiauh/extensions/octoapp/octoapp_extension.py diff --git a/kiauh/extensions/octoapp/__init__.py b/kiauh/extensions/octoapp/__init__.py new file mode 100644 index 0000000..eb76fe4 --- /dev/null +++ b/kiauh/extensions/octoapp/__init__.py @@ -0,0 +1,29 @@ +# ======================================================================= # +# 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 # +# ======================================================================= # +from pathlib import Path + +# repo +OA_REPO = "https://github.com/crysxd/OctoApp-Plugin.git" + +# directories +OA_DIR = Path.home().joinpath("octoapp") +OA_ENV_DIR = Path.home().joinpath("octoapp-env") +OA_STORE_DIR = OA_DIR.joinpath("octoapp-store") + +# files +OA_REQ_FILE = OA_DIR.joinpath("requirements.txt") +OA_DEPS_JSON_FILE = OA_DIR.joinpath("moonraker-system-dependencies.json") +OA_INSTALL_SCRIPT = OA_DIR.joinpath("install.sh") +OA_UPDATE_SCRIPT = OA_DIR.joinpath("update.sh") +OA_INSTALLER_LOG_FILE = Path.home().joinpath("octoapp-installer.log") + +# filenames +OA_CFG_NAME = "octoapp.conf" +OA_LOG_NAME = "octoapp.log" +OA_SYS_CFG_NAME = "octoapp-system.cfg" diff --git a/kiauh/extensions/octoapp/metadata.json b/kiauh/extensions/octoapp/metadata.json new file mode 100644 index 0000000..4660ab8 --- /dev/null +++ b/kiauh/extensions/octoapp/metadata.json @@ -0,0 +1,17 @@ +{ + "metadata": { + "index": 9, + "module": "octoapp_extension", + "maintained_by": "crysxd", + "display_name": "OctoApp for Klipper", + "description": [ + "Your favorite 3D printing app for iOS & Android", + "- Print notifications on your phone & watch", + "- Control and start prints from your phone", + "- Live webcam view", + "- Live Gcode preview", + "- And much much more!" + ], + "updates": true + } +} diff --git a/kiauh/extensions/octoapp/octoapp.py b/kiauh/extensions/octoapp/octoapp.py new file mode 100644 index 0000000..d5c663d --- /dev/null +++ b/kiauh/extensions/octoapp/octoapp.py @@ -0,0 +1,75 @@ +# ======================================================================= # +# 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 # +# ======================================================================= # +from __future__ import annotations + +from dataclasses import dataclass, field +from pathlib import Path +from subprocess import CalledProcessError, run + +from components.moonraker import MOONRAKER_CFG_NAME +from components.moonraker.moonraker import Moonraker +from core.instance_manager.base_instance import BaseInstance +from core.logger import Logger +from extensions.octoapp import ( + OA_CFG_NAME, + OA_DIR, + OA_ENV_DIR, + OA_INSTALL_SCRIPT, + OA_LOG_NAME, + OA_SYS_CFG_NAME, + OA_UPDATE_SCRIPT, +) +from utils.sys_utils import get_service_file_path + + +@dataclass +class Octoapp: + suffix: str + base: BaseInstance = field(init=False, repr=False) + service_file_path: Path = field(init=False) + log_file_name = OA_LOG_NAME + dir: Path = OA_DIR + env_dir: Path = OA_ENV_DIR + data_dir: Path = field(init=False) + store_dir: Path = field(init=False) + cfg_file: Path = field(init=False) + sys_cfg_file: Path = field(init=False) + + def __post_init__(self): + self.base: BaseInstance = BaseInstance(Moonraker, self.suffix) + self.base.log_file_name = self.log_file_name + + self.service_file_path: Path = get_service_file_path( + Octoapp, self.suffix + ) + self.store_dir = self.base.data_dir.joinpath("store") + self.cfg_file = self.base.cfg_dir.joinpath(OA_CFG_NAME) + self.sys_cfg_file = self.base.cfg_dir.joinpath(OA_SYS_CFG_NAME) + self.data_dir = self.base.data_dir + self.sys_cfg_file = self.base.cfg_dir.joinpath(OA_SYS_CFG_NAME) + + def create(self) -> None: + Logger.print_status("Creating OctoApp for Klipper Instance ...") + + try: + cmd = f"{OA_INSTALL_SCRIPT} {self.base.cfg_dir}/{MOONRAKER_CFG_NAME}" + run(cmd, check=True, shell=True) + + except CalledProcessError as e: + Logger.print_error(f"Error creating instance: {e}") + raise + + @staticmethod + def update() -> None: + try: + run(OA_UPDATE_SCRIPT.as_posix(), check=True, shell=True, cwd=OA_DIR) + + except CalledProcessError as e: + Logger.print_error(f"Error updating OctoApp for Klipper: {e}") + raise diff --git a/kiauh/extensions/octoapp/octoapp_extension.py b/kiauh/extensions/octoapp/octoapp_extension.py new file mode 100644 index 0000000..c5d293d --- /dev/null +++ b/kiauh/extensions/octoapp/octoapp_extension.py @@ -0,0 +1,191 @@ +# ======================================================================= # +# 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 # +# ======================================================================= # +import json +from typing import List + +from components.moonraker.moonraker import Moonraker +from core.instance_manager.instance_manager import InstanceManager +from core.logger import DialogType, Logger +from extensions.base_extension import BaseExtension +from extensions.octoapp import ( + OA_DEPS_JSON_FILE, + OA_DIR, + OA_ENV_DIR, + OA_INSTALL_SCRIPT, + OA_INSTALLER_LOG_FILE, + OA_REPO, + OA_REQ_FILE, + OA_SYS_CFG_NAME, +) +from extensions.octoapp.octoapp import Octoapp +from utils.common import ( + check_install_dependencies, + moonraker_exists, +) +from utils.config_utils import ( + remove_config_section, +) +from utils.fs_utils import run_remove_routines +from utils.git_utils import git_clone_wrapper +from utils.input_utils import get_confirm +from utils.instance_utils import get_instances +from utils.sys_utils import ( + install_python_requirements, + parse_packages_from_file, +) + + +# noinspection PyMethodMayBeStatic +class OctoappExtension(BaseExtension): + def install_extension(self, **kwargs) -> None: + Logger.print_status("Installing OctoApp for Klipper ...") + + # check if moonraker is installed. if not, notify the user and exit + if not moonraker_exists(): + return + + force_clone = False + OA_instances: List[Octoapp] = get_instances(Octoapp) + if OA_instances: + Logger.print_dialog( + DialogType.INFO, + [ + "OctoApp is already installed!", + "It is safe to run the installer again to link your " + "printer or repair any issues.", + ], + ) + if not get_confirm("Re-run OctoApp installation?"): + Logger.print_info("Exiting OctoApp for Klipper installation ...") + return + else: + Logger.print_status("Re-Installing OctoApp for Klipper ...") + force_clone = True + + mr_instances: List[Moonraker] = get_instances(Moonraker) + + mr_names = [f"● {moonraker.data_dir.name}" for moonraker in mr_instances] + if len(mr_names) > 1: + Logger.print_dialog( + DialogType.INFO, + [ + "The following Moonraker instances were found:", + *mr_names, + "\n\n", + "The setup will apply the same names to OctoApp!", + ], + ) + + if not get_confirm( + "Continue OctoApp for Klipper installation?", + default_choice=True, + allow_go_back=True, + ): + Logger.print_info("Exiting OctoApp for Klipper installation ...") + return + + try: + git_clone_wrapper(OA_REPO, OA_DIR, force=force_clone) + + for moonraker in mr_instances: + instance = Octoapp(suffix=moonraker.suffix) + instance.create() + + InstanceManager.restart_all(mr_instances) + + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoApp for Klipper successfully installed!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error( + f"Error during OctoApp for Klipper installation:\n{e}" + ) + + def update_extension(self, **kwargs) -> None: + Logger.print_status("Updating OctoApp for Klipper ...") + try: + Octoapp.update() + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoApp for Klipper successfully updated!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error(f"Error during OctoApp for Klipper update:\n{e}") + + def remove_extension(self, **kwargs) -> None: + Logger.print_status("Removing OctoApp for Klipper ...") + + mr_instances: List[Moonraker] = get_instances(Moonraker) + ob_instances: List[Octoapp] = get_instances(Octoapp) + + try: + self._remove_OA_instances(ob_instances) + self._remove_OA_dir() + self._remove_OA_env() + remove_config_section(f"include {OA_SYS_CFG_NAME}", mr_instances) + run_remove_routines(OA_INSTALLER_LOG_FILE) + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoApp for Klipper successfully removed!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error(f"Error during OctoApp for Klipper removal:\n{e}") + + def _install_OA_dependencies(self) -> None: + OA_deps = [] + if OA_DEPS_JSON_FILE.exists(): + with open(OA_DEPS_JSON_FILE, "r") as deps: + OA_deps = json.load(deps).get("debian", []) + elif OA_INSTALL_SCRIPT.exists(): + OA_deps = parse_packages_from_file(OA_INSTALL_SCRIPT) + + if not OA_deps: + raise ValueError("Error reading OctoApp dependencies!") + + check_install_dependencies({*OA_deps}) + install_python_requirements(OA_ENV_DIR, OA_REQ_FILE) + + def _remove_OA_instances( + self, + instance_list: List[Octoapp], + ) -> None: + if not instance_list: + Logger.print_info("No OctoApp instances found. Skipped ...") + return + + for instance in instance_list: + Logger.print_status( + f"Removing instance {instance.service_file_path.stem} ..." + ) + InstanceManager.remove(instance) + + def _remove_OA_dir(self) -> None: + Logger.print_status("Removing OctoApp for Klipper directory ...") + + if not OA_DIR.exists(): + Logger.print_info(f"'{OA_DIR}' does not exist. Skipped ...") + return + + run_remove_routines(OA_DIR) + + def _remove_OA_env(self) -> None: + Logger.print_status("Removing OctoApp for Klipper environment ...") + + if not OA_ENV_DIR.exists(): + Logger.print_info(f"'{OA_ENV_DIR}' does not exist. Skipped ...") + return + + run_remove_routines(OA_ENV_DIR) diff --git a/kiauh/utils/instance_type.py b/kiauh/utils/instance_type.py index 4387509..47b3baf 100644 --- a/kiauh/utils/instance_type.py +++ b/kiauh/utils/instance_type.py @@ -13,6 +13,7 @@ from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker from extensions.obico.moonraker_obico import MoonrakerObico from extensions.octoeverywhere.octoeverywhere import Octoeverywhere +from extensions.octoapp.octoapp import Octoapp from extensions.telegram_bot.moonraker_telegram_bot import MoonrakerTelegramBot InstanceType = TypeVar( @@ -22,4 +23,5 @@ InstanceType = TypeVar( MoonrakerTelegramBot, MoonrakerObico, Octoeverywhere, + Octoapp, )