diff --git a/kiauh/components/klipper/klipper_dialogs.py b/kiauh/components/klipper/klipper_dialogs.py index 9108b32..61895e7 100644 --- a/kiauh/components/klipper/klipper_dialogs.py +++ b/kiauh/components/klipper/klipper_dialogs.py @@ -17,8 +17,8 @@ from core.constants import ( COLOR_YELLOW, RESET_FORMAT, ) -from core.instance_type import InstanceType from core.menus.base_menu import print_back_footer +from utils.instance_type import InstanceType @unique diff --git a/kiauh/components/mobileraker/mobileraker.py b/kiauh/components/mobileraker/mobileraker.py deleted file mode 100644 index 6370524..0000000 --- a/kiauh/components/mobileraker/mobileraker.py +++ /dev/null @@ -1,201 +0,0 @@ -# ======================================================================= # -# 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 shutil -from pathlib import Path -from subprocess import CalledProcessError, run -from typing import List - -from components.klipper.klipper import Klipper -from components.mobileraker import ( - MOBILERAKER_BACKUP_DIR, - MOBILERAKER_DIR, - MOBILERAKER_ENV_DIR, - MOBILERAKER_INSTALL_SCRIPT, - MOBILERAKER_LOG_NAME, - MOBILERAKER_REPO, - MOBILERAKER_REQ_FILE, - MOBILERAKER_SERVICE_FILE, - MOBILERAKER_SERVICE_NAME, - MOBILERAKER_UPDATER_SECTION_NAME, -) -from components.moonraker.moonraker import Moonraker -from core.backup_manager.backup_manager import BackupManager -from core.instance_manager.instance_manager import InstanceManager -from core.logger import DialogType, Logger -from core.settings.kiauh_settings import KiauhSettings -from core.types import ComponentStatus -from utils.common import check_install_dependencies, get_install_status -from utils.config_utils import add_config_section, remove_config_section -from utils.git_utils import ( - git_clone_wrapper, - git_pull_wrapper, -) -from utils.input_utils import get_confirm -from utils.instance_utils import get_instances -from utils.sys_utils import ( - check_python_version, - cmd_sysctl_service, - install_python_requirements, - remove_system_service, -) - - -def install_mobileraker() -> None: - Logger.print_status("Installing Mobileraker's companion ...") - - if not check_python_version(3, 7): - return - - mr_instances = get_instances(Moonraker) - if not mr_instances: - Logger.print_dialog( - DialogType.WARNING, - [ - "Moonraker not found! Mobileraker's companion will not properly work " - "without a working Moonraker installation.", - "Mobileraker's companion's update manager configuration for Moonraker " - "will not be added to any moonraker.conf.", - ], - ) - if not get_confirm( - "Continue Mobileraker's companion installation?", - default_choice=False, - allow_go_back=True, - ): - return - - check_install_dependencies() - - git_clone_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR) - - try: - run(MOBILERAKER_INSTALL_SCRIPT.as_posix(), shell=True, check=True) - if mr_instances: - patch_mobileraker_update_manager(mr_instances) - InstanceManager.restart_all(mr_instances) - else: - Logger.print_info( - "Moonraker is not installed! Cannot add Mobileraker's " - "companion to update manager!" - ) - Logger.print_ok("Mobileraker's companion successfully installed!") - except CalledProcessError as e: - Logger.print_error(f"Error installing Mobileraker's companion:\n{e}") - return - - -def patch_mobileraker_update_manager(instances: List[Moonraker]) -> None: - add_config_section( - section=MOBILERAKER_UPDATER_SECTION_NAME, - instances=instances, - options=[ - ("type", "git_repo"), - ("path", MOBILERAKER_DIR.as_posix()), - ("origin", MOBILERAKER_REPO), - ("primary_branch", "main"), - ("managed_services", "mobileraker"), - ("env", f"{MOBILERAKER_ENV_DIR}/bin/python"), - ("requirements", MOBILERAKER_REQ_FILE.as_posix()), - ("install_script", MOBILERAKER_INSTALL_SCRIPT.as_posix()), - ], - ) - - -def update_mobileraker() -> None: - try: - if not MOBILERAKER_DIR.exists(): - Logger.print_info( - "Mobileraker's companion does not seem to be installed! Skipping ..." - ) - return - - Logger.print_status("Updating Mobileraker's companion ...") - - cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "stop") - - settings = KiauhSettings() - if settings.kiauh.backup_before_update: - backup_mobileraker_dir() - - git_pull_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR) - - install_python_requirements(MOBILERAKER_ENV_DIR, MOBILERAKER_REQ_FILE) - - cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "start") - - Logger.print_ok("Mobileraker's companion updated successfully.", end="\n\n") - except CalledProcessError as e: - Logger.print_error(f"Error updating Mobileraker's companion:\n{e}") - return - - -def get_mobileraker_status() -> ComponentStatus: - return get_install_status( - MOBILERAKER_DIR, - MOBILERAKER_ENV_DIR, - files=[MOBILERAKER_SERVICE_FILE], - ) - - -def remove_mobileraker() -> None: - Logger.print_status("Removing Mobileraker's companion ...") - try: - if MOBILERAKER_DIR.exists(): - Logger.print_status("Removing Mobileraker's companion directory ...") - shutil.rmtree(MOBILERAKER_DIR) - Logger.print_ok("Mobileraker's companion directory successfully removed!") - else: - Logger.print_warn("Mobileraker's companion directory not found!") - - if MOBILERAKER_ENV_DIR.exists(): - Logger.print_status("Removing Mobileraker's companion environment ...") - shutil.rmtree(MOBILERAKER_ENV_DIR) - Logger.print_ok("Mobileraker's companion environment successfully removed!") - else: - Logger.print_warn("Mobileraker's companion environment not found!") - - if MOBILERAKER_SERVICE_FILE.exists(): - remove_system_service(MOBILERAKER_SERVICE_NAME) - - kl_instances: List[Klipper] = get_instances(Klipper) - for instance in kl_instances: - logfile = instance.base.log_dir.joinpath(MOBILERAKER_LOG_NAME) - if logfile.exists(): - Logger.print_status(f"Removing {logfile} ...") - Path(logfile).unlink() - Logger.print_ok(f"{logfile} successfully removed!") - - mr_instances: List[Moonraker] = get_instances(Moonraker) - if mr_instances: - Logger.print_status( - "Removing Mobileraker's companion from update manager ..." - ) - remove_config_section(MOBILERAKER_UPDATER_SECTION_NAME, mr_instances) - Logger.print_ok( - "Mobileraker's companion successfully removed from update manager!" - ) - - Logger.print_ok("Mobileraker's companion successfully removed!") - - except Exception as e: - Logger.print_error(f"Error removing Mobileraker's companion:\n{e}") - - -def backup_mobileraker_dir() -> None: - bm = BackupManager() - bm.backup_directory( - MOBILERAKER_DIR.name, - source=MOBILERAKER_DIR, - target=MOBILERAKER_BACKUP_DIR, - ) - bm.backup_directory( - MOBILERAKER_ENV_DIR.name, - source=MOBILERAKER_ENV_DIR, - target=MOBILERAKER_BACKUP_DIR, - ) diff --git a/kiauh/components/octoeverywhere/octoeverywhere_setup.py b/kiauh/components/octoeverywhere/octoeverywhere_setup.py deleted file mode 100644 index 9940de4..0000000 --- a/kiauh/components/octoeverywhere/octoeverywhere_setup.py +++ /dev/null @@ -1,197 +0,0 @@ -# ======================================================================= # -# 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 components.octoeverywhere import ( - OE_DEPS_JSON_FILE, - OE_DIR, - OE_ENV_DIR, - OE_INSTALL_SCRIPT, - OE_INSTALLER_LOG_FILE, - OE_REPO, - OE_REQ_FILE, - OE_SYS_CFG_NAME, -) -from components.octoeverywhere.octoeverywhere import Octoeverywhere -from core.instance_manager.instance_manager import InstanceManager -from core.logger import DialogType, Logger -from core.types import ComponentStatus -from utils.common import ( - check_install_dependencies, - get_install_status, - 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, -) - - -def get_octoeverywhere_status() -> ComponentStatus: - return get_install_status(OE_DIR, OE_ENV_DIR, Octoeverywhere) - - -def install_octoeverywhere() -> None: - Logger.print_status("Installing OctoEverywhere for Klipper ...") - - # check if moonraker is installed. if not, notify the user and exit - if not moonraker_exists(): - return - - force_clone = False - oe_instances: List[Octoeverywhere] = get_instances(Octoeverywhere) - if oe_instances: - Logger.print_dialog( - DialogType.INFO, - [ - "OctoEverywhere 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 OctoEverywhere installation?"): - Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") - return - else: - Logger.print_status("Re-Installing OctoEverywhere 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 OctoEverywhere!", - ], - ) - - if not get_confirm( - "Continue OctoEverywhere for Klipper installation?", - default_choice=True, - allow_go_back=True, - ): - Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") - return - - try: - git_clone_wrapper(OE_REPO, OE_DIR, force=force_clone) - - for moonraker in mr_instances: - instance = Octoeverywhere(suffix=moonraker.suffix) - instance.create() - - InstanceManager.restart_all(mr_instances) - - Logger.print_dialog( - DialogType.SUCCESS, - ["OctoEverywhere for Klipper successfully installed!"], - center_content=True, - ) - - except Exception as e: - Logger.print_error( - f"Error during OctoEverywhere for Klipper installation:\n{e}" - ) - - -def update_octoeverywhere() -> None: - Logger.print_status("Updating OctoEverywhere for Klipper ...") - try: - Octoeverywhere.update() - Logger.print_dialog( - DialogType.SUCCESS, - ["OctoEverywhere for Klipper successfully updated!"], - center_content=True, - ) - - except Exception as e: - Logger.print_error(f"Error during OctoEverywhere for Klipper update:\n{e}") - - -def remove_octoeverywhere() -> None: - Logger.print_status("Removing OctoEverywhere for Klipper ...") - - mr_instances: List[Moonraker] = get_instances(Moonraker) - ob_instances: List[Octoeverywhere] = get_instances(Octoeverywhere) - - try: - remove_oe_instances(ob_instances) - remove_oe_dir() - remove_oe_env() - remove_config_section(f"include {OE_SYS_CFG_NAME}", mr_instances) - run_remove_routines(OE_INSTALLER_LOG_FILE) - Logger.print_dialog( - DialogType.SUCCESS, - ["OctoEverywhere for Klipper successfully removed!"], - center_content=True, - ) - - except Exception as e: - Logger.print_error(f"Error during OctoEverywhere for Klipper removal:\n{e}") - - -def install_oe_dependencies() -> None: - oe_deps = [] - if OE_DEPS_JSON_FILE.exists(): - with open(OE_DEPS_JSON_FILE, "r") as deps: - oe_deps = json.load(deps).get("debian", []) - elif OE_INSTALL_SCRIPT.exists(): - oe_deps = parse_packages_from_file(OE_INSTALL_SCRIPT) - - if not oe_deps: - raise ValueError("Error reading OctoEverywhere dependencies!") - - check_install_dependencies({*oe_deps}) - install_python_requirements(OE_ENV_DIR, OE_REQ_FILE) - - -def remove_oe_instances( - instance_list: List[Octoeverywhere], -) -> None: - if not instance_list: - Logger.print_info("No OctoEverywhere 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_oe_dir() -> None: - Logger.print_status("Removing OctoEverywhere for Klipper directory ...") - - if not OE_DIR.exists(): - Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...") - return - - run_remove_routines(OE_DIR) - - -def remove_oe_env() -> None: - Logger.print_status("Removing OctoEverywhere for Klipper environment ...") - - if not OE_ENV_DIR.exists(): - Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...") - return - - run_remove_routines(OE_ENV_DIR) diff --git a/kiauh/core/instance_manager/instance_manager.py b/kiauh/core/instance_manager/instance_manager.py index 9142bee..1032fc9 100644 --- a/kiauh/core/instance_manager/instance_manager.py +++ b/kiauh/core/instance_manager/instance_manager.py @@ -12,8 +12,8 @@ from pathlib import Path from subprocess import CalledProcessError from typing import List -from core.instance_type import InstanceType from core.logger import Logger +from utils.instance_type import InstanceType from utils.sys_utils import cmd_sysctl_service diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index 990b8dc..8318006 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -14,9 +14,7 @@ from typing import Type from components.crowsnest.crowsnest import install_crowsnest from components.klipper import klipper_setup from components.klipperscreen.klipperscreen import install_klipperscreen -from components.mobileraker.mobileraker import install_mobileraker from components.moonraker import moonraker_setup -from components.octoeverywhere.octoeverywhere_setup import install_octoeverywhere from components.webui_client import client_setup from components.webui_client.client_config import client_config_setup from components.webui_client.fluidd_data import FluiddData @@ -47,9 +45,7 @@ class InstallMenu(BaseMenu): "5": Option(method=self.install_mainsail_config), "6": Option(method=self.install_fluidd_config), "7": Option(method=self.install_klipperscreen), - "8": Option(method=self.install_mobileraker), - "9": Option(method=self.install_crowsnest), - "10": Option(method=self.install_octoeverywhere), + "8": Option(method=self.install_crowsnest), } def print_menu(self) -> None: @@ -64,15 +60,14 @@ class InstallMenu(BaseMenu): ║ Firmware & API: │ Touchscreen GUI: ║ ║ 1) [Klipper] │ 7) [KlipperScreen] ║ ║ 2) [Moonraker] │ ║ - ║ │ Android / iOS: ║ - ║ Webinterface: │ 8) [Mobileraker] ║ + ║ │ Webcam Streamer: ║ + ║ Webinterface: │ 8) [Crowsnest] ║ ║ 3) [Mainsail] │ ║ - ║ 4) [Fluidd] │ Webcam Streamer: ║ - ║ │ 9) [Crowsnest] ║ - ║ Client-Config: │ ║ - ║ 5) [Mainsail-Config] │ Remote Access: ║ - ║ 6) [Fluidd-Config] │ 10) [OctoEverywhere] ║ + ║ 4) [Fluidd] │ ║ ║ │ ║ + ║ Client-Config: │ ║ + ║ 5) [Mainsail-Config] │ ║ + ║ 6) [Fluidd-Config] │ ║ ╟───────────────────────────┴───────────────────────────╢ """ )[1:] @@ -99,11 +94,5 @@ class InstallMenu(BaseMenu): def install_klipperscreen(self, **kwargs) -> None: install_klipperscreen() - def install_mobileraker(self, **kwargs) -> None: - install_mobileraker() - def install_crowsnest(self, **kwargs) -> None: install_crowsnest() - - def install_octoeverywhere(self, **kwargs) -> None: - install_octoeverywhere() diff --git a/kiauh/core/menus/main_menu.py b/kiauh/core/menus/main_menu.py index 7366053..2e691b8 100644 --- a/kiauh/core/menus/main_menu.py +++ b/kiauh/core/menus/main_menu.py @@ -16,9 +16,7 @@ from components.crowsnest.crowsnest import get_crowsnest_status from components.klipper.klipper_utils import get_klipper_status from components.klipperscreen.klipperscreen import get_klipperscreen_status from components.log_uploads.menus.log_upload_menu import LogUploadMenu -from components.mobileraker.mobileraker import get_mobileraker_status from components.moonraker.moonraker_utils import get_moonraker_status -from components.octoeverywhere.octoeverywhere_setup import get_octoeverywhere_status from components.webui_client.client_utils import ( get_client_status, get_current_client_config, @@ -59,8 +57,8 @@ class MainMenu(BaseMenu): self.version = "" self.kl_status = self.kl_owner = self.kl_repo = "" self.mr_status = self.mr_owner = self.mr_repo = "" - self.ms_status = self.fl_status = self.ks_status = self.mb_status = "" - self.cn_status = self.cc_status = self.oe_status = "" + self.ms_status = self.fl_status = self.ks_status = "" + self.cn_status = self.cc_status = "" self._init_status() def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: @@ -80,7 +78,7 @@ class MainMenu(BaseMenu): } def _init_status(self) -> None: - status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn", "oe"] + status_vars = ["kl", "mr", "ms", "fl", "ks", "cn"] for var in status_vars: setattr( self, @@ -96,9 +94,7 @@ class MainMenu(BaseMenu): self._get_component_status("fl", get_client_status, FluiddData()) self.cc_status = get_current_client_config([MainsailData(), FluiddData()]) self._get_component_status("ks", get_klipperscreen_status) - self._get_component_status("mb", get_mobileraker_status) self._get_component_status("cn", get_crowsnest_status) - self._get_component_status("oe", get_octoeverywhere_status) def _get_component_status(self, name: str, status_fn: Callable, *args) -> None: status_data: ComponentStatus = status_fn(*args) @@ -155,8 +151,6 @@ class MainMenu(BaseMenu): ║ Community: │ Client-Config: {self.cc_status:<{pad2}} ║ ║ E) [Extensions] │ ║ ║ │ KlipperScreen: {self.ks_status:<{pad2}} ║ - ║ │ Mobileraker: {self.mb_status:<{pad2}} ║ - ║ │ OctoEverywhere: {self.oe_status:<{pad2}} ║ ║ │ Crowsnest: {self.cn_status:<{pad2}} ║ ╟──────────────────┼────────────────────────────────────╢ ║ {footer1:^25} │ {footer2:^43} ║ diff --git a/kiauh/core/menus/remove_menu.py b/kiauh/core/menus/remove_menu.py index 2f18455..53540d2 100644 --- a/kiauh/core/menus/remove_menu.py +++ b/kiauh/core/menus/remove_menu.py @@ -14,11 +14,9 @@ from typing import Type from components.crowsnest.crowsnest import remove_crowsnest from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu from components.klipperscreen.klipperscreen import remove_klipperscreen -from components.mobileraker.mobileraker import remove_mobileraker from components.moonraker.menus.moonraker_remove_menu import ( MoonrakerRemoveMenu, ) -from components.octoeverywhere.octoeverywhere_setup import remove_octoeverywhere from components.webui_client.fluidd_data import FluiddData from components.webui_client.mainsail_data import MainsailData from components.webui_client.menus.client_remove_menu import ClientRemoveMenu @@ -46,9 +44,8 @@ class RemoveMenu(BaseMenu): "3": Option(method=self.remove_mainsail), "4": Option(method=self.remove_fluidd), "5": Option(method=self.remove_klipperscreen), - "6": Option(method=self.remove_mobileraker), - "7": Option(method=self.remove_crowsnest), - "8": Option(method=self.remove_octoeverywhere), + "6": Option(method=self.remove_crowsnest), + "7": Option(method=self.remove_octoeverywhere), } def print_menu(self) -> None: @@ -62,16 +59,13 @@ class RemoveMenu(BaseMenu): ╟───────────────────────────────────────────────────────╢ ║ INFO: Configurations and/or any backups will be kept! ║ ╟───────────────────────────┬───────────────────────────╢ - ║ Firmware & API: │ Android / iOS: ║ - ║ 1) [Klipper] │ 6) [Mobileraker] ║ + ║ Firmware & API: │ Touchscreen GUI: ║ + ║ 1) [Klipper] │ 5) [KlipperScreen] ║ ║ 2) [Moonraker] │ ║ ║ │ Webcam Streamer: ║ - ║ Klipper Webinterface: │ 7) [Crowsnest] ║ + ║ Klipper Webinterface: │ 6) [Crowsnest] ║ ║ 3) [Mainsail] │ ║ - ║ 4) [Fluidd] │ Remote Access: ║ - ║ │ 8) [OctoEverywhere] ║ - ║ Touchscreen GUI: │ ║ - ║ 5) [KlipperScreen] │ ║ + ║ 4) [Fluidd] │ ║ ╟───────────────────────────┴───────────────────────────╢ """ )[1:] @@ -92,11 +86,5 @@ class RemoveMenu(BaseMenu): def remove_klipperscreen(self, **kwargs) -> None: remove_klipperscreen() - def remove_mobileraker(self, **kwargs) -> None: - remove_mobileraker() - def remove_crowsnest(self, **kwargs) -> None: remove_crowsnest() - - def remove_octoeverywhere(self, **kwargs) -> None: - remove_octoeverywhere() diff --git a/kiauh/core/menus/update_menu.py b/kiauh/core/menus/update_menu.py index daa6b21..0838015 100644 --- a/kiauh/core/menus/update_menu.py +++ b/kiauh/core/menus/update_menu.py @@ -20,16 +20,8 @@ from components.klipperscreen.klipperscreen import ( get_klipperscreen_status, update_klipperscreen, ) -from components.mobileraker.mobileraker import ( - get_mobileraker_status, - update_mobileraker, -) from components.moonraker.moonraker_setup import update_moonraker from components.moonraker.moonraker_utils import get_moonraker_status -from components.octoeverywhere.octoeverywhere_setup import ( - get_octoeverywhere_status, - update_octoeverywhere, -) from components.webui_client.client_config.client_config_setup import ( update_client_config, ) @@ -76,9 +68,7 @@ class UpdateMenu(BaseMenu): self.fluidd_local = self.fluidd_remote = "" self.fluidd_config_local = self.fluidd_config_remote = "" self.klipperscreen_local = self.klipperscreen_remote = "" - self.mobileraker_local = self.mobileraker_remote = "" self.crowsnest_local = self.crowsnest_remote = "" - self.octoeverywhere_local = self.octoeverywhere_remote = "" self.mainsail_data = MainsailData() self.fluidd_data = FluiddData() @@ -89,10 +79,8 @@ class UpdateMenu(BaseMenu): "mainsail_config": {"installed": False, "local": None, "remote": None}, "fluidd": {"installed": False, "local": None, "remote": None}, "fluidd_config": {"installed": False, "local": None, "remote": None}, - "mobileraker": {"installed": False, "local": None, "remote": None}, "klipperscreen": {"installed": False, "local": None, "remote": None}, "crowsnest": {"installed": False, "local": None, "remote": None}, - "octoeverywhere": {"installed": False, "local": None, "remote": None}, } def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: @@ -110,10 +98,8 @@ class UpdateMenu(BaseMenu): "5": Option(self.update_mainsail_config), "6": Option(self.update_fluidd_config), "7": Option(self.update_klipperscreen), - "8": Option(self.update_mobileraker), - "9": Option(self.update_crowsnest), - "10": Option(self.update_octoeverywhere), - "11": Option(self.upgrade_system_packages), + "8": Option(self.update_crowsnest), + "9": Option(self.upgrade_system_packages), } def print_menu(self) -> None: @@ -157,11 +143,9 @@ class UpdateMenu(BaseMenu): ║ │ │ ║ ║ Other: ├───────────────┼───────────────╢ ║ 7) KlipperScreen │ {self.klipperscreen_local:<22} │ {self.klipperscreen_remote:<22} ║ - ║ 8) Mobileraker │ {self.mobileraker_local:<22} │ {self.mobileraker_remote:<22} ║ - ║ 9) Crowsnest │ {self.crowsnest_local:<22} │ {self.crowsnest_remote:<22} ║ - ║ 10) OctoEverywhere │ {self.octoeverywhere_local:<22} │ {self.octoeverywhere_remote:<22} ║ + ║ 8) Crowsnest │ {self.crowsnest_local:<22} │ {self.crowsnest_remote:<22} ║ ║ ├───────────────┴───────────────╢ - ║ 11) System │ {sysupgrades:^{padding}} ║ + ║ 9) System │ {sysupgrades:^{padding}} ║ ╟───────────────────────┴───────────────────────────────╢ """ )[1:] @@ -198,18 +182,10 @@ class UpdateMenu(BaseMenu): if self._check_is_installed("klipperscreen"): update_klipperscreen() - def update_mobileraker(self, **kwargs) -> None: - if self._check_is_installed("mobileraker"): - update_mobileraker() - def update_crowsnest(self, **kwargs) -> None: if self._check_is_installed("crowsnest"): update_crowsnest() - def update_octoeverywhere(self, **kwargs) -> None: - if self._check_is_installed("octoeverywhere"): - update_octoeverywhere() - def upgrade_system_packages(self, **kwargs) -> None: self._run_system_updates() @@ -225,9 +201,7 @@ class UpdateMenu(BaseMenu): "fluidd_config", get_client_config_status, self.fluidd_data ) self._set_status_data("klipperscreen", get_klipperscreen_status) - self._set_status_data("mobileraker", get_mobileraker_status) self._set_status_data("crowsnest", get_crowsnest_status) - self._set_status_data("octoeverywhere", get_octoeverywhere_status) update_system_package_lists(silent=True) self.packages = get_upgradable_packages() diff --git a/kiauh/extensions/klipper_backup/metadata.json b/kiauh/extensions/klipper_backup/metadata.json index ac09323..68f215d 100644 --- a/kiauh/extensions/klipper_backup/metadata.json +++ b/kiauh/extensions/klipper_backup/metadata.json @@ -1,6 +1,6 @@ { "metadata": { - "index": 3, + "index": 4, "module": "klipper_backup_extension", "maintained_by": "Staubgeborener", "display_name": "Klipper-Backup", diff --git a/kiauh/extensions/mainsail_theme_installer/mainsail_theme_installer_extension.py b/kiauh/extensions/mainsail_theme_installer/mainsail_theme_installer_extension.py index 0277582..c613f04 100644 --- a/kiauh/extensions/mainsail_theme_installer/mainsail_theme_installer_extension.py +++ b/kiauh/extensions/mainsail_theme_installer/mainsail_theme_installer_extension.py @@ -21,13 +21,13 @@ from components.klipper.klipper_dialogs import ( print_instance_overview, ) from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT -from core.instance_type import InstanceType from core.logger import Logger from core.menus import Option from core.menus.base_menu import BaseMenu from extensions.base_extension import BaseExtension from utils.git_utils import git_clone_wrapper from utils.input_utils import get_selection_input +from utils.instance_type import InstanceType from utils.instance_utils import get_instances diff --git a/kiauh/components/mobileraker/__init__.py b/kiauh/extensions/mobileraker/__init__.py similarity index 99% rename from kiauh/components/mobileraker/__init__.py rename to kiauh/extensions/mobileraker/__init__.py index e8be4ad..3ce4987 100644 --- a/kiauh/components/mobileraker/__init__.py +++ b/kiauh/extensions/mobileraker/__init__.py @@ -6,6 +6,7 @@ # # # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # + from pathlib import Path from core.backup_manager import BACKUP_ROOT_DIR diff --git a/kiauh/extensions/mobileraker/metadata.json b/kiauh/extensions/mobileraker/metadata.json new file mode 100644 index 0000000..42aa311 --- /dev/null +++ b/kiauh/extensions/mobileraker/metadata.json @@ -0,0 +1,12 @@ +{ + "metadata": { + "index": 3, + "module": "mobileraker_extension", + "maintained_by": "Clon1998", + "display_name": "Mobileraker", + "description": [ + "Companion for Mobileraker, enabling push notification for Klipper using Moonraker." + ], + "updates": true + } +} diff --git a/kiauh/extensions/mobileraker/mobileraker_extension.py b/kiauh/extensions/mobileraker/mobileraker_extension.py new file mode 100644 index 0000000..47aca2c --- /dev/null +++ b/kiauh/extensions/mobileraker/mobileraker_extension.py @@ -0,0 +1,192 @@ +# ======================================================================= # +# 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 shutil +from pathlib import Path +from subprocess import CalledProcessError, run +from typing import List + +from components.klipper.klipper import Klipper +from components.moonraker.moonraker import Moonraker +from core.backup_manager.backup_manager import BackupManager +from core.instance_manager.instance_manager import InstanceManager +from core.logger import DialogType, Logger +from core.settings.kiauh_settings import KiauhSettings +from extensions.base_extension import BaseExtension +from extensions.mobileraker import ( + MOBILERAKER_BACKUP_DIR, + MOBILERAKER_DIR, + MOBILERAKER_ENV_DIR, + MOBILERAKER_INSTALL_SCRIPT, + MOBILERAKER_LOG_NAME, + MOBILERAKER_REPO, + MOBILERAKER_REQ_FILE, + MOBILERAKER_SERVICE_FILE, + MOBILERAKER_SERVICE_NAME, + MOBILERAKER_UPDATER_SECTION_NAME, +) +from utils.common import check_install_dependencies +from utils.config_utils import add_config_section, remove_config_section +from utils.git_utils import git_clone_wrapper, git_pull_wrapper +from utils.input_utils import get_confirm +from utils.instance_utils import get_instances +from utils.sys_utils import ( + check_python_version, + cmd_sysctl_service, + install_python_requirements, + remove_system_service, +) + + +# noinspection PyMethodMayBeStatic +class MobilerakerExtension(BaseExtension): + def install_extension(self, **kwargs) -> None: + Logger.print_status("Installing Mobileraker's companion ...") + + if not check_python_version(3, 7): + return + + mr_instances = get_instances(Moonraker) + if not mr_instances: + Logger.print_dialog( + DialogType.WARNING, + [ + "Moonraker not found! Mobileraker's companion will not properly " + "work without a working Moonraker installation.", + "Mobileraker's companion's update manager configuration for " + "Moonraker will not be added to any moonraker.conf.", + ], + ) + if not get_confirm( + "Continue Mobileraker's companion installation?", + default_choice=False, + allow_go_back=True, + ): + return + + check_install_dependencies() + + git_clone_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR) + + try: + run(MOBILERAKER_INSTALL_SCRIPT.as_posix(), shell=True, check=True) + if mr_instances: + self._patch_mobileraker_update_manager(mr_instances) + InstanceManager.restart_all(mr_instances) + else: + Logger.print_info( + "Moonraker is not installed! Cannot add Mobileraker's " + "companion to update manager!" + ) + Logger.print_ok("Mobileraker's companion successfully installed!") + except CalledProcessError as e: + Logger.print_error(f"Error installing Mobileraker's companion:\n{e}") + return + + def update_extension(self, **kwargs) -> None: + try: + if not MOBILERAKER_DIR.exists(): + Logger.print_info( + "Mobileraker's companion doesn't seem to be installed! Skipping ..." + ) + return + + Logger.print_status("Updating Mobileraker's companion ...") + + cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "stop") + + settings = KiauhSettings() + if settings.kiauh.backup_before_update: + self._backup_mobileraker_dir() + + git_pull_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR) + + install_python_requirements(MOBILERAKER_ENV_DIR, MOBILERAKER_REQ_FILE) + + cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "start") + + Logger.print_ok("Mobileraker's companion updated successfully.", end="\n\n") + except CalledProcessError as e: + Logger.print_error(f"Error updating Mobileraker's companion:\n{e}") + return + + def remove_extension(self, **kwargs) -> None: + Logger.print_status("Removing Mobileraker's companion ...") + try: + if MOBILERAKER_DIR.exists(): + Logger.print_status("Removing Mobileraker's companion directory ...") + shutil.rmtree(MOBILERAKER_DIR) + Logger.print_ok( + "Mobileraker's companion directory successfully removed!" + ) + else: + Logger.print_warn("Mobileraker's companion directory not found!") + + if MOBILERAKER_ENV_DIR.exists(): + Logger.print_status("Removing Mobileraker's companion environment ...") + shutil.rmtree(MOBILERAKER_ENV_DIR) + Logger.print_ok( + "Mobileraker's companion environment successfully removed!" + ) + else: + Logger.print_warn("Mobileraker's companion environment not found!") + + if MOBILERAKER_SERVICE_FILE.exists(): + remove_system_service(MOBILERAKER_SERVICE_NAME) + + kl_instances: List[Klipper] = get_instances(Klipper) + for instance in kl_instances: + logfile = instance.base.log_dir.joinpath(MOBILERAKER_LOG_NAME) + if logfile.exists(): + Logger.print_status(f"Removing {logfile} ...") + Path(logfile).unlink() + Logger.print_ok(f"{logfile} successfully removed!") + + mr_instances: List[Moonraker] = get_instances(Moonraker) + if mr_instances: + Logger.print_status( + "Removing Mobileraker's companion from update manager ..." + ) + remove_config_section(MOBILERAKER_UPDATER_SECTION_NAME, mr_instances) + Logger.print_ok( + "Mobileraker's companion successfully removed from update manager!" + ) + + Logger.print_ok("Mobileraker's companion successfully removed!") + + except Exception as e: + Logger.print_error(f"Error removing Mobileraker's companion:\n{e}") + + def _patch_mobileraker_update_manager(self, instances: List[Moonraker]) -> None: + add_config_section( + section=MOBILERAKER_UPDATER_SECTION_NAME, + instances=instances, + options=[ + ("type", "git_repo"), + ("path", MOBILERAKER_DIR.as_posix()), + ("origin", MOBILERAKER_REPO), + ("primary_branch", "main"), + ("managed_services", "mobileraker"), + ("env", f"{MOBILERAKER_ENV_DIR}/bin/python"), + ("requirements", MOBILERAKER_REQ_FILE.as_posix()), + ("install_script", MOBILERAKER_INSTALL_SCRIPT.as_posix()), + ], + ) + + def _backup_mobileraker_dir(self) -> None: + bm = BackupManager() + bm.backup_directory( + MOBILERAKER_DIR.name, + source=MOBILERAKER_DIR, + target=MOBILERAKER_BACKUP_DIR, + ) + bm.backup_directory( + MOBILERAKER_ENV_DIR.name, + source=MOBILERAKER_ENV_DIR, + target=MOBILERAKER_BACKUP_DIR, + ) diff --git a/kiauh/components/octoeverywhere/__init__.py b/kiauh/extensions/octoeverywhere/__init__.py similarity index 100% rename from kiauh/components/octoeverywhere/__init__.py rename to kiauh/extensions/octoeverywhere/__init__.py diff --git a/kiauh/extensions/octoeverywhere/metadata.json b/kiauh/extensions/octoeverywhere/metadata.json new file mode 100644 index 0000000..212688d --- /dev/null +++ b/kiauh/extensions/octoeverywhere/metadata.json @@ -0,0 +1,16 @@ +{ + "metadata": { + "index": 7, + "module": "octoeverywhere_extension", + "maintained_by": "QuinnDamerell", + "display_name": "OctoEverywhere for Klipper", + "description": [ + "Cloud Empower Your Klipper 3D Printers With:", + "- Free, Private, And Secure Remote Access", + "- AI Print Failure Detection", + "- Real-time Notifications", + "- Live Streaming, and More!" + ], + "updates": true + } +} diff --git a/kiauh/components/octoeverywhere/octoeverywhere.py b/kiauh/extensions/octoeverywhere/octoeverywhere.py similarity index 98% rename from kiauh/components/octoeverywhere/octoeverywhere.py rename to kiauh/extensions/octoeverywhere/octoeverywhere.py index 7a1f58a..d3cdfd0 100644 --- a/kiauh/components/octoeverywhere/octoeverywhere.py +++ b/kiauh/extensions/octoeverywhere/octoeverywhere.py @@ -14,7 +14,9 @@ from subprocess import CalledProcessError, run from components.moonraker import MOONRAKER_CFG_NAME from components.moonraker.moonraker import Moonraker -from components.octoeverywhere import ( +from core.instance_manager.base_instance import BaseInstance +from core.logger import Logger +from extensions.octoeverywhere import ( OE_CFG_NAME, OE_DIR, OE_ENV_DIR, @@ -23,8 +25,6 @@ from components.octoeverywhere import ( OE_SYS_CFG_NAME, OE_UPDATE_SCRIPT, ) -from core.instance_manager.base_instance import BaseInstance -from core.logger import Logger from utils.sys_utils import get_service_file_path diff --git a/kiauh/extensions/octoeverywhere/octoeverywhere_extension.py b/kiauh/extensions/octoeverywhere/octoeverywhere_extension.py new file mode 100644 index 0000000..72e4912 --- /dev/null +++ b/kiauh/extensions/octoeverywhere/octoeverywhere_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.octoeverywhere import ( + OE_DEPS_JSON_FILE, + OE_DIR, + OE_ENV_DIR, + OE_INSTALL_SCRIPT, + OE_INSTALLER_LOG_FILE, + OE_REPO, + OE_REQ_FILE, + OE_SYS_CFG_NAME, +) +from extensions.octoeverywhere.octoeverywhere import Octoeverywhere +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 OctoeverywhereExtension(BaseExtension): + def install_extension(self, **kwargs) -> None: + Logger.print_status("Installing OctoEverywhere for Klipper ...") + + # check if moonraker is installed. if not, notify the user and exit + if not moonraker_exists(): + return + + force_clone = False + oe_instances: List[Octoeverywhere] = get_instances(Octoeverywhere) + if oe_instances: + Logger.print_dialog( + DialogType.INFO, + [ + "OctoEverywhere 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 OctoEverywhere installation?"): + Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") + return + else: + Logger.print_status("Re-Installing OctoEverywhere 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 OctoEverywhere!", + ], + ) + + if not get_confirm( + "Continue OctoEverywhere for Klipper installation?", + default_choice=True, + allow_go_back=True, + ): + Logger.print_info("Exiting OctoEverywhere for Klipper installation ...") + return + + try: + git_clone_wrapper(OE_REPO, OE_DIR, force=force_clone) + + for moonraker in mr_instances: + instance = Octoeverywhere(suffix=moonraker.suffix) + instance.create() + + InstanceManager.restart_all(mr_instances) + + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoEverywhere for Klipper successfully installed!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error( + f"Error during OctoEverywhere for Klipper installation:\n{e}" + ) + + def update_extension(self, **kwargs) -> None: + Logger.print_status("Updating OctoEverywhere for Klipper ...") + try: + Octoeverywhere.update() + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoEverywhere for Klipper successfully updated!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error(f"Error during OctoEverywhere for Klipper update:\n{e}") + + def remove_extension(self, **kwargs) -> None: + Logger.print_status("Removing OctoEverywhere for Klipper ...") + + mr_instances: List[Moonraker] = get_instances(Moonraker) + ob_instances: List[Octoeverywhere] = get_instances(Octoeverywhere) + + try: + self._remove_oe_instances(ob_instances) + self._remove_oe_dir() + self._remove_oe_env() + remove_config_section(f"include {OE_SYS_CFG_NAME}", mr_instances) + run_remove_routines(OE_INSTALLER_LOG_FILE) + Logger.print_dialog( + DialogType.SUCCESS, + ["OctoEverywhere for Klipper successfully removed!"], + center_content=True, + ) + + except Exception as e: + Logger.print_error(f"Error during OctoEverywhere for Klipper removal:\n{e}") + + def _install_oe_dependencies(self) -> None: + oe_deps = [] + if OE_DEPS_JSON_FILE.exists(): + with open(OE_DEPS_JSON_FILE, "r") as deps: + oe_deps = json.load(deps).get("debian", []) + elif OE_INSTALL_SCRIPT.exists(): + oe_deps = parse_packages_from_file(OE_INSTALL_SCRIPT) + + if not oe_deps: + raise ValueError("Error reading OctoEverywhere dependencies!") + + check_install_dependencies({*oe_deps}) + install_python_requirements(OE_ENV_DIR, OE_REQ_FILE) + + def _remove_oe_instances( + self, + instance_list: List[Octoeverywhere], + ) -> None: + if not instance_list: + Logger.print_info("No OctoEverywhere 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_oe_dir(self) -> None: + Logger.print_status("Removing OctoEverywhere for Klipper directory ...") + + if not OE_DIR.exists(): + Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...") + return + + run_remove_routines(OE_DIR) + + def _remove_oe_env(self) -> None: + Logger.print_status("Removing OctoEverywhere for Klipper environment ...") + + if not OE_ENV_DIR.exists(): + Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...") + return + + run_remove_routines(OE_ENV_DIR) diff --git a/kiauh/extensions/pretty_gcode/metadata.json b/kiauh/extensions/pretty_gcode/metadata.json index 187a429..0470f01 100644 --- a/kiauh/extensions/pretty_gcode/metadata.json +++ b/kiauh/extensions/pretty_gcode/metadata.json @@ -1,6 +1,6 @@ { "metadata": { - "index": 5, + "index": 8, "module": "pretty_gcode_extension", "maintained_by": "Kragrathea", "display_name": "PrettyGCode for Klipper", diff --git a/kiauh/extensions/telegram_bot/metadata.json b/kiauh/extensions/telegram_bot/metadata.json index 35b72ae..3ffadc7 100644 --- a/kiauh/extensions/telegram_bot/metadata.json +++ b/kiauh/extensions/telegram_bot/metadata.json @@ -1,6 +1,6 @@ { "metadata": { - "index": 4, + "index": 5, "module": "moonraker_telegram_bot_extension", "maintained_by": "nlef", "display_name": "Moonraker Telegram Bot", diff --git a/kiauh/utils/config_utils.py b/kiauh/utils/config_utils.py index 28f88d9..32f5b9c 100644 --- a/kiauh/utils/config_utils.py +++ b/kiauh/utils/config_utils.py @@ -12,11 +12,11 @@ import tempfile from pathlib import Path from typing import List, Tuple -from core.instance_type import InstanceType from core.logger import Logger from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( SimpleConfigParser, ) +from utils.instance_type import InstanceType ConfigOption = Tuple[str, str] diff --git a/kiauh/utils/git_utils.py b/kiauh/utils/git_utils.py index 6e2977c..fd39e0c 100644 --- a/kiauh/utils/git_utils.py +++ b/kiauh/utils/git_utils.py @@ -10,9 +10,9 @@ from subprocess import DEVNULL, PIPE, CalledProcessError, check_output, run from typing import List, Type from core.instance_manager.instance_manager import InstanceManager -from core.instance_type import InstanceType from core.logger import Logger from utils.input_utils import get_confirm, get_number_input +from utils.instance_type import InstanceType from utils.instance_utils import get_instances diff --git a/kiauh/core/instance_type.py b/kiauh/utils/instance_type.py similarity index 94% rename from kiauh/core/instance_type.py rename to kiauh/utils/instance_type.py index c021c50..4387509 100644 --- a/kiauh/core/instance_type.py +++ b/kiauh/utils/instance_type.py @@ -11,8 +11,8 @@ from typing import TypeVar from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker -from components.octoeverywhere.octoeverywhere import Octoeverywhere from extensions.obico.moonraker_obico import MoonrakerObico +from extensions.octoeverywhere.octoeverywhere import Octoeverywhere from extensions.telegram_bot.moonraker_telegram_bot import MoonrakerTelegramBot InstanceType = TypeVar( diff --git a/kiauh/utils/instance_utils.py b/kiauh/utils/instance_utils.py index b95e99d..af882e9 100644 --- a/kiauh/utils/instance_utils.py +++ b/kiauh/utils/instance_utils.py @@ -14,7 +14,7 @@ from typing import List from core.constants import SYSTEMD from core.instance_manager.base_instance import SUFFIX_BLACKLIST -from core.instance_type import InstanceType +from utils.instance_type import InstanceType def get_instances(instance_type: type) -> List[InstanceType]: