From 1fc7cc129e652b58c7445248e5c3a020d3a20dd9 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sat, 13 Apr 2024 15:40:57 +0200 Subject: [PATCH 01/27] add klipper-backup to KIAUH --- kiauh/extensions/klipper_backup/__init__.py | 23 +++++++ .../klipper_backup_extension.py | 68 +++++++++++++++++++ kiauh/extensions/klipper_backup/metadata.json | 10 +++ 3 files changed, 101 insertions(+) create mode 100644 kiauh/extensions/klipper_backup/__init__.py create mode 100644 kiauh/extensions/klipper_backup/klipper_backup_extension.py create mode 100644 kiauh/extensions/klipper_backup/metadata.json diff --git a/kiauh/extensions/klipper_backup/__init__.py b/kiauh/extensions/klipper_backup/__init__.py new file mode 100644 index 0000000..8ac0b48 --- /dev/null +++ b/kiauh/extensions/klipper_backup/__init__.py @@ -0,0 +1,23 @@ +# ======================================================================= # +# Copyright (C) 2023 - 2024 Staubgeborener # +# https://github.com/Staubgeborener/klipper-backup # +# # +# 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 + +EXT_MODULE_NAME = "klipper_backup_extension.py" +MODULE_PATH = Path(__file__).resolve().parent +MODULE_ASSETS = MODULE_PATH.joinpath("assets") +KLIPPER_DIR = Path.home().joinpath("klipper-backup") +KLIPPERBACKUP_DIR = Path.home().joinpath("klipper-backup") +KLIPPERBACKUP_CONFIG_DIR = Path.home().joinpath("config_backup") +DEFAULT_KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" +KLIPPER_EXTRAS = KLIPPER_DIR.joinpath("klippy/extras") +EXTENSION_SRC = MODULE_ASSETS.joinpath(EXT_MODULE_NAME) +EXTENSION_TARGET_PATH = KLIPPERBACKUP_DIR +EXAMPLE_CFG_SRC = MODULE_ASSETS.joinpath("shell_command.cfg") diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py new file mode 100644 index 0000000..c40f2e6 --- /dev/null +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -0,0 +1,68 @@ +# ======================================================================= # +# 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 os +import shutil +import subprocess +from typing import List + +from components.klipper.klipper import Klipper +from core.backup_manager.backup_manager import BackupManager +from extensions.base_extension import BaseExtension +from core.config_manager.config_manager import ConfigManager +from core.instance_manager.instance_manager import InstanceManager +from extensions.klipper_backup import ( + EXTENSION_TARGET_PATH, + EXTENSION_SRC, + KLIPPER_DIR, + DEFAULT_KLIPPERBACKUP_REPO_URL, + KLIPPERBACKUP_DIR, + KLIPPERBACKUP_CONFIG_DIR, + EXAMPLE_CFG_SRC, + KLIPPER_EXTRAS, +) +from utils.filesystem_utils import check_file_exist +from utils.input_utils import get_confirm +from utils.logger import Logger + + +# noinspection PyMethodMayBeStatic +class KlipperbackupExtension(BaseExtension): + def install_extension(self, **kwargs) -> None: + if not KLIPPERBACKUP_DIR.exists(): + subprocess.run(["git", "clone", str(DEFAULT_KLIPPERBACKUP_REPO_URL), str(KLIPPERBACKUP_DIR)]) + subprocess.run(["git", "-C", str(KLIPPERBACKUP_DIR), "checkout", "installer-dev"]) + subprocess.run(["chmod", "+x", str(KLIPPERBACKUP_DIR / "install.sh")]) + subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "install_repo"]) + + def update_extension(self, **kwargs) -> None: + extension_installed = check_file_exist(EXTENSION_TARGET_PATH) + if not extension_installed: + Logger.print_info("Extension does not seem to be installed! Skipping ...") + return + else: + subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "check_updates"]) + + def remove_extension(self, **kwargs) -> None: + extension_installed = check_file_exist(EXTENSION_TARGET_PATH) + if not extension_installed: + Logger.print_info("Extension does not seem to be installed! Skipping ...") + return + + question = "Do you really want to remove the extension?" + if get_confirm(question, True, False): + try: + Logger.print_status(f"Removing '{EXTENSION_TARGET_PATH}' ...") + shutil.rmtree(EXTENSION_TARGET_PATH) + config_backup_exists = check_file_exist(KLIPPERBACKUP_CONFIG_DIR) + if config_backup_exists: + shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) + Logger.print_ok("Extension successfully removed!") + except OSError as e: + Logger.print_error(f"Unable to remove extension: {e}") diff --git a/kiauh/extensions/klipper_backup/metadata.json b/kiauh/extensions/klipper_backup/metadata.json new file mode 100644 index 0000000..9e5ef7e --- /dev/null +++ b/kiauh/extensions/klipper_backup/metadata.json @@ -0,0 +1,10 @@ +{ + "metadata": { + "index": 3, + "module": "klipper_backup_extension", + "maintained_by": "Staubgeborener", + "display_name": "Klipper-Backup", + "description": "Backup all your klipper files in GitHub", + "updates": true + } +} -- 2.39.5 From 813c74facbd69fde5101a9ee75a69c4976a154c5 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sat, 13 Apr 2024 15:44:25 +0200 Subject: [PATCH 02/27] remove unused imports and change copyright notes --- kiauh/extensions/klipper_backup/__init__.py | 6 +----- .../klipper_backup/klipper_backup_extension.py | 13 ++----------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/kiauh/extensions/klipper_backup/__init__.py b/kiauh/extensions/klipper_backup/__init__.py index 8ac0b48..fa3e1a9 100644 --- a/kiauh/extensions/klipper_backup/__init__.py +++ b/kiauh/extensions/klipper_backup/__init__.py @@ -12,12 +12,8 @@ from pathlib import Path EXT_MODULE_NAME = "klipper_backup_extension.py" MODULE_PATH = Path(__file__).resolve().parent -MODULE_ASSETS = MODULE_PATH.joinpath("assets") -KLIPPER_DIR = Path.home().joinpath("klipper-backup") KLIPPERBACKUP_DIR = Path.home().joinpath("klipper-backup") KLIPPERBACKUP_CONFIG_DIR = Path.home().joinpath("config_backup") DEFAULT_KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" -KLIPPER_EXTRAS = KLIPPER_DIR.joinpath("klippy/extras") EXTENSION_SRC = MODULE_ASSETS.joinpath(EXT_MODULE_NAME) -EXTENSION_TARGET_PATH = KLIPPERBACKUP_DIR -EXAMPLE_CFG_SRC = MODULE_ASSETS.joinpath("shell_command.cfg") +EXTENSION_TARGET_PATH = KLIPPERBACKUP_DIR \ No newline at end of file diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index c40f2e6..68be4e6 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -1,5 +1,6 @@ # ======================================================================= # -# Copyright (C) 2020 - 2024 Dominik Willner # +# Copyright (C) 2023 - 2024 Staubgeborener # +# https://github.com/Staubgeborener/klipper-backup # # # # This file is part of KIAUH - Klipper Installation And Update Helper # # https://github.com/dw-0/kiauh # @@ -7,25 +8,15 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # -import os import shutil import subprocess -from typing import List -from components.klipper.klipper import Klipper -from core.backup_manager.backup_manager import BackupManager from extensions.base_extension import BaseExtension -from core.config_manager.config_manager import ConfigManager -from core.instance_manager.instance_manager import InstanceManager from extensions.klipper_backup import ( EXTENSION_TARGET_PATH, - EXTENSION_SRC, - KLIPPER_DIR, DEFAULT_KLIPPERBACKUP_REPO_URL, KLIPPERBACKUP_DIR, KLIPPERBACKUP_CONFIG_DIR, - EXAMPLE_CFG_SRC, - KLIPPER_EXTRAS, ) from utils.filesystem_utils import check_file_exist from utils.input_utils import get_confirm -- 2.39.5 From fa88ae5ea31d802d14ae36af524558068063f9d5 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sat, 13 Apr 2024 15:50:25 +0200 Subject: [PATCH 03/27] remove unused variables --- kiauh/extensions/klipper_backup/__init__.py | 4 +--- .../klipper_backup/klipper_backup_extension.py | 9 ++++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/kiauh/extensions/klipper_backup/__init__.py b/kiauh/extensions/klipper_backup/__init__.py index fa3e1a9..a774534 100644 --- a/kiauh/extensions/klipper_backup/__init__.py +++ b/kiauh/extensions/klipper_backup/__init__.py @@ -14,6 +14,4 @@ EXT_MODULE_NAME = "klipper_backup_extension.py" MODULE_PATH = Path(__file__).resolve().parent KLIPPERBACKUP_DIR = Path.home().joinpath("klipper-backup") KLIPPERBACKUP_CONFIG_DIR = Path.home().joinpath("config_backup") -DEFAULT_KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" -EXTENSION_SRC = MODULE_ASSETS.joinpath(EXT_MODULE_NAME) -EXTENSION_TARGET_PATH = KLIPPERBACKUP_DIR \ No newline at end of file +KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" \ No newline at end of file diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 68be4e6..a40d76d 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -13,7 +13,6 @@ import subprocess from extensions.base_extension import BaseExtension from extensions.klipper_backup import ( - EXTENSION_TARGET_PATH, DEFAULT_KLIPPERBACKUP_REPO_URL, KLIPPERBACKUP_DIR, KLIPPERBACKUP_CONFIG_DIR, @@ -33,7 +32,7 @@ class KlipperbackupExtension(BaseExtension): subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "install_repo"]) def update_extension(self, **kwargs) -> None: - extension_installed = check_file_exist(EXTENSION_TARGET_PATH) + extension_installed = check_file_exist(KLIPPERBACKUP_DIR) if not extension_installed: Logger.print_info("Extension does not seem to be installed! Skipping ...") return @@ -41,7 +40,7 @@ class KlipperbackupExtension(BaseExtension): subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "check_updates"]) def remove_extension(self, **kwargs) -> None: - extension_installed = check_file_exist(EXTENSION_TARGET_PATH) + extension_installed = check_file_exist(KLIPPERBACKUP_DIR) if not extension_installed: Logger.print_info("Extension does not seem to be installed! Skipping ...") return @@ -49,8 +48,8 @@ class KlipperbackupExtension(BaseExtension): question = "Do you really want to remove the extension?" if get_confirm(question, True, False): try: - Logger.print_status(f"Removing '{EXTENSION_TARGET_PATH}' ...") - shutil.rmtree(EXTENSION_TARGET_PATH) + Logger.print_status(f"Removing '{KLIPPERBACKUP_DIR}' ...") + shutil.rmtree(KLIPPERBACKUP_DIR) config_backup_exists = check_file_exist(KLIPPERBACKUP_CONFIG_DIR) if config_backup_exists: shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) -- 2.39.5 From 8a622aa6eea764096a1700189683d0f554bf7a6e Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sat, 13 Apr 2024 15:51:48 +0200 Subject: [PATCH 04/27] DEFAULT_KLIPPERBACKUP_REPO_URL -> KLIPPERBACKUP_REPO_URL --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index a40d76d..c8266a7 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -13,7 +13,7 @@ import subprocess from extensions.base_extension import BaseExtension from extensions.klipper_backup import ( - DEFAULT_KLIPPERBACKUP_REPO_URL, + KLIPPERBACKUP_REPO_URL, KLIPPERBACKUP_DIR, KLIPPERBACKUP_CONFIG_DIR, ) @@ -26,7 +26,7 @@ from utils.logger import Logger class KlipperbackupExtension(BaseExtension): def install_extension(self, **kwargs) -> None: if not KLIPPERBACKUP_DIR.exists(): - subprocess.run(["git", "clone", str(DEFAULT_KLIPPERBACKUP_REPO_URL), str(KLIPPERBACKUP_DIR)]) + subprocess.run(["git", "clone", str(KLIPPERBACKUP_REPO_URL), str(KLIPPERBACKUP_DIR)]) subprocess.run(["git", "-C", str(KLIPPERBACKUP_DIR), "checkout", "installer-dev"]) subprocess.run(["chmod", "+x", str(KLIPPERBACKUP_DIR / "install.sh")]) subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "install_repo"]) -- 2.39.5 From 0736ca260c9395fad25f4e43a2cdd19416221d22 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sun, 14 Apr 2024 12:11:11 +0200 Subject: [PATCH 05/27] Check for service files and remove them --- .../klipper_backup_extension.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index c8266a7..daeab40 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -8,6 +8,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # +import os import shutil import subprocess @@ -40,6 +41,26 @@ class KlipperbackupExtension(BaseExtension): subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "check_updates"]) def remove_extension(self, **kwargs) -> None: + + def is_service_installed(service_name): + try: + # Führe den Befehl aus und leite stdout und stderr in den Null-Geräte + with open(os.devnull, 'w') as devnull: + subprocess.run(["systemctl", "status", service_name], stdout=devnull, stderr=devnull, check=True) + return True + except subprocess.CalledProcessError: + # Wenn ein Fehler auftritt, bedeutet das, dass der Dienst nicht installiert ist + return False + + def uninstall_service(service_name): + try: + # Deinstalliere den Dienst, indem du den Befehl "systemctl disable" und "systemctl stop" ausführst + subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) + subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) + Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") + except subprocess.CalledProcessError: + Logger.print_error(f"Error uninstalling the service {service_name}.") + extension_installed = check_file_exist(KLIPPERBACKUP_DIR) if not extension_installed: Logger.print_info("Extension does not seem to be installed! Skipping ...") @@ -56,3 +77,17 @@ class KlipperbackupExtension(BaseExtension): Logger.print_ok("Extension successfully removed!") except OSError as e: Logger.print_error(f"Unable to remove extension: {e}") + + service_name = "klipper-backup-on-boot.service" + + if is_service_installed(service_name): + uninstall_service(service_name) + else: + Logger.print_info(f"The service {service_name} is not installed. Skipping ...") + + service_name = "klipper-backup-filewatch.service" + + if is_service_installed(service_name): + uninstall_service(service_name) + else: + Logger.print_info(f"The service {service_name} is not installed. Skipping ...") \ No newline at end of file -- 2.39.5 From fc380d33c46332366975905bc8b801283fc170a4 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sun, 14 Apr 2024 12:12:24 +0200 Subject: [PATCH 06/27] remove comments --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index daeab40..067bc5d 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -44,17 +44,14 @@ class KlipperbackupExtension(BaseExtension): def is_service_installed(service_name): try: - # Führe den Befehl aus und leite stdout und stderr in den Null-Geräte with open(os.devnull, 'w') as devnull: subprocess.run(["systemctl", "status", service_name], stdout=devnull, stderr=devnull, check=True) return True except subprocess.CalledProcessError: - # Wenn ein Fehler auftritt, bedeutet das, dass der Dienst nicht installiert ist return False def uninstall_service(service_name): try: - # Deinstalliere den Dienst, indem du den Befehl "systemctl disable" und "systemctl stop" ausführst subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") -- 2.39.5 From 7a7c9eb25308db5938c4f4d1592f79186b57edb4 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sun, 14 Apr 2024 13:44:15 +0200 Subject: [PATCH 07/27] put system service files in array --- .../klipper_backup_extension.py | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 067bc5d..c9084ac 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -30,7 +30,7 @@ class KlipperbackupExtension(BaseExtension): subprocess.run(["git", "clone", str(KLIPPERBACKUP_REPO_URL), str(KLIPPERBACKUP_DIR)]) subprocess.run(["git", "-C", str(KLIPPERBACKUP_DIR), "checkout", "installer-dev"]) subprocess.run(["chmod", "+x", str(KLIPPERBACKUP_DIR / "install.sh")]) - subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "install_repo"]) + subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh")]) def update_extension(self, **kwargs) -> None: extension_installed = check_file_exist(KLIPPERBACKUP_DIR) @@ -38,25 +38,30 @@ class KlipperbackupExtension(BaseExtension): Logger.print_info("Extension does not seem to be installed! Skipping ...") return else: - subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "kiauh", "check_updates"]) + subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "check_updates"]) def remove_extension(self, **kwargs) -> None: - def is_service_installed(service_name): - try: - with open(os.devnull, 'w') as devnull: - subprocess.run(["systemctl", "status", service_name], stdout=devnull, stderr=devnull, check=True) - return True - except subprocess.CalledProcessError: - return False + def is_service_installed(service_names): + installed = True + for service_name in service_names: + try: + with open(os.devnull, 'w') as devnull: + subprocess.run(["systemctl", "status", service_name], stdout=devnull, stderr=devnull, check=True) + except subprocess.CalledProcessError: + installed = False + break + return installed + + def uninstall_service(service_names): + for service_name in service_names: + try: + subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) + subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) + Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") + except subprocess.CalledProcessError: + Logger.print_error(f"Error uninstalling the service {service_name}.") - def uninstall_service(service_name): - try: - subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) - subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) - Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") - except subprocess.CalledProcessError: - Logger.print_error(f"Error uninstalling the service {service_name}.") extension_installed = check_file_exist(KLIPPERBACKUP_DIR) if not extension_installed: @@ -75,16 +80,10 @@ class KlipperbackupExtension(BaseExtension): except OSError as e: Logger.print_error(f"Unable to remove extension: {e}") - service_name = "klipper-backup-on-boot.service" + service_names = ["klipper-backup-on-boot.service", "klipper-backup-filewatch.service"] - if is_service_installed(service_name): - uninstall_service(service_name) - else: - Logger.print_info(f"The service {service_name} is not installed. Skipping ...") - - service_name = "klipper-backup-filewatch.service" - - if is_service_installed(service_name): - uninstall_service(service_name) - else: - Logger.print_info(f"The service {service_name} is not installed. Skipping ...") \ No newline at end of file + for service_name in service_names: + if is_service_installed(service_name): + uninstall_service(service_name) + else: + Logger.print_info(f"The service {service_name} is not installed. Skipping ...") \ No newline at end of file -- 2.39.5 From 79c3c4d327ab6b8c461e30083cc401dcbbc4b5d5 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sun, 14 Apr 2024 13:53:25 +0200 Subject: [PATCH 08/27] add Tylerjet to copyright note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't forget the other dude ¯\_(ツ)_/¯ --- kiauh/extensions/klipper_backup/__init__.py | 2 +- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kiauh/extensions/klipper_backup/__init__.py b/kiauh/extensions/klipper_backup/__init__.py index a774534..61e1c61 100644 --- a/kiauh/extensions/klipper_backup/__init__.py +++ b/kiauh/extensions/klipper_backup/__init__.py @@ -1,5 +1,5 @@ # ======================================================================= # -# Copyright (C) 2023 - 2024 Staubgeborener # +# Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet # # https://github.com/Staubgeborener/klipper-backup # # # # This file is part of KIAUH - Klipper Installation And Update Helper # diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index c9084ac..5e2a81d 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -1,5 +1,5 @@ # ======================================================================= # -# Copyright (C) 2023 - 2024 Staubgeborener # +# Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet # # https://github.com/Staubgeborener/klipper-backup # # # # This file is part of KIAUH - Klipper Installation And Update Helper # -- 2.39.5 From e4d539f7304ef17dde57a5fbdaa4438fef3188e9 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Sun, 14 Apr 2024 15:46:30 +0200 Subject: [PATCH 09/27] remove klipper-backup cron --- .../klipper_backup_extension.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 5e2a81d..c051481 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -1,5 +1,5 @@ # ======================================================================= # -# Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet # +# Copyright (C) 2023 - 2024 Staubgeborener # # https://github.com/Staubgeborener/klipper-backup # # # # This file is part of KIAUH - Klipper Installation And Update Helper # @@ -62,6 +62,16 @@ class KlipperbackupExtension(BaseExtension): except subprocess.CalledProcessError: Logger.print_error(f"Error uninstalling the service {service_name}.") + def check_crontab_entry(entry): + try: + crontab_content = subprocess.check_output(["crontab", "-l"], stderr=subprocess.DEVNULL, text=True) + except subprocess.CalledProcessError: + return False + + for line in crontab_content.splitlines(): + if entry in line: + return True + return False extension_installed = check_file_exist(KLIPPERBACKUP_DIR) if not extension_installed: @@ -80,10 +90,20 @@ class KlipperbackupExtension(BaseExtension): except OSError as e: Logger.print_error(f"Unable to remove extension: {e}") + # Remove Klipper-Backup services service_names = ["klipper-backup-on-boot.service", "klipper-backup-filewatch.service"] for service_name in service_names: if is_service_installed(service_name): uninstall_service(service_name) else: - Logger.print_info(f"The service {service_name} is not installed. Skipping ...") \ No newline at end of file + Logger.print_info(f"The service {service_name} is not installed. Skipping ...") + + # Remove Klipper-Backup cron + entry_to_check = "$HOME/klipper-backup/script.sh" + if check_crontab_entry(entry_to_check): + command = "crontab -l | grep -v '$HOME/klipper-backup/script.sh' | crontab -" + subprocess.run(command, shell=True, check=True) + Logger.print_ok("Der Eintrag wurde aus dem crontab entfernt.") + else: + Logger.print_info("Der Eintrag ist nicht im crontab vorhanden. Skipping ...") -- 2.39.5 From da4c5fe1096425b5783bdfdc21db494230307a4a Mon Sep 17 00:00:00 2001 From: dw-0 Date: Sun, 14 Apr 2024 22:11:40 +0200 Subject: [PATCH 10/27] refactor: rework of menu lifecycle and option handling Signed-off-by: Dominik Willner --- .../klipper/menus/klipper_remove_menu.py | 33 +++-- .../klipper_firmware/flash_utils.py | 131 ------------------ .../log_uploads/menus/log_upload_menu.py | 21 ++- .../moonraker/menus/moonraker_remove_menu.py | 34 +++-- .../webui_client/menus/client_remove_menu.py | 31 +++-- kiauh/core/menus/__init__.py | 26 ++-- kiauh/core/menus/backup_menu.py | 39 ++++-- kiauh/core/menus/base_menu.py | 71 +++++----- kiauh/core/menus/install_menu.py | 28 ++-- kiauh/core/menus/main_menu.py | 45 +++--- kiauh/core/menus/remove_menu.py | 38 ++--- kiauh/core/menus/settings_menu.py | 13 +- kiauh/core/menus/update_menu.py | 41 +++--- kiauh/extensions/extensions_menu.py | 52 ++++--- 14 files changed, 290 insertions(+), 313 deletions(-) delete mode 100644 kiauh/components/klipper_firmware/flash_utils.py diff --git a/kiauh/components/klipper/menus/klipper_remove_menu.py b/kiauh/components/klipper/menus/klipper_remove_menu.py index dca26f9..d079265 100644 --- a/kiauh/components/klipper/menus/klipper_remove_menu.py +++ b/kiauh/components/klipper/menus/klipper_remove_menu.py @@ -8,34 +8,41 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.klipper import klipper_remove -from core.menus import FooterType +from core.menus import FooterType, Option from core.menus.base_menu import BaseMenu from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN # noinspection PyUnusedLocal class KlipperRemoveMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu - self.options = { - "0": self.toggle_all, - "1": self.toggle_remove_klipper_service, - "2": self.toggle_remove_klipper_dir, - "3": self.toggle_remove_klipper_env, - "4": self.toggle_delete_klipper_logs, - "c": self.run_removal_process, - } self.footer_type = FooterType.BACK_HELP - self.remove_klipper_service = False self.remove_klipper_dir = False self.remove_klipper_env = False self.delete_klipper_logs = False + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.remove_menu import RemoveMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else RemoveMenu + ) + + def set_options(self) -> None: + self.options = { + "0": Option(method=self.toggle_all, menu=False), + "1": Option(method=self.toggle_remove_klipper_service, menu=False), + "2": Option(method=self.toggle_remove_klipper_dir, menu=False), + "3": Option(method=self.toggle_remove_klipper_env, menu=False), + "4": Option(method=self.toggle_delete_klipper_logs, menu=False), + "c": Option(method=self.run_removal_process, menu=False), + } + def print_menu(self) -> None: header = " [ Remove Klipper ] " color = COLOR_RED diff --git a/kiauh/components/klipper_firmware/flash_utils.py b/kiauh/components/klipper_firmware/flash_utils.py deleted file mode 100644 index b064e46..0000000 --- a/kiauh/components/klipper_firmware/flash_utils.py +++ /dev/null @@ -1,131 +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 subprocess -from subprocess import CalledProcessError, check_output, Popen, PIPE, STDOUT -from typing import List - -from components.klipper import KLIPPER_DIR -from components.klipper_firmware import SD_FLASH_SCRIPT -from components.klipper_firmware.flash_options import ( - FlashOptions, - FlashMethod, -) -from utils.logger import Logger -from utils.system_utils import log_process - - -def find_firmware_file(method: FlashMethod) -> bool: - target = KLIPPER_DIR.joinpath("out") - target_exists = target.exists() - if method is FlashMethod.REGULAR: - f1 = "klipper.elf.hex" - f2 = "klipper.elf" - fw_file_exists = target.joinpath(f1).exists() and target.joinpath(f2).exists() - elif method is FlashMethod.SD_CARD: - fw_file_exists = target.joinpath("klipper.bin").exists() - else: - raise Exception("Unknown flash method") - - return target_exists and fw_file_exists - - -def find_usb_device_by_id() -> List[str]: - try: - command = "find /dev/serial/by-id/* 2>/dev/null" - output = check_output(command, shell=True, text=True) - return output.splitlines() - except CalledProcessError as e: - Logger.print_error("Unable to find a USB device!") - Logger.print_error(e, prefix=False) - return [] - - -def find_uart_device() -> List[str]: - try: - command = '"find /dev -maxdepth 1 -regextype posix-extended -regex "^\/dev\/tty(AMA0|S0)$" 2>/dev/null"' - output = check_output(command, shell=True, text=True) - return output.splitlines() - except CalledProcessError as e: - Logger.print_error("Unable to find a UART device!") - Logger.print_error(e, prefix=False) - return [] - - -def find_usb_dfu_device() -> List[str]: - try: - command = '"lsusb | grep "DFU" | cut -d " " -f 6 2>/dev/null"' - output = check_output(command, shell=True, text=True) - return output.splitlines() - except CalledProcessError as e: - Logger.print_error("Unable to find a USB DFU device!") - Logger.print_error(e, prefix=False) - return [] - - -def get_sd_flash_board_list() -> List[str]: - if not KLIPPER_DIR.exists() or not SD_FLASH_SCRIPT.exists(): - return [] - - try: - cmd = f"{SD_FLASH_SCRIPT} -l" - blist = subprocess.check_output(cmd, shell=True, text=True) - return blist.splitlines()[1:] - except subprocess.CalledProcessError as e: - Logger.print_error(f"An unexpected error occured:\n{e}") - - -def start_flash_process(flash_options: FlashOptions) -> None: - Logger.print_status(f"Flashing '{flash_options.selected_mcu}' ...") - try: - if not flash_options.flash_method: - raise Exception("Missing value for flash_method!") - if not flash_options.flash_command: - raise Exception("Missing value for flash_command!") - if not flash_options.selected_mcu: - raise Exception("Missing value for selected_mcu!") - if not flash_options.connection_type: - raise Exception("Missing value for connection_type!") - if ( - flash_options.flash_method == FlashMethod.SD_CARD - and not flash_options.selected_board - ): - raise Exception("Missing value for selected_board!") - - if flash_options.flash_method is FlashMethod.REGULAR: - cmd = [ - "make", - flash_options.flash_command.value, - f"FLASH_DEVICE={flash_options.selected_mcu}", - ] - elif flash_options.flash_method is FlashMethod.SD_CARD: - if not SD_FLASH_SCRIPT.exists(): - raise Exception("Unable to find Klippers sdcard flash script!") - cmd = [ - SD_FLASH_SCRIPT, - "-b", - flash_options.selected_baudrate, - flash_options.selected_mcu, - flash_options.selected_board, - ] - else: - raise Exception("Invalid value for flash_method!") - - process = Popen(cmd, cwd=KLIPPER_DIR, stdout=PIPE, stderr=STDOUT, text=True) - log_process(process) - - rc = process.returncode - if rc != 0: - raise Exception(f"Flashing failed with returncode: {rc}") - else: - Logger.print_ok("Flashing successfull!", start="\n", end="\n\n") - - except (Exception, CalledProcessError): - Logger.print_error("Flashing failed!", start="\n") - Logger.print_error("See the console output above!", end="\n\n") diff --git a/kiauh/components/log_uploads/menus/log_upload_menu.py b/kiauh/components/log_uploads/menus/log_upload_menu.py index 35ec143..3129c52 100644 --- a/kiauh/components/log_uploads/menus/log_upload_menu.py +++ b/kiauh/components/log_uploads/menus/log_upload_menu.py @@ -8,22 +8,33 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.log_uploads.log_upload_utils import get_logfile_list from components.log_uploads.log_upload_utils import upload_logfile +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import RESET_FORMAT, COLOR_YELLOW # noinspection PyMethodMayBeStatic class LogUploadMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self): super().__init__() - - self.previous_menu: BaseMenu = previous_menu self.logfile_list = get_logfile_list() - options = {f"{index}": self.upload for index in range(len(self.logfile_list))} - self.options = options + + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self) -> None: + self.options = { + f"{index}": Option(self.upload, False, opt_index=f"{index}") + for index in range(len(self.logfile_list)) + } def print_menu(self): header = " [ Log Upload ] " diff --git a/kiauh/components/moonraker/menus/moonraker_remove_menu.py b/kiauh/components/moonraker/menus/moonraker_remove_menu.py index 6a55078..a72b61c 100644 --- a/kiauh/components/moonraker/menus/moonraker_remove_menu.py +++ b/kiauh/components/moonraker/menus/moonraker_remove_menu.py @@ -8,34 +8,42 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.moonraker import moonraker_remove +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN # noinspection PyUnusedLocal class MoonrakerRemoveMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu - self.options = { - "0": self.toggle_all, - "1": self.toggle_remove_moonraker_service, - "2": self.toggle_remove_moonraker_dir, - "3": self.toggle_remove_moonraker_env, - "4": self.toggle_remove_moonraker_polkit, - "5": self.toggle_delete_moonraker_logs, - "c": self.run_removal_process, - } - self.remove_moonraker_service = False self.remove_moonraker_dir = False self.remove_moonraker_env = False self.remove_moonraker_polkit = False self.delete_moonraker_logs = False + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.remove_menu import RemoveMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else RemoveMenu + ) + + def set_options(self) -> None: + self.options = { + "0": Option(method=self.toggle_all, menu=False), + "1": Option(method=self.toggle_remove_moonraker_service, menu=False), + "2": Option(method=self.toggle_remove_moonraker_dir, menu=False), + "3": Option(method=self.toggle_remove_moonraker_env, menu=False), + "4": Option(method=self.toggle_remove_moonraker_polkit, menu=False), + "5": Option(method=self.toggle_delete_moonraker_logs, menu=False), + "c": Option(method=self.run_removal_process, menu=False), + } + def print_menu(self) -> None: header = " [ Remove Moonraker ] " color = COLOR_RED diff --git a/kiauh/components/webui_client/menus/client_remove_menu.py b/kiauh/components/webui_client/menus/client_remove_menu.py index f6e99a0..ed11e0a 100644 --- a/kiauh/components/webui_client/menus/client_remove_menu.py +++ b/kiauh/components/webui_client/menus/client_remove_menu.py @@ -8,35 +8,42 @@ # ======================================================================= # import textwrap -from typing import Callable, Dict +from typing import Dict, Type, Optional from components.webui_client import client_remove from components.webui_client.base_data import BaseWebClient, WebClientType +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN # noinspection PyUnusedLocal class ClientRemoveMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu, client: BaseWebClient): + def __init__( + self, client: BaseWebClient, previous_menu: Optional[Type[BaseMenu]] = None + ): super().__init__() - self.previous_menu = previous_menu - self.options = self.get_options(client) - self.client = client self.rm_client = False self.rm_client_config = False self.backup_mainsail_config_json = False - def get_options(self, client: BaseWebClient) -> Dict[str, Callable]: + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.remove_menu import RemoveMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else RemoveMenu + ) + + def set_options(self) -> Dict[str, Option]: options = { - "0": self.toggle_all, - "1": self.toggle_rm_client, - "2": self.toggle_rm_client_config, - "c": self.run_removal_process, + "0": Option(method=self.toggle_all, menu=False), + "1": Option(method=self.toggle_rm_client, menu=False), + "2": Option(method=self.toggle_rm_client_config, menu=False), + "c": Option(method=self.run_removal_process, menu=False), } - if client.client == WebClientType.MAINSAIL: - options["3"] = self.toggle_backup_mainsail_config_json + if self.client.client == WebClientType.MAINSAIL: + options["3"] = Option(self.toggle_backup_mainsail_config_json, False) return options diff --git a/kiauh/core/menus/__init__.py b/kiauh/core/menus/__init__.py index 06c68b2..8102e76 100644 --- a/kiauh/core/menus/__init__.py +++ b/kiauh/core/menus/__init__.py @@ -7,7 +7,25 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # +from dataclasses import dataclass from enum import Enum +from typing import Callable, Any, Union + + +@dataclass +class Option: + """ + Represents a menu option. + :param method: Method that will be used to call the menu option + :param menu: Flag for singaling that another menu will be opened + :param opt_index: Can be used to pass the user input to the menu option + :param opt_data: Can be used to pass any additional data to the menu option + """ + + method: Union[Callable, None] = None + menu: bool = False + opt_index: str = "" + opt_data: Any = None class FooterType(Enum): @@ -15,11 +33,3 @@ class FooterType(Enum): BACK = "BACK" BACK_HELP = "BACK_HELP" BLANK = "BLANK" - - -class ExitAppException(Exception): - pass - - -class GoBackException(Exception): - pass diff --git a/kiauh/core/menus/backup_menu.py b/kiauh/core/menus/backup_menu.py index 72f1735..0eb8698 100644 --- a/kiauh/core/menus/backup_menu.py +++ b/kiauh/core/menus/backup_menu.py @@ -8,6 +8,7 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.klipper.klipper_utils import backup_klipper_dir from components.moonraker.moonraker_utils import ( @@ -20,6 +21,7 @@ from components.webui_client.client_utils import ( ) from components.webui_client.fluidd_data import FluiddData from components.webui_client.mainsail_data import MainsailData +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.common import backup_printer_config_dir from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW @@ -28,20 +30,27 @@ from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class BackupMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self) -> None: self.options = { - "1": self.backup_klipper, - "2": self.backup_moonraker, - "3": self.backup_printer_config, - "4": self.backup_moonraker_db, - "5": self.backup_mainsail, - "6": self.backup_fluidd, - "7": self.backup_mainsail_config, - "8": self.backup_fluidd_config, - "9": self.backup_klipperscreen, + "1": Option(method=self.backup_klipper, menu=False), + "2": Option(method=self.backup_moonraker, menu=False), + "3": Option(method=self.backup_printer_config, menu=False), + "4": Option(method=self.backup_moonraker_db, menu=False), + "5": Option(method=self.backup_mainsail, menu=False), + "6": Option(method=self.backup_fluidd, menu=False), + "7": Option(method=self.backup_mainsail_config, menu=False), + "8": Option(method=self.backup_fluidd_config, menu=False), + "9": Option(method=self.backup_klipperscreen, menu=False), } def print_menu(self): @@ -82,16 +91,16 @@ class BackupMenu(BaseMenu): backup_moonraker_db_dir() def backup_mainsail(self, **kwargs): - backup_client_data(MainsailData().get()) + backup_client_data(MainsailData()) def backup_fluidd(self, **kwargs): - backup_client_data(FluiddData().get()) + backup_client_data(FluiddData()) def backup_mainsail_config(self, **kwargs): - backup_client_config_data(MainsailData().get()) + backup_client_config_data(MainsailData()) def backup_fluidd_config(self, **kwargs): - backup_client_config_data(FluiddData().get()) + backup_client_config_data(FluiddData()) def backup_klipperscreen(self, **kwargs): pass diff --git a/kiauh/core/menus/base_menu.py b/kiauh/core/menus/base_menu.py index 84f4a13..2d5e1f8 100644 --- a/kiauh/core/menus/base_menu.py +++ b/kiauh/core/menus/base_menu.py @@ -13,9 +13,9 @@ import subprocess import sys import textwrap from abc import abstractmethod -from typing import Dict, Union, Callable, Type, Tuple +from typing import Type, Dict, Optional -from core.menus import FooterType, ExitAppException, GoBackException +from core.menus import FooterType, Option from utils.constants import ( COLOR_GREEN, COLOR_YELLOW, @@ -106,12 +106,12 @@ class PostInitCaller(type): # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class BaseMenu(metaclass=PostInitCaller): - options: Dict[str, Callable] = {} + options: Dict[str, Option] = {} options_offset: int = 0 - default_option: Union[Callable, None] = None + default_option: Option = None input_label_txt: str = "Perform action" header: bool = False - previous_menu: Union[Type[BaseMenu], BaseMenu] = None + previous_menu: Type[BaseMenu] = None help_menu: Type[BaseMenu] = None footer_type: FooterType = FooterType.BACK @@ -120,30 +120,42 @@ class BaseMenu(metaclass=PostInitCaller): raise NotImplementedError("BaseMenu cannot be instantiated directly.") def __post_init__(self): + self.set_previous_menu(self.previous_menu) + self.set_options() + # conditionally add options based on footer type if self.footer_type is FooterType.QUIT: - self.options["q"] = self.__exit + self.options["q"] = Option(method=self.__exit, menu=False) if self.footer_type is FooterType.BACK: - self.options["b"] = self.__go_back + self.options["b"] = Option(method=self.__go_back, menu=False) if self.footer_type is FooterType.BACK_HELP: - self.options["b"] = self.__go_back - self.options["h"] = self.__go_to_help + self.options["b"] = Option(method=self.__go_back, menu=False) + self.options["h"] = Option(method=self.__go_to_help, menu=False) # if defined, add the default option to the options dict if self.default_option is not None: self.options[""] = self.default_option def __go_back(self, **kwargs): - raise GoBackException() + self.previous_menu().run() def __go_to_help(self, **kwargs): self.help_menu(previous_menu=self).run() def __exit(self, **kwargs): - raise ExitAppException() + Logger.print_ok("###### Happy printing!", False) + sys.exit(0) + + @abstractmethod + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + raise NotImplementedError + + @abstractmethod + def set_options(self) -> None: + raise NotImplementedError @abstractmethod def print_menu(self) -> None: - raise NotImplementedError("Subclasses must implement the print_menu method") + raise NotImplementedError def print_footer(self) -> None: if self.footer_type is FooterType.QUIT: @@ -155,53 +167,50 @@ class BaseMenu(metaclass=PostInitCaller): elif self.footer_type is FooterType.BLANK: print_blank_footer() else: - raise NotImplementedError("Method for printing footer not implemented.") + raise NotImplementedError def display_menu(self) -> None: - # clear() if self.header: print_header() self.print_menu() self.print_footer() - def validate_user_input(self, usr_input: str) -> Tuple[Callable, str]: + def validate_user_input(self, usr_input: str) -> Option: """ Validate the user input and either return an Option, a string or None :param usr_input: The user input in form of a string :return: Option, str or None """ usr_input = usr_input.lower() - option = self.options.get(usr_input, None) + option = self.options.get(usr_input, Option(None, False, "", None)) # if option/usr_input is None/empty string, we execute the menus default option if specified if (option is None or usr_input == "") and self.default_option is not None: - return self.default_option, usr_input + self.default_option.opt_index = usr_input + return self.default_option # user selected a regular option - return option, usr_input + option.opt_index = usr_input + return option - def handle_user_input(self) -> Tuple[Callable, str]: + def handle_user_input(self) -> Option: """Handle the user input, return the validated input or print an error.""" while True: print(f"{COLOR_CYAN}###### {self.input_label_txt}: {RESET_FORMAT}", end="") usr_input = input().lower() validated_input = self.validate_user_input(usr_input) - method_to_call = validated_input[0] - if method_to_call is not None: + if validated_input.method is not None: return validated_input else: Logger.print_error("Invalid input!", False) def run(self) -> None: """Start the menu lifecycle. When this function returns, the lifecycle of the menu ends.""" - while True: - try: - self.display_menu() - option = self.handle_user_input() - option[0](opt_index=option[1]) - except GoBackException: - return - except ExitAppException: - Logger.print_ok("###### Happy printing!", False) - sys.exit(0) + try: + self.display_menu() + option = self.handle_user_input() + option.method(opt_index=option.opt_index, opt_data=option.opt_data) + self.run() + except Exception as e: + Logger.print_error(f"An unexpecred error occured:\n{e}") diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index 5db3846..ecf4f78 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -8,6 +8,7 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.klipper import klipper_setup from components.moonraker import moonraker_setup @@ -15,6 +16,7 @@ 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 from components.webui_client.mainsail_data import MainsailData +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import COLOR_GREEN, RESET_FORMAT @@ -23,20 +25,24 @@ from utils.constants import COLOR_GREEN, RESET_FORMAT # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class InstallMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self) -> None: self.options = { - "1": self.install_klipper, - "2": self.install_moonraker, - "3": self.install_mainsail, - "4": self.install_fluidd, - "5": self.install_mainsail_config, - "6": self.install_fluidd_config, - "7": None, - "8": None, - "9": None, + "1": Option(method=self.install_klipper, menu=False), + "2": Option(method=self.install_moonraker, menu=False), + "3": Option(method=self.install_mainsail, menu=False), + "4": Option(method=self.install_fluidd, menu=False), + "5": Option(method=self.install_mainsail_config, menu=False), + "6": Option(method=self.install_fluidd_config, menu=False), } def print_menu(self): diff --git a/kiauh/core/menus/main_menu.py b/kiauh/core/menus/main_menu.py index 5cf45fd..d347807 100644 --- a/kiauh/core/menus/main_menu.py +++ b/kiauh/core/menus/main_menu.py @@ -8,6 +8,7 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.klipper.klipper_utils import get_klipper_status from components.log_uploads.menus.log_upload_menu import LogUploadMenu @@ -21,7 +22,7 @@ from components.webui_client.mainsail_data import MainsailData from core.menus import FooterType from core.menus.advanced_menu import AdvancedMenu from core.menus.backup_menu import BackupMenu -from core.menus.base_menu import BaseMenu +from core.menus.base_menu import BaseMenu, Option from extensions.extensions_menu import ExtensionsMenu from core.menus.install_menu import InstallMenu from core.menus.remove_menu import RemoveMenu @@ -43,16 +44,6 @@ class MainMenu(BaseMenu): def __init__(self): super().__init__() - self.options = { - "0": self.log_upload_menu, - "1": self.install_menu, - "2": self.update_menu, - "3": self.remove_menu, - "4": self.advanced_menu, - "5": self.backup_menu, - "e": self.extension_menu, - "s": self.settings_menu, - } self.header = True self.footer_type = FooterType.QUIT @@ -68,6 +59,22 @@ class MainMenu(BaseMenu): self.cc_status = "" self.init_status() + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + """MainMenu does not have a previous menu""" + pass + + def set_options(self) -> None: + self.options = { + "0": Option(method=self.log_upload_menu, menu=True), + "1": Option(method=self.install_menu, menu=True), + "2": Option(method=self.update_menu, menu=True), + "3": Option(method=self.remove_menu, menu=True), + "4": Option(method=self.advanced_menu, menu=True), + "5": Option(method=self.backup_menu, menu=True), + "e": Option(method=self.extension_menu, menu=True), + "s": Option(method=self.settings_menu, menu=True), + } + def init_status(self) -> None: status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn"] for var in status_vars: @@ -140,25 +147,25 @@ class MainMenu(BaseMenu): print(menu, end="") def log_upload_menu(self, **kwargs): - LogUploadMenu(previous_menu=self).run() + LogUploadMenu().run() def install_menu(self, **kwargs): - InstallMenu(previous_menu=self).run() + InstallMenu(previous_menu=self.__class__).run() def update_menu(self, **kwargs): - UpdateMenu(previous_menu=self).run() + UpdateMenu(previous_menu=self.__class__).run() def remove_menu(self, **kwargs): - RemoveMenu(previous_menu=self).run() + RemoveMenu(previous_menu=self.__class__).run() def advanced_menu(self, **kwargs): - AdvancedMenu().run() + AdvancedMenu(previous_menu=self.__class__).run() def backup_menu(self, **kwargs): - BackupMenu(previous_menu=self).run() + BackupMenu(previous_menu=self.__class__).run() def settings_menu(self, **kwargs): - SettingsMenu(previous_menu=self).run() + SettingsMenu().run() def extension_menu(self, **kwargs): - ExtensionsMenu(previous_menu=self).run() + ExtensionsMenu(previous_menu=self.__class__).run() diff --git a/kiauh/core/menus/remove_menu.py b/kiauh/core/menus/remove_menu.py index 0ba8992..7d8ac75 100644 --- a/kiauh/core/menus/remove_menu.py +++ b/kiauh/core/menus/remove_menu.py @@ -8,6 +8,7 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu from components.moonraker.menus.moonraker_remove_menu import ( @@ -16,6 +17,7 @@ from components.moonraker.menus.moonraker_remove_menu import ( 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 +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import COLOR_RED, RESET_FORMAT @@ -23,24 +25,22 @@ from utils.constants import COLOR_RED, RESET_FORMAT # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class RemoveMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self): self.options = { - "1": self.remove_klipper, - "2": self.remove_moonraker, - "3": self.remove_mainsail, - "4": self.remove_fluidd, - "5": None, - "6": None, - "7": None, - "8": None, - "9": None, - "10": None, - "11": None, - "12": None, - "13": None, + "1": Option(method=self.remove_klipper, menu=True), + "2": Option(method=self.remove_moonraker, menu=True), + "3": Option(method=self.remove_mainsail, menu=True), + "4": Option(method=self.remove_fluidd, menu=True), } def print_menu(self): @@ -71,13 +71,13 @@ class RemoveMenu(BaseMenu): print(menu, end="") def remove_klipper(self, **kwargs): - KlipperRemoveMenu(previous_menu=self).run() + KlipperRemoveMenu(previous_menu=self.__class__).run() def remove_moonraker(self, **kwargs): - MoonrakerRemoveMenu(previous_menu=self).run() + MoonrakerRemoveMenu(previous_menu=self.__class__).run() def remove_mainsail(self, **kwargs): - ClientRemoveMenu(previous_menu=self, client=MainsailData()).run() + ClientRemoveMenu(previous_menu=self.__class__, client=MainsailData()).run() def remove_fluidd(self, **kwargs): - ClientRemoveMenu(previous_menu=self, client=FluiddData()).run() + ClientRemoveMenu(previous_menu=self.__class__, client=FluiddData()).run() diff --git a/kiauh/core/menus/settings_menu.py b/kiauh/core/menus/settings_menu.py index 9d28b14..d26f49a 100644 --- a/kiauh/core/menus/settings_menu.py +++ b/kiauh/core/menus/settings_menu.py @@ -6,16 +6,25 @@ # # # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # +from typing import Type, Optional from core.menus.base_menu import BaseMenu # noinspection PyMethodMayBeStatic class SettingsMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self) -> None: + pass def print_menu(self): print("self") diff --git a/kiauh/core/menus/update_menu.py b/kiauh/core/menus/update_menu.py index dcff75b..ba7d735 100644 --- a/kiauh/core/menus/update_menu.py +++ b/kiauh/core/menus/update_menu.py @@ -8,6 +8,7 @@ # ======================================================================= # import textwrap +from typing import Type, Optional from components.klipper.klipper_setup import update_klipper from components.klipper.klipper_utils import ( @@ -26,6 +27,7 @@ from components.webui_client.client_utils import ( ) from components.webui_client.fluidd_data import FluiddData from components.webui_client.mainsail_data import MainsailData +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import ( COLOR_GREEN, @@ -39,24 +41,9 @@ from utils.constants import ( # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class UpdateMenu(BaseMenu): - def __init__(self, previous_menu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - self.previous_menu: BaseMenu = previous_menu - self.options = { - "0": self.update_all, - "1": self.update_klipper, - "2": self.update_moonraker, - "3": self.update_mainsail, - "4": self.update_fluidd, - "5": self.update_mainsail_config, - "6": self.update_fluidd_config, - "7": self.update_klipperscreen, - "8": self.update_mobileraker, - "9": self.update_crowsnest, - "10": self.upgrade_system_packages, - } - self.kl_local = f"{COLOR_WHITE}{RESET_FORMAT}" self.kl_remote = f"{COLOR_WHITE}{RESET_FORMAT}" self.mr_local = f"{COLOR_WHITE}{RESET_FORMAT}" @@ -73,6 +60,28 @@ class UpdateMenu(BaseMenu): self.mainsail_client = MainsailData() self.fluidd_client = FluiddData() + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self) -> None: + self.options = { + "0": Option(self.update_all, menu=False), + "1": Option(self.update_klipper, menu=False), + "2": Option(self.update_moonraker, menu=False), + "3": Option(self.update_mainsail, menu=False), + "4": Option(self.update_fluidd, menu=False), + "5": Option(self.update_mainsail_config, menu=False), + "6": Option(self.update_fluidd_config, menu=False), + "7": Option(self.update_klipperscreen, menu=False), + "8": Option(self.update_mobileraker, menu=False), + "9": Option(self.update_crowsnest, menu=False), + "10": Option(self.upgrade_system_packages, menu=False), + } + def print_menu(self): self.fetch_update_status() diff --git a/kiauh/extensions/extensions_menu.py b/kiauh/extensions/extensions_menu.py index 5e437d2..b9f7e13 100644 --- a/kiauh/extensions/extensions_menu.py +++ b/kiauh/extensions/extensions_menu.py @@ -12,8 +12,9 @@ import inspect import json import textwrap from pathlib import Path -from typing import Type, Dict +from typing import Type, Dict, Optional +from core.menus import Option from extensions import EXTENSION_ROOT from extensions.base_extension import BaseExtension from core.menus.base_menu import BaseMenu @@ -23,12 +24,24 @@ from utils.constants import RESET_FORMAT, COLOR_CYAN, COLOR_YELLOW # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class ExtensionsMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.extensions: Dict[str, BaseExtension] = self.discover_extensions() - self.previous_menu: BaseMenu = previous_menu - self.extensions = self.discover_extensions() - self.options = {ext: self.extension_submenu for ext in self.extensions} + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.main_menu import MainMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self) -> None: + self.options = { + i: Option( + self.extension_submenu, menu=True, opt_data=self.extensions.get(i) + ) + for i in self.extensions + } def discover_extensions(self) -> Dict[str, BaseExtension]: ext_dict = {} @@ -63,8 +76,7 @@ class ExtensionsMenu(BaseMenu): return ext_dict def extension_submenu(self, **kwargs): - extension = self.extensions.get(kwargs.get("opt_index")) - ExtensionSubmenu(self, extension).run() + ExtensionSubmenu(kwargs.get("opt_data"), self.__class__).run() def print_menu(self): header = " [ Extensions Menu ] " @@ -92,28 +104,32 @@ class ExtensionsMenu(BaseMenu): # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class ExtensionSubmenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu, extension: BaseExtension): + def __init__( + self, extension: BaseExtension, previous_menu: Optional[Type[BaseMenu]] = None + ): super().__init__() - self.extension = extension - self.extension_name = extension.metadata.get("display_name") - self.extension_desc = extension.metadata.get("description") - self.previous_menu = previous_menu - self.options["1"] = extension.install_extension + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else ExtensionsMenu + ) + + def set_options(self) -> None: + self.options["1"] = Option(self.extension.install_extension, menu=False) if self.extension.metadata.get("updates"): - self.options["2"] = extension.update_extension - self.options["3"] = extension.remove_extension + self.options["2"] = Option(self.extension.update_extension, menu=False) + self.options["3"] = Option(self.extension.remove_extension, menu=False) else: - self.options["2"] = extension.remove_extension + self.options["2"] = Option(self.extension.remove_extension, menu=False) def print_menu(self) -> None: - header = f" [ {self.extension_name} ] " + header = f" [ {self.extension.metadata.get('display_name')} ] " color = COLOR_YELLOW count = 62 - len(color) - len(RESET_FORMAT) wrapper = textwrap.TextWrapper(55, initial_indent="| ", subsequent_indent="| ") - lines = wrapper.wrap(self.extension_desc) + lines = wrapper.wrap(self.extension.metadata.get("description")) formatted_lines = [f"{line:<55} |" for line in lines] description_text = "\n".join(formatted_lines) -- 2.39.5 From 20445f9c7a9a9fcab31798ec53b46f60c9cf9915 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Mon, 15 Apr 2024 20:32:42 +0200 Subject: [PATCH 11/27] remove moonraker entry function --- .../klipper_backup_extension.py | 58 ++++++++++++++----- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index c051481..f4a1acf 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -1,5 +1,5 @@ # ======================================================================= # -# Copyright (C) 2023 - 2024 Staubgeborener # +# Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet # # https://github.com/Staubgeborener/klipper-backup # # # # This file is part of KIAUH - Klipper Installation And Update Helper # @@ -12,12 +12,14 @@ import os import shutil import subprocess +from components.moonraker import MOONRAKER_DIR from extensions.base_extension import BaseExtension from extensions.klipper_backup import ( KLIPPERBACKUP_REPO_URL, KLIPPERBACKUP_DIR, KLIPPERBACKUP_CONFIG_DIR, ) + from utils.filesystem_utils import check_file_exist from utils.input_utils import get_confirm from utils.logger import Logger @@ -41,7 +43,6 @@ class KlipperbackupExtension(BaseExtension): subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "check_updates"]) def remove_extension(self, **kwargs) -> None: - def is_service_installed(service_names): installed = True for service_name in service_names: @@ -78,23 +79,35 @@ class KlipperbackupExtension(BaseExtension): Logger.print_info("Extension does not seem to be installed! Skipping ...") return + def remove_moonraker_entry(): + original_file_path = os.path.join(str(MOONRAKER_DIR), 'printer_data', 'config', 'moonraker.conf') + comparison_file_path = os.path.join(str(KLIPPERBACKUP_DIR), 'install-files', 'moonraker.conf') + + if not os.path.exists(original_file_path) or not os.path.exists(comparison_file_path): + Logger.print_error(f"Unknown error, either the moonraker.conf is not found or the klipper-backup entry under ~/klipper-backup/install-files/moonraker.conf. Skipping ...") + return + + with open(original_file_path, 'r') as original_file, open(comparison_file_path, 'r') as comparison_file: + original_content = original_file.read() + comparison_content = comparison_file.read() + + if comparison_content in original_content: + modified_content = original_content.replace(comparison_content, '') + with open(original_file_path, 'w') as original_file: + original_file.write(modified_content) + Logger.print_ok("Klipper backup entry in moonraker.conf removed) + else: + Logger.print_ok(f"Klipper-Backup entry not found in moonraker.conf. Skipping ...") + question = "Do you really want to remove the extension?" if get_confirm(question, True, False): - try: - Logger.print_status(f"Removing '{KLIPPERBACKUP_DIR}' ...") - shutil.rmtree(KLIPPERBACKUP_DIR) - config_backup_exists = check_file_exist(KLIPPERBACKUP_CONFIG_DIR) - if config_backup_exists: - shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) - Logger.print_ok("Extension successfully removed!") - except OSError as e: - Logger.print_error(f"Unable to remove extension: {e}") - + # Remove Klipper-Backup services service_names = ["klipper-backup-on-boot.service", "klipper-backup-filewatch.service"] - for service_name in service_names: + Logger.print_info(f"Check whether the service {service_name} is installed ...") if is_service_installed(service_name): + Logger.print_ok(f"Service {service_name} detected.") uninstall_service(service_name) else: Logger.print_info(f"The service {service_name} is not installed. Skipping ...") @@ -104,6 +117,21 @@ class KlipperbackupExtension(BaseExtension): if check_crontab_entry(entry_to_check): command = "crontab -l | grep -v '$HOME/klipper-backup/script.sh' | crontab -" subprocess.run(command, shell=True, check=True) - Logger.print_ok("Der Eintrag wurde aus dem crontab entfernt.") + Logger.print_ok("The klipper-backup entry has been removed from the crontab.") else: - Logger.print_info("Der Eintrag ist nicht im crontab vorhanden. Skipping ...") + Logger.print_info("The klipper-backup entry is not present in the crontab. Skipping ...") + + # Remove Moonraker entry + Logger.print_ok("Check for klipper-backup moonraker entry ...") + remove_moonraker_entry() + + # Remove Klipper-Backup + try: + Logger.print_status(f"Removing '{KLIPPERBACKUP_DIR}' ...") + shutil.rmtree(KLIPPERBACKUP_DIR) + config_backup_exists = check_file_exist(KLIPPERBACKUP_CONFIG_DIR) + if config_backup_exists: + shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) + Logger.print_ok("Extension successfully removed!") + except OSError as e: + Logger.print_error(f"Unable to remove extension: {e}") \ No newline at end of file -- 2.39.5 From ecb673a088a9792abc5d809aa13da38d1069cfb3 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Mon, 15 Apr 2024 21:29:13 +0200 Subject: [PATCH 12/27] feat: implement firmware build Signed-off-by: Dominik Willner --- .../klipper_firmware/firmware_utils.py | 169 ++++++++++++++++++ .../menus/klipper_build_menu.py | 111 ++++++++++++ .../menus/klipper_flash_error_menu.py | 21 ++- .../menus/klipper_flash_help_menu.py | 48 ++++- .../menus/klipper_flash_menu.py | 135 ++++++++------ kiauh/core/menus/advanced_menu.py | 33 ++-- kiauh/utils/system_utils.py | 8 +- 7 files changed, 451 insertions(+), 74 deletions(-) create mode 100644 kiauh/components/klipper_firmware/firmware_utils.py create mode 100644 kiauh/components/klipper_firmware/menus/klipper_build_menu.py diff --git a/kiauh/components/klipper_firmware/firmware_utils.py b/kiauh/components/klipper_firmware/firmware_utils.py new file mode 100644 index 0000000..698fdb1 --- /dev/null +++ b/kiauh/components/klipper_firmware/firmware_utils.py @@ -0,0 +1,169 @@ +# ======================================================================= # +# 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 subprocess import CalledProcessError, check_output, Popen, PIPE, STDOUT, run +from typing import List + +from components.klipper import KLIPPER_DIR +from components.klipper_firmware import SD_FLASH_SCRIPT +from components.klipper_firmware.flash_options import ( + FlashOptions, + FlashMethod, +) +from utils.logger import Logger +from utils.system_utils import log_process + + +def find_firmware_file(method: FlashMethod) -> bool: + target = KLIPPER_DIR.joinpath("out") + target_exists = target.exists() + if method is FlashMethod.REGULAR: + f1 = "klipper.elf.hex" + f2 = "klipper.elf" + fw_file_exists = target.joinpath(f1).exists() and target.joinpath(f2).exists() + elif method is FlashMethod.SD_CARD: + fw_file_exists = target.joinpath("klipper.bin").exists() + else: + raise Exception("Unknown flash method") + + return target_exists and fw_file_exists + + +def find_usb_device_by_id() -> List[str]: + try: + command = "find /dev/serial/by-id/* 2>/dev/null" + output = check_output(command, shell=True, text=True) + return output.splitlines() + except CalledProcessError as e: + Logger.print_error("Unable to find a USB device!") + Logger.print_error(e, prefix=False) + return [] + + +def find_uart_device() -> List[str]: + try: + command = '"find /dev -maxdepth 1 -regextype posix-extended -regex "^\/dev\/tty(AMA0|S0)$" 2>/dev/null"' + output = check_output(command, shell=True, text=True) + return output.splitlines() + except CalledProcessError as e: + Logger.print_error("Unable to find a UART device!") + Logger.print_error(e, prefix=False) + return [] + + +def find_usb_dfu_device() -> List[str]: + try: + command = '"lsusb | grep "DFU" | cut -d " " -f 6 2>/dev/null"' + output = check_output(command, shell=True, text=True) + return output.splitlines() + except CalledProcessError as e: + Logger.print_error("Unable to find a USB DFU device!") + Logger.print_error(e, prefix=False) + return [] + + +def get_sd_flash_board_list() -> List[str]: + if not KLIPPER_DIR.exists() or not SD_FLASH_SCRIPT.exists(): + return [] + + try: + cmd = f"{SD_FLASH_SCRIPT} -l" + blist = check_output(cmd, shell=True, text=True) + return blist.splitlines()[1:] + except CalledProcessError as e: + Logger.print_error(f"An unexpected error occured:\n{e}") + + +def start_flash_process(flash_options: FlashOptions) -> None: + Logger.print_status(f"Flashing '{flash_options.selected_mcu}' ...") + try: + if not flash_options.flash_method: + raise Exception("Missing value for flash_method!") + if not flash_options.flash_command: + raise Exception("Missing value for flash_command!") + if not flash_options.selected_mcu: + raise Exception("Missing value for selected_mcu!") + if not flash_options.connection_type: + raise Exception("Missing value for connection_type!") + if ( + flash_options.flash_method == FlashMethod.SD_CARD + and not flash_options.selected_board + ): + raise Exception("Missing value for selected_board!") + + if flash_options.flash_method is FlashMethod.REGULAR: + cmd = [ + "make", + flash_options.flash_command.value, + f"FLASH_DEVICE={flash_options.selected_mcu}", + ] + elif flash_options.flash_method is FlashMethod.SD_CARD: + if not SD_FLASH_SCRIPT.exists(): + raise Exception("Unable to find Klippers sdcard flash script!") + cmd = [ + SD_FLASH_SCRIPT, + "-b", + flash_options.selected_baudrate, + flash_options.selected_mcu, + flash_options.selected_board, + ] + else: + raise Exception("Invalid value for flash_method!") + + process = Popen(cmd, cwd=KLIPPER_DIR, stdout=PIPE, stderr=STDOUT, text=True) + log_process(process) + + rc = process.returncode + if rc != 0: + raise Exception(f"Flashing failed with returncode: {rc}") + else: + Logger.print_ok("Flashing successfull!", start="\n", end="\n\n") + + except (Exception, CalledProcessError): + Logger.print_error("Flashing failed!", start="\n") + Logger.print_error("See the console output above!", end="\n\n") + + +def run_make_clean() -> None: + try: + run( + "make clean", + cwd=KLIPPER_DIR, + shell=True, + check=True, + ) + except CalledProcessError as e: + Logger.print_error(f"Unexpected error:\n{e}") + raise + + +def run_make_menuconfig() -> None: + try: + run( + "make PYTHON=python3 menuconfig", + cwd=KLIPPER_DIR, + shell=True, + check=True, + ) + except CalledProcessError as e: + Logger.print_error(f"Unexpected error:\n{e}") + raise + + +def run_make() -> None: + try: + run( + "make PYTHON=python3", + cwd=KLIPPER_DIR, + shell=True, + check=True, + ) + except CalledProcessError as e: + Logger.print_error(f"Unexpected error:\n{e}") + raise diff --git a/kiauh/components/klipper_firmware/menus/klipper_build_menu.py b/kiauh/components/klipper_firmware/menus/klipper_build_menu.py new file mode 100644 index 0000000..6dc4272 --- /dev/null +++ b/kiauh/components/klipper_firmware/menus/klipper_build_menu.py @@ -0,0 +1,111 @@ +# ======================================================================= # +# 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 textwrap +from typing import Type, Optional + +from components.klipper import KLIPPER_DIR +from components.klipper_firmware.firmware_utils import ( + run_make_clean, + run_make_menuconfig, + run_make, +) +from core.menus import Option +from core.menus.base_menu import BaseMenu +from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_GREEN, COLOR_RED +from utils.logger import Logger +from utils.system_utils import ( + check_package_install, + update_system_package_lists, + install_system_packages, +) + + +# noinspection PyUnusedLocal +# noinspection PyMethodMayBeStatic +class KlipperBuildFirmwareMenu(BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): + super().__init__() + self.deps = ["build-essential", "dpkg-dev", "make"] + self.missing_deps = check_package_install(self.deps) + + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from core.menus.advanced_menu import AdvancedMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else AdvancedMenu + ) + + def set_options(self) -> None: + if len(self.missing_deps) == 0: + self.input_label_txt = "Press ENTER to continue" + self.default_option = Option(method=self.start_build_process, menu=False) + else: + self.input_label_txt = "Press ENTER to install dependencies" + self.default_option = Option(method=self.install_missing_deps, menu=False) + + def print_menu(self) -> None: + header = " [ Build Firmware Menu ] " + color = COLOR_CYAN + count = 62 - len(color) - len(RESET_FORMAT) + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:~^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | The following dependencies are required: | + | | + """ + )[1:] + + for d in self.deps: + status_ok = f"{COLOR_GREEN}*INSTALLED*{RESET_FORMAT}" + status_missing = f"{COLOR_RED}*MISSING*{RESET_FORMAT}" + status = status_missing if d in self.missing_deps else status_ok + padding = 39 - len(d) + len(status) + (len(status_ok) - len(status)) + d = f" {COLOR_CYAN}● {d}{RESET_FORMAT}" + menu += f"| {d}{status:>{padding}} |\n" + + menu += "| |\n" + if len(self.missing_deps) == 0: + line = f"{COLOR_GREEN}All dependencies are met!{RESET_FORMAT}" + else: + line = f"{COLOR_RED}Dependencies are missing!{RESET_FORMAT}" + + menu += f"| {line:<62} |\n" + + print(menu, end="") + + def install_missing_deps(self, **kwargs) -> None: + try: + update_system_package_lists(silent=False) + Logger.print_status("Installing system packages...") + install_system_packages(self.missing_deps) + except Exception as e: + Logger.print_error(e) + Logger.print_error("Installding dependencies failed!") + finally: + # restart this menu + KlipperBuildFirmwareMenu().run() + + def start_build_process(self, **kwargs) -> None: + try: + run_make_clean() + run_make_menuconfig() + run_make() + + Logger.print_ok("Firmware successfully built!") + Logger.print_ok(f"Firmware file located in '{KLIPPER_DIR}/out'!") + + except Exception as e: + Logger.print_error(e) + Logger.print_error("Building Klipper Firmware failed!") + + finally: + self.previous_menu().run() diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py index a54040d..61dea31 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py @@ -7,9 +7,10 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # import textwrap +from typing import Optional, Type from components.klipper_firmware.flash_options import FlashOptions, FlashMethod -from core.menus import FooterType +from core.menus import FooterType, Option from core.menus.base_menu import BaseMenu from utils.constants import COLOR_RED, RESET_FORMAT @@ -20,11 +21,19 @@ class KlipperNoFirmwareErrorMenu(BaseMenu): def __init__(self): super().__init__() + self.set_previous_menu(None) + self.set_options() + self.flash_options = FlashOptions() - self.default_option = self.go_back self.footer_type = FooterType.BLANK self.input_label_txt = "Press ENTER to go back to [Advanced Menu]" + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + pass + + def set_options(self) -> None: + self.default_option = Option(self.go_back, False) + def print_menu(self) -> None: header = "!!! NO FIRMWARE FILE FOUND !!!" color = COLOR_RED @@ -62,11 +71,15 @@ class KlipperNoFirmwareErrorMenu(BaseMenu): class KlipperNoBoardTypesErrorMenu(BaseMenu): def __init__(self): super().__init__() - - self.default_option = self.go_back self.footer_type = FooterType.BLANK self.input_label_txt = "Press ENTER to go back to [Main Menu]" + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + pass + + def set_options(self) -> None: + self.default_option = Option(self.go_back, False) + def print_menu(self) -> None: header = "!!! ERROR GETTING BOARD LIST !!!" color = COLOR_RED diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py index 16ebdcb..ed7d3cf 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py @@ -7,16 +7,28 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # import textwrap +from typing import Type, Optional from core.menus.base_menu import BaseMenu from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW +# noinspection DuplicatedCode class KlipperFlashMethodHelpMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from components.klipper_firmware.menus.klipper_flash_menu import ( + KlipperFlashMethodMenu, + ) + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else KlipperFlashMethodMenu + ) + + def set_options(self) -> None: + pass def print_menu(self) -> None: header = " < ? > Help: Flash MCU < ? > " @@ -57,11 +69,22 @@ class KlipperFlashMethodHelpMenu(BaseMenu): print(menu, end="") +# noinspection DuplicatedCode class KlipperFlashCommandHelpMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from components.klipper_firmware.menus.klipper_flash_menu import ( + KlipperFlashCommandMenu, + ) + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else KlipperFlashCommandMenu + ) + + def set_options(self) -> None: + pass def print_menu(self) -> None: header = " < ? > Help: Flash MCU < ? > " @@ -89,11 +112,24 @@ class KlipperFlashCommandHelpMenu(BaseMenu): print(menu, end="") +# noinspection DuplicatedCode class KlipperMcuConnectionHelpMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self): super().__init__() - self.previous_menu: BaseMenu = previous_menu + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + from components.klipper_firmware.menus.klipper_flash_menu import ( + KlipperSelectMcuConnectionMenu, + ) + + self.previous_menu: Type[BaseMenu] = ( + previous_menu + if previous_menu is not None + else KlipperSelectMcuConnectionMenu + ) + + def set_options(self) -> None: + pass def print_menu(self) -> None: header = " < ? > Help: Flash MCU < ? > " diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index 27f1269..09b812b 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -9,6 +9,7 @@ import textwrap import time +from typing import Type, Optional from components.klipper_firmware.flash_options import ( FlashOptions, @@ -16,7 +17,7 @@ from components.klipper_firmware.flash_options import ( FlashCommand, ConnectionType, ) -from components.klipper_firmware.flash_utils import ( +from components.klipper_firmware.firmware_utils import ( find_usb_device_by_id, find_uart_device, find_usb_dfu_device, @@ -33,7 +34,7 @@ from components.klipper_firmware.menus.klipper_flash_help_menu import ( KlipperFlashCommandHelpMenu, KlipperFlashMethodHelpMenu, ) -from core.menus import FooterType +from core.menus import FooterType, Option from core.menus.base_menu import BaseMenu from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW, COLOR_RED @@ -44,20 +45,22 @@ from utils.logger import Logger # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperFlashMethodMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu self.help_menu = KlipperFlashMethodHelpMenu - self.options = { - "1": self.select_regular, - "2": self.select_sdcard, - } self.input_label_txt = "Select flash method" self.footer_type = FooterType.BACK_HELP - self.flash_options = FlashOptions() + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = previous_menu + + def set_options(self) -> None: + self.options = { + "1": Option(self.select_regular, menu=False), + "2": Option(self.select_sdcard, menu=False), + } + def print_menu(self) -> None: header = " [ MCU Flash Menu ] " subheader = f"{COLOR_YELLOW}ATTENTION:{RESET_FORMAT}" @@ -95,7 +98,7 @@ class KlipperFlashMethodMenu(BaseMenu): def goto_next_menu(self, **kwargs): if find_firmware_file(self.flash_options.flash_method): - KlipperFlashCommandMenu(previous_menu=self).run() + KlipperFlashCommandMenu(previous_menu=self.__class__).run() else: KlipperNoFirmwareErrorMenu().run() @@ -103,21 +106,25 @@ class KlipperFlashMethodMenu(BaseMenu): # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperFlashCommandMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu self.help_menu = KlipperFlashCommandHelpMenu - self.options = { - "1": self.select_flash, - "2": self.select_serialflash, - } - self.default_option = self.select_flash self.input_label_txt = "Select flash command" self.footer_type = FooterType.BACK_HELP - self.flash_options = FlashOptions() + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else KlipperFlashMethodMenu + ) + + def set_options(self) -> None: + self.options = { + "1": Option(self.select_flash, menu=False), + "2": Option(self.select_serialflash, menu=False), + } + self.default_option = Option(self.select_flash, menu=False) + def print_menu(self) -> None: menu = textwrap.dedent( """ @@ -140,28 +147,34 @@ class KlipperFlashCommandMenu(BaseMenu): self.goto_next_menu() def goto_next_menu(self, **kwargs): - KlipperSelectMcuConnectionMenu(previous_menu=self).run() + KlipperSelectMcuConnectionMenu(previous_menu=self.__class__).run() # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperSelectMcuConnectionMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu, standalone: bool = False): + def __init__( + self, previous_menu: Optional[Type[BaseMenu]] = None, standalone: bool = False + ): super().__init__() - self.__standalone = standalone - self.previous_menu: BaseMenu = previous_menu self.help_menu = KlipperMcuConnectionHelpMenu - self.options = { - "1": self.select_usb, - "2": self.select_dfu, - "3": self.select_usb_dfu, - } self.input_label_txt = "Select connection type" self.footer_type = FooterType.BACK_HELP - self.flash_options = FlashOptions() + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else KlipperFlashCommandMenu + ) + + def set_options(self) -> None: + self.options = { + "1": Option(method=self.select_usb, menu=False), + "2": Option(method=self.select_dfu, menu=False), + "3": Option(method=self.select_usb_dfu, menu=False), + } + def print_menu(self) -> None: header = "Make sure that the controller board is connected now!" color = COLOR_YELLOW @@ -221,23 +234,32 @@ class KlipperSelectMcuConnectionMenu(BaseMenu): self.goto_next_menu() def goto_next_menu(self, **kwargs): - KlipperSelectMcuIdMenu(previous_menu=self).run() + KlipperSelectMcuIdMenu(previous_menu=self.__class__).run() # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperSelectMcuIdMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu self.flash_options = FlashOptions() self.mcu_list = self.flash_options.mcu_list - options = {f"{index}": self.flash_mcu for index in range(len(self.mcu_list))} - self.options = options self.input_label_txt = "Select MCU to flash" self.footer_type = FooterType.BACK_HELP + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = ( + previous_menu + if previous_menu is not None + else KlipperSelectMcuConnectionMenu + ) + + def set_options(self) -> None: + self.options = { + f"{i}": Option(self.flash_mcu, False, f"{i}") + for i in range(len(self.mcu_list)) + } + def print_menu(self) -> None: header = "!!! ATTENTION !!!" header2 = f"[{COLOR_CYAN}List of available MCUs{RESET_FORMAT}]" @@ -268,24 +290,30 @@ class KlipperSelectMcuIdMenu(BaseMenu): self.flash_options.selected_mcu = selected_mcu if self.flash_options.flash_method == FlashMethod.SD_CARD: - KlipperSelectSDFlashBoardMenu(previous_menu=self).run() + KlipperSelectSDFlashBoardMenu(previous_menu=self.__class__).run() elif self.flash_options.flash_method == FlashMethod.REGULAR: - KlipperFlashOverviewMenu(previous_menu=self).run() + KlipperFlashOverviewMenu(previous_menu=self.__class__).run() # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperSelectSDFlashBoardMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu self.flash_options = FlashOptions() self.available_boards = get_sd_flash_board_list() self.input_label_txt = "Select board type" - options = {f"{i}": self.board_select for i in range(len(self.available_boards))} - self.options = options + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else KlipperSelectMcuIdMenu + ) + + def set_options(self) -> None: + self.options = { + f"{i}": Option(self.board_select, False, f"{i}") + for i in range(len(self.available_boards)) + } def print_menu(self) -> None: if len(self.available_boards) < 1: @@ -331,20 +359,27 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu): min_count=0, allow_go_back=True, ) - KlipperFlashOverviewMenu(previous_menu=self).run() + KlipperFlashOverviewMenu(previous_menu=self.__class__).run() # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperFlashOverviewMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.previous_menu: BaseMenu = previous_menu self.flash_options = FlashOptions() - self.options = {"Y": self.execute_flash, "N": self.abort_process} self.input_label_txt = "Perform action (default=Y)" - self.default_option = self.execute_flash + + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: + self.previous_menu: Type[BaseMenu] = previous_menu + + def set_options(self) -> None: + self.options = { + "Y": Option(self.execute_flash, menu=False), + "N": Option(self.abort_process, menu=False), + } + + self.default_option = Option(self.execute_flash, menu=False) def print_menu(self) -> None: header = "!!! ATTENTION !!!" @@ -397,7 +432,7 @@ class KlipperFlashOverviewMenu(BaseMenu): start_flash_process(self.flash_options) Logger.print_info("Returning to MCU Flash Menu in 5 seconds ...") time.sleep(5) - KlipperFlashMethodMenu(previous_menu=AdvancedMenu()).run() + KlipperFlashMethodMenu(previous_menu=AdvancedMenu).run() def abort_process(self, **kwargs): from core.menus.advanced_menu import AdvancedMenu diff --git a/kiauh/core/menus/advanced_menu.py b/kiauh/core/menus/advanced_menu.py index 3000ba9..959b1ac 100644 --- a/kiauh/core/menus/advanced_menu.py +++ b/kiauh/core/menus/advanced_menu.py @@ -8,30 +8,37 @@ # ======================================================================= # import textwrap +from typing import Type, Optional +from components.klipper_firmware.menus.klipper_build_menu import ( + KlipperBuildFirmwareMenu, +) from components.klipper_firmware.menus.klipper_flash_menu import ( KlipperFlashMethodMenu, KlipperSelectMcuConnectionMenu, ) +from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import COLOR_YELLOW, RESET_FORMAT # noinspection PyUnusedLocal class AdvancedMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from core.menus.main_menu import MainMenu - self.previous_menu: BaseMenu = MainMenu() + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else MainMenu + ) + + def set_options(self): self.options = { - "1": None, - "2": None, - "3": None, - "4": self.flash, - "5": None, - "6": self.get_id, + "3": Option(method=self.build, menu=True), + "4": Option(method=self.flash, menu=False), + "6": Option(method=self.get_id, menu=False), } def print_menu(self): @@ -56,8 +63,14 @@ class AdvancedMenu(BaseMenu): )[1:] print(menu, end="") + def build(self, **kwargs): + KlipperBuildFirmwareMenu(previous_menu=self.__class__).run() + def flash(self, **kwargs): - KlipperFlashMethodMenu(previous_menu=self).run() + KlipperFlashMethodMenu(previous_menu=self.__class__).run() def get_id(self, **kwargs): - KlipperSelectMcuConnectionMenu(previous_menu=self, standalone=True).run() + KlipperSelectMcuConnectionMenu( + previous_menu=self.__class__, + standalone=True, + ).run() diff --git a/kiauh/utils/system_utils.py b/kiauh/utils/system_utils.py index 6ac932f..bdd3527 100644 --- a/kiauh/utils/system_utils.py +++ b/kiauh/utils/system_utils.py @@ -190,7 +190,8 @@ def update_system_package_lists(silent: bool, rls_info_change=False) -> None: Logger.print_ok("System package list update successful!") except CalledProcessError as e: - kill(f"Error updating system package list:\n{e.stderr.decode()}") + Logger.print_error(f"Error updating system package list:\n{e.stderr.decode()}") + raise def check_package_install(packages: List[str]) -> List[str]: @@ -210,8 +211,6 @@ def check_package_install(packages: List[str]) -> List[str]: ) if "installed" not in result.stdout.strip("'").split(): not_installed.append(package) - else: - Logger.print_ok(f"{package} already installed.") return not_installed @@ -230,7 +229,8 @@ def install_system_packages(packages: List[str]) -> None: Logger.print_ok("Packages installed successfully.") except CalledProcessError as e: - kill(f"Error installing packages:\n{e.stderr.decode()}") + Logger.print_error(f"Error installing packages:\n{e.stderr.decode()}") + raise def mask_system_service(service_name: str) -> None: -- 2.39.5 From c2b0ca5b193114fbc1990aae17d86c7151ee4f55 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Mon, 15 Apr 2024 21:31:54 +0200 Subject: [PATCH 13/27] fix: typo Signed-off-by: Dominik Willner --- kiauh/core/menus/base_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiauh/core/menus/base_menu.py b/kiauh/core/menus/base_menu.py index 2d5e1f8..68e27b9 100644 --- a/kiauh/core/menus/base_menu.py +++ b/kiauh/core/menus/base_menu.py @@ -213,4 +213,4 @@ class BaseMenu(metaclass=PostInitCaller): option.method(opt_index=option.opt_index, opt_data=option.opt_data) self.run() except Exception as e: - Logger.print_error(f"An unexpecred error occured:\n{e}") + Logger.print_error(f"An unexpected error occured:\n{e}") -- 2.39.5 From 8de7ab7e11f1a391fb91cacf73043d7c305939c3 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Mon, 15 Apr 2024 21:37:25 +0200 Subject: [PATCH 14/27] fix: wrong default previous menu for KlipperFlashMethodMenu Signed-off-by: Dominik Willner --- .../components/klipper_firmware/menus/klipper_flash_menu.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index 09b812b..f414924 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -53,7 +53,11 @@ class KlipperFlashMethodMenu(BaseMenu): self.flash_options = FlashOptions() def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: - self.previous_menu: Type[BaseMenu] = previous_menu + from core.menus.advanced_menu import AdvancedMenu + + self.previous_menu: Type[BaseMenu] = ( + previous_menu if previous_menu is not None else AdvancedMenu + ) def set_options(self) -> None: self.options = { -- 2.39.5 From cd63034b7409fa3b54afed11742cd4c4ee6f3f69 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Mon, 15 Apr 2024 21:52:32 +0200 Subject: [PATCH 15/27] fix: ignore flash method when checking for firmware files Signed-off-by: Dominik Willner --- .../klipper_firmware/firmware_utils.py | 17 ++++++++--------- .../menus/klipper_flash_menu.py | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/kiauh/components/klipper_firmware/firmware_utils.py b/kiauh/components/klipper_firmware/firmware_utils.py index 698fdb1..a75fb22 100644 --- a/kiauh/components/klipper_firmware/firmware_utils.py +++ b/kiauh/components/klipper_firmware/firmware_utils.py @@ -20,17 +20,16 @@ from utils.logger import Logger from utils.system_utils import log_process -def find_firmware_file(method: FlashMethod) -> bool: +def find_firmware_file() -> bool: target = KLIPPER_DIR.joinpath("out") target_exists = target.exists() - if method is FlashMethod.REGULAR: - f1 = "klipper.elf.hex" - f2 = "klipper.elf" - fw_file_exists = target.joinpath(f1).exists() and target.joinpath(f2).exists() - elif method is FlashMethod.SD_CARD: - fw_file_exists = target.joinpath("klipper.bin").exists() - else: - raise Exception("Unknown flash method") + + f1 = "klipper.elf.hex" + f2 = "klipper.elf" + f3 = "klipper.bin" + fw_file_exists = ( + target.joinpath(f1).exists() and target.joinpath(f2).exists() + ) or target.joinpath(f3).exists() return target_exists and fw_file_exists diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index f414924..09b1322 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -101,7 +101,7 @@ class KlipperFlashMethodMenu(BaseMenu): self.goto_next_menu() def goto_next_menu(self, **kwargs): - if find_firmware_file(self.flash_options.flash_method): + if find_firmware_file(): KlipperFlashCommandMenu(previous_menu=self.__class__).run() else: KlipperNoFirmwareErrorMenu().run() -- 2.39.5 From 336414c43ce217669aa264bf9b61256ba35e5cf4 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Mon, 15 Apr 2024 22:12:14 +0200 Subject: [PATCH 16/27] fix: init previous_menu in menus Signed-off-by: Dominik Willner --- .../components/klipper/menus/klipper_remove_menu.py | 1 + .../klipper_firmware/menus/klipper_build_menu.py | 1 + .../menus/klipper_flash_error_menu.py | 13 ++++++------- .../menus/klipper_flash_help_menu.py | 9 ++++++--- .../klipper_firmware/menus/klipper_flash_menu.py | 1 + .../components/log_uploads/menus/log_upload_menu.py | 3 ++- .../moonraker/menus/moonraker_remove_menu.py | 1 + .../webui_client/menus/client_remove_menu.py | 1 + kiauh/core/menus/advanced_menu.py | 1 + kiauh/core/menus/backup_menu.py | 1 + kiauh/core/menus/install_menu.py | 1 + kiauh/core/menus/remove_menu.py | 1 + kiauh/core/menus/settings_menu.py | 3 ++- kiauh/core/menus/update_menu.py | 1 + kiauh/extensions/extensions_menu.py | 2 ++ 15 files changed, 28 insertions(+), 12 deletions(-) diff --git a/kiauh/components/klipper/menus/klipper_remove_menu.py b/kiauh/components/klipper/menus/klipper_remove_menu.py index d079265..1190e98 100644 --- a/kiauh/components/klipper/menus/klipper_remove_menu.py +++ b/kiauh/components/klipper/menus/klipper_remove_menu.py @@ -20,6 +20,7 @@ from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN class KlipperRemoveMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.footer_type = FooterType.BACK_HELP self.remove_klipper_service = False self.remove_klipper_dir = False diff --git a/kiauh/components/klipper_firmware/menus/klipper_build_menu.py b/kiauh/components/klipper_firmware/menus/klipper_build_menu.py index 6dc4272..4bba6f4 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_build_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_build_menu.py @@ -32,6 +32,7 @@ from utils.system_utils import ( class KlipperBuildFirmwareMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.deps = ["build-essential", "dpkg-dev", "make"] self.missing_deps = check_package_install(self.deps) diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py index 61dea31..7df7e82 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py @@ -18,18 +18,16 @@ from utils.constants import COLOR_RED, RESET_FORMAT # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperNoFirmwareErrorMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() - - self.set_previous_menu(None) - self.set_options() + self.previous_menu = previous_menu self.flash_options = FlashOptions() self.footer_type = FooterType.BLANK self.input_label_txt = "Press ENTER to go back to [Advanced Menu]" def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: - pass + self.previous_menu = previous_menu def set_options(self) -> None: self.default_option = Option(self.go_back, False) @@ -69,13 +67,14 @@ class KlipperNoFirmwareErrorMenu(BaseMenu): # noinspection PyUnusedLocal # noinspection PyMethodMayBeStatic class KlipperNoBoardTypesErrorMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.footer_type = FooterType.BLANK self.input_label_txt = "Press ENTER to go back to [Main Menu]" def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: - pass + self.previous_menu = previous_menu def set_options(self) -> None: self.default_option = Option(self.go_back, False) diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py index ed7d3cf..736d9e9 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py @@ -15,8 +15,9 @@ from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW # noinspection DuplicatedCode class KlipperFlashMethodHelpMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from components.klipper_firmware.menus.klipper_flash_menu import ( @@ -71,8 +72,9 @@ class KlipperFlashMethodHelpMenu(BaseMenu): # noinspection DuplicatedCode class KlipperFlashCommandHelpMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from components.klipper_firmware.menus.klipper_flash_menu import ( @@ -114,8 +116,9 @@ class KlipperFlashCommandHelpMenu(BaseMenu): # noinspection DuplicatedCode class KlipperMcuConnectionHelpMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from components.klipper_firmware.menus.klipper_flash_menu import ( diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index 09b1322..9c2394f 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -161,6 +161,7 @@ class KlipperSelectMcuConnectionMenu(BaseMenu): self, previous_menu: Optional[Type[BaseMenu]] = None, standalone: bool = False ): super().__init__() + self.previous_menu = previous_menu self.__standalone = standalone self.help_menu = KlipperMcuConnectionHelpMenu self.input_label_txt = "Select connection type" diff --git a/kiauh/components/log_uploads/menus/log_upload_menu.py b/kiauh/components/log_uploads/menus/log_upload_menu.py index 3129c52..34f9834 100644 --- a/kiauh/components/log_uploads/menus/log_upload_menu.py +++ b/kiauh/components/log_uploads/menus/log_upload_menu.py @@ -19,8 +19,9 @@ from utils.constants import RESET_FORMAT, COLOR_YELLOW # noinspection PyMethodMayBeStatic class LogUploadMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.logfile_list = get_logfile_list() def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: diff --git a/kiauh/components/moonraker/menus/moonraker_remove_menu.py b/kiauh/components/moonraker/menus/moonraker_remove_menu.py index a72b61c..102c369 100644 --- a/kiauh/components/moonraker/menus/moonraker_remove_menu.py +++ b/kiauh/components/moonraker/menus/moonraker_remove_menu.py @@ -20,6 +20,7 @@ from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN class MoonrakerRemoveMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.remove_moonraker_service = False self.remove_moonraker_dir = False self.remove_moonraker_env = False diff --git a/kiauh/components/webui_client/menus/client_remove_menu.py b/kiauh/components/webui_client/menus/client_remove_menu.py index ed11e0a..8cfa50f 100644 --- a/kiauh/components/webui_client/menus/client_remove_menu.py +++ b/kiauh/components/webui_client/menus/client_remove_menu.py @@ -23,6 +23,7 @@ class ClientRemoveMenu(BaseMenu): self, client: BaseWebClient, previous_menu: Optional[Type[BaseMenu]] = None ): super().__init__() + self.previous_menu = previous_menu self.client = client self.rm_client = False self.rm_client_config = False diff --git a/kiauh/core/menus/advanced_menu.py b/kiauh/core/menus/advanced_menu.py index 959b1ac..2235334 100644 --- a/kiauh/core/menus/advanced_menu.py +++ b/kiauh/core/menus/advanced_menu.py @@ -26,6 +26,7 @@ from utils.constants import COLOR_YELLOW, RESET_FORMAT class AdvancedMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from core.menus.main_menu import MainMenu diff --git a/kiauh/core/menus/backup_menu.py b/kiauh/core/menus/backup_menu.py index 0eb8698..4bfb2c5 100644 --- a/kiauh/core/menus/backup_menu.py +++ b/kiauh/core/menus/backup_menu.py @@ -32,6 +32,7 @@ from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW class BackupMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from core.menus.main_menu import MainMenu diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index ecf4f78..ce42419 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -27,6 +27,7 @@ from utils.constants import COLOR_GREEN, RESET_FORMAT class InstallMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from core.menus.main_menu import MainMenu diff --git a/kiauh/core/menus/remove_menu.py b/kiauh/core/menus/remove_menu.py index 7d8ac75..ee6272a 100644 --- a/kiauh/core/menus/remove_menu.py +++ b/kiauh/core/menus/remove_menu.py @@ -27,6 +27,7 @@ from utils.constants import COLOR_RED, RESET_FORMAT class RemoveMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from core.menus.main_menu import MainMenu diff --git a/kiauh/core/menus/settings_menu.py b/kiauh/core/menus/settings_menu.py index d26f49a..8ed407b 100644 --- a/kiauh/core/menus/settings_menu.py +++ b/kiauh/core/menus/settings_menu.py @@ -13,8 +13,9 @@ from core.menus.base_menu import BaseMenu # noinspection PyMethodMayBeStatic class SettingsMenu(BaseMenu): - def __init__(self): + def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: from core.menus.main_menu import MainMenu diff --git a/kiauh/core/menus/update_menu.py b/kiauh/core/menus/update_menu.py index ba7d735..3af9085 100644 --- a/kiauh/core/menus/update_menu.py +++ b/kiauh/core/menus/update_menu.py @@ -43,6 +43,7 @@ from utils.constants import ( class UpdateMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.kl_local = f"{COLOR_WHITE}{RESET_FORMAT}" self.kl_remote = f"{COLOR_WHITE}{RESET_FORMAT}" diff --git a/kiauh/extensions/extensions_menu.py b/kiauh/extensions/extensions_menu.py index b9f7e13..7d7e59e 100644 --- a/kiauh/extensions/extensions_menu.py +++ b/kiauh/extensions/extensions_menu.py @@ -26,6 +26,7 @@ from utils.constants import RESET_FORMAT, COLOR_CYAN, COLOR_YELLOW class ExtensionsMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() + self.previous_menu = previous_menu self.extensions: Dict[str, BaseExtension] = self.discover_extensions() def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: @@ -109,6 +110,7 @@ class ExtensionSubmenu(BaseMenu): ): super().__init__() self.extension = extension + self.previous_menu = previous_menu def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: self.previous_menu: Type[BaseMenu] = ( -- 2.39.5 From 4b04720b490087b4d72632db7cc1f9e3757a8527 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Wed, 17 Apr 2024 06:23:14 +0200 Subject: [PATCH 17/27] adjusted some scripts and fix'd Logger.prints --- .../klipper_backup_extension.py | 94 +++++++++++-------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index f4a1acf..6828494 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -43,25 +43,24 @@ class KlipperbackupExtension(BaseExtension): subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "check_updates"]) def remove_extension(self, **kwargs) -> None: - def is_service_installed(service_names): - installed = True - for service_name in service_names: - try: - with open(os.devnull, 'w') as devnull: - subprocess.run(["systemctl", "status", service_name], stdout=devnull, stderr=devnull, check=True) - except subprocess.CalledProcessError: - installed = False - break - return installed + def is_service_installed(service_name): + command = ["systemctl", "status", service_name] + result = subprocess.run(command, capture_output=True, text=True) + if "Loaded:" in result.stdout: + return True + else: + return False - def uninstall_service(service_names): - for service_name in service_names: - try: - subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) - subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) - Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") - except subprocess.CalledProcessError: - Logger.print_error(f"Error uninstalling the service {service_name}.") + def uninstall_service(service_name): + try: + subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) + subprocess.run(["sudo", "systemctl", "disable", service_name], check=True) + subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True) + service_path = f'/etc/systemd/system/{service_name}' + os.system(f'sudo rm {service_path}') + Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") + except subprocess.CalledProcessError: + Logger.print_error(f"Error uninstalling the service {service_name}.") def check_crontab_entry(entry): try: @@ -84,54 +83,67 @@ class KlipperbackupExtension(BaseExtension): comparison_file_path = os.path.join(str(KLIPPERBACKUP_DIR), 'install-files', 'moonraker.conf') if not os.path.exists(original_file_path) or not os.path.exists(comparison_file_path): - Logger.print_error(f"Unknown error, either the moonraker.conf is not found or the klipper-backup entry under ~/klipper-backup/install-files/moonraker.conf. Skipping ...") - return + return False with open(original_file_path, 'r') as original_file, open(comparison_file_path, 'r') as comparison_file: original_content = original_file.read() - comparison_content = comparison_file.read() + comparison_content = comparison_file.read().strip() if comparison_content in original_content: modified_content = original_content.replace(comparison_content, '') with open(original_file_path, 'w') as original_file: original_file.write(modified_content) - Logger.print_ok("Klipper backup entry in moonraker.conf removed) + return True else: - Logger.print_ok(f"Klipper-Backup entry not found in moonraker.conf. Skipping ...") + return False question = "Do you really want to remove the extension?" if get_confirm(question, True, False): - + # Remove Klipper-Backup services service_names = ["klipper-backup-on-boot.service", "klipper-backup-filewatch.service"] for service_name in service_names: - Logger.print_info(f"Check whether the service {service_name} is installed ...") - if is_service_installed(service_name): - Logger.print_ok(f"Service {service_name} detected.") - uninstall_service(service_name) - else: - Logger.print_info(f"The service {service_name} is not installed. Skipping ...") + try: + Logger.print_status(f"Check whether the service {service_name} is installed ...") + if is_service_installed(service_name): + Logger.print_info(f"Service {service_name} detected.") + uninstall_service(service_name) + else: + Logger.print_info(f"The service {service_name} is not installed. Skipping ...") + except: + Logger.print_error(f"Unable to remove the service {service_name}") # Remove Klipper-Backup cron - entry_to_check = "$HOME/klipper-backup/script.sh" - if check_crontab_entry(entry_to_check): - command = "crontab -l | grep -v '$HOME/klipper-backup/script.sh' | crontab -" - subprocess.run(command, shell=True, check=True) - Logger.print_ok("The klipper-backup entry has been removed from the crontab.") - else: - Logger.print_info("The klipper-backup entry is not present in the crontab. Skipping ...") + Logger.print_status("Check for Klipper-Backup cron entry ...") + entry_to_check = "/klipper-backup/script.sh" + try: + if check_crontab_entry(entry_to_check): + crontab_content = subprocess.check_output(["crontab", "-l"], text=True) + modified_content = "\n".join(line for line in crontab_content.splitlines() if entry_to_check not in line) + subprocess.run(["crontab", "-"], input=modified_content, text=True, check=True) + Logger.print_ok("The Klipper-Backup entry has been removed from the crontab.") + else: + Logger.print_info("The Klipper-Backup entry is not present in the crontab. Skipping ...") + except: + Logger.print_error("Unable to remove the Klipper-Backup cron entry") # Remove Moonraker entry - Logger.print_ok("Check for klipper-backup moonraker entry ...") - remove_moonraker_entry() + Logger.print_status(f"Check for Klipper-Backup moonraker entry ...") + try: + if remove_moonraker_entry(): + Logger.print_ok("Klipper-Backup entry in moonraker.conf removed") + else: + Logger.print_info("Klipper-Backup entry not found in moonraker.conf. Skipping ...") + except: + Logger.print_error("Unknown error, either the moonraker.conf is not found or the Klipper-Backup entry under ~/klipper-backup/install-files/moonraker.conf. Skipping ...") # Remove Klipper-Backup + Logger.print_status(f"Removing '{KLIPPERBACKUP_DIR}' ...") try: - Logger.print_status(f"Removing '{KLIPPERBACKUP_DIR}' ...") shutil.rmtree(KLIPPERBACKUP_DIR) config_backup_exists = check_file_exist(KLIPPERBACKUP_CONFIG_DIR) if config_backup_exists: shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) - Logger.print_ok("Extension successfully removed!") + Logger.print_ok("Extension Klipper-Backup successfully removed!") except OSError as e: Logger.print_error(f"Unable to remove extension: {e}") \ No newline at end of file -- 2.39.5 From e5e5b0a3c99b5d5cda9fa95d72b1740b77c0a038 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Wed, 17 Apr 2024 06:24:38 +0200 Subject: [PATCH 18/27] remove paragraphs to make the code more clean --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 6828494..158db20 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -67,7 +67,6 @@ class KlipperbackupExtension(BaseExtension): crontab_content = subprocess.check_output(["crontab", "-l"], stderr=subprocess.DEVNULL, text=True) except subprocess.CalledProcessError: return False - for line in crontab_content.splitlines(): if entry in line: return True @@ -81,14 +80,11 @@ class KlipperbackupExtension(BaseExtension): def remove_moonraker_entry(): original_file_path = os.path.join(str(MOONRAKER_DIR), 'printer_data', 'config', 'moonraker.conf') comparison_file_path = os.path.join(str(KLIPPERBACKUP_DIR), 'install-files', 'moonraker.conf') - if not os.path.exists(original_file_path) or not os.path.exists(comparison_file_path): return False - with open(original_file_path, 'r') as original_file, open(comparison_file_path, 'r') as comparison_file: original_content = original_file.read() comparison_content = comparison_file.read().strip() - if comparison_content in original_content: modified_content = original_content.replace(comparison_content, '') with open(original_file_path, 'w') as original_file: -- 2.39.5 From e9846ab7eaa2e60661206771f3d015fdecd7c533 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Wed, 17 Apr 2024 12:28:02 +0200 Subject: [PATCH 19/27] Update klipper_backup_extension.py --- .../klipper_backup/klipper_backup_extension.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 158db20..4792116 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -46,6 +46,7 @@ class KlipperbackupExtension(BaseExtension): def is_service_installed(service_name): command = ["systemctl", "status", service_name] result = subprocess.run(command, capture_output=True, text=True) + # Doesn't matter whether the service is active or not, what matters is whether it is installed. So let's search for "Loaded:" in stdout if "Loaded:" in result.stdout: return True else: @@ -58,9 +59,9 @@ class KlipperbackupExtension(BaseExtension): subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True) service_path = f'/etc/systemd/system/{service_name}' os.system(f'sudo rm {service_path}') - Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") + return True except subprocess.CalledProcessError: - Logger.print_error(f"Error uninstalling the service {service_name}.") + return False def check_crontab_entry(entry): try: @@ -103,7 +104,10 @@ class KlipperbackupExtension(BaseExtension): Logger.print_status(f"Check whether the service {service_name} is installed ...") if is_service_installed(service_name): Logger.print_info(f"Service {service_name} detected.") - uninstall_service(service_name) + if uninstall_service(service_name): + Logger.print_ok(f"The service {service_name} has been successfully uninstalled.") + else: + Logger.print_error(f"Error uninstalling the service {service_name}.") else: Logger.print_info(f"The service {service_name} is not installed. Skipping ...") except: @@ -142,4 +146,4 @@ class KlipperbackupExtension(BaseExtension): shutil.rmtree(KLIPPERBACKUP_CONFIG_DIR) Logger.print_ok("Extension Klipper-Backup successfully removed!") except OSError as e: - Logger.print_error(f"Unable to remove extension: {e}") \ No newline at end of file + Logger.print_error(f"Unable to remove extension: {e}") -- 2.39.5 From 65132b68e70be514c9d3098c6457318aed4698c7 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Wed, 17 Apr 2024 14:34:32 +0200 Subject: [PATCH 20/27] add MOONRAKER_CONF to __init__.py --- kiauh/extensions/klipper_backup/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kiauh/extensions/klipper_backup/__init__.py b/kiauh/extensions/klipper_backup/__init__.py index 61e1c61..6597237 100644 --- a/kiauh/extensions/klipper_backup/__init__.py +++ b/kiauh/extensions/klipper_backup/__init__.py @@ -12,6 +12,7 @@ from pathlib import Path EXT_MODULE_NAME = "klipper_backup_extension.py" MODULE_PATH = Path(__file__).resolve().parent +MOONRAKER_CONF = Path.home().joinpath("printer_data", "config", "moonraker.conf") KLIPPERBACKUP_DIR = Path.home().joinpath("klipper-backup") KLIPPERBACKUP_CONFIG_DIR = Path.home().joinpath("config_backup") -KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" \ No newline at end of file +KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup" -- 2.39.5 From de506433d9613891d08d3e203548240b30aa5aa9 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Wed, 17 Apr 2024 15:27:18 +0200 Subject: [PATCH 21/27] remove newline characters in moonraker.conf --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 4792116..99855e4 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -18,6 +18,7 @@ from extensions.klipper_backup import ( KLIPPERBACKUP_REPO_URL, KLIPPERBACKUP_DIR, KLIPPERBACKUP_CONFIG_DIR, + MOONRAKER_CONF, ) from utils.filesystem_utils import check_file_exist @@ -79,7 +80,7 @@ class KlipperbackupExtension(BaseExtension): return def remove_moonraker_entry(): - original_file_path = os.path.join(str(MOONRAKER_DIR), 'printer_data', 'config', 'moonraker.conf') + original_file_path = MOONRAKER_CONF comparison_file_path = os.path.join(str(KLIPPERBACKUP_DIR), 'install-files', 'moonraker.conf') if not os.path.exists(original_file_path) or not os.path.exists(comparison_file_path): return False @@ -87,7 +88,8 @@ class KlipperbackupExtension(BaseExtension): original_content = original_file.read() comparison_content = comparison_file.read().strip() if comparison_content in original_content: - modified_content = original_content.replace(comparison_content, '') + modified_content = original_content.replace(comparison_content, '').strip() + modified_content = "\n".join(line for line in modified_content.split("\n") if line.strip()) with open(original_file_path, 'w') as original_file: original_file.write(modified_content) return True -- 2.39.5 From bfd731b900fc80c2a06963b13030578dc2342272 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Wed, 17 Apr 2024 15:28:30 +0200 Subject: [PATCH 22/27] remove MOONRAKER_DIR from components.moonraker MOONRAKER_DIR does not provide the correct folder for config file --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 1 - 1 file changed, 1 deletion(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 99855e4..7da4922 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -12,7 +12,6 @@ import os import shutil import subprocess -from components.moonraker import MOONRAKER_DIR from extensions.base_extension import BaseExtension from extensions.klipper_backup import ( KLIPPERBACKUP_REPO_URL, -- 2.39.5 From 449317b118fdc7bf97f6678f5aa0774cfdaec196 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Wed, 17 Apr 2024 19:48:31 +0200 Subject: [PATCH 23/27] fix: fix sd flash process Signed-off-by: Dominik Willner --- kiauh/components/klipper_firmware/firmware_utils.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kiauh/components/klipper_firmware/firmware_utils.py b/kiauh/components/klipper_firmware/firmware_utils.py index a75fb22..c8210e1 100644 --- a/kiauh/components/klipper_firmware/firmware_utils.py +++ b/kiauh/components/klipper_firmware/firmware_utils.py @@ -11,11 +11,13 @@ from subprocess import CalledProcessError, check_output, Popen, PIPE, STDOUT, ru from typing import List from components.klipper import KLIPPER_DIR +from components.klipper.klipper import Klipper from components.klipper_firmware import SD_FLASH_SCRIPT from components.klipper_firmware.flash_options import ( FlashOptions, FlashMethod, ) +from core.instance_manager.instance_manager import InstanceManager from utils.logger import Logger from utils.system_utils import log_process @@ -106,18 +108,22 @@ def start_flash_process(flash_options: FlashOptions) -> None: if not SD_FLASH_SCRIPT.exists(): raise Exception("Unable to find Klippers sdcard flash script!") cmd = [ - SD_FLASH_SCRIPT, - "-b", - flash_options.selected_baudrate, + SD_FLASH_SCRIPT.as_posix(), + f"-b {flash_options.selected_baudrate}", flash_options.selected_mcu, flash_options.selected_board, ] else: raise Exception("Invalid value for flash_method!") + instance_manager = InstanceManager(Klipper) + instance_manager.stop_all_instance() + process = Popen(cmd, cwd=KLIPPER_DIR, stdout=PIPE, stderr=STDOUT, text=True) log_process(process) + instance_manager.start_all_instance() + rc = process.returncode if rc != 0: raise Exception(f"Flashing failed with returncode: {rc}") -- 2.39.5 From aa1b435da5058673d72798d204bcb879df735247 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Wed, 17 Apr 2024 19:58:40 +0200 Subject: [PATCH 24/27] feat: implement build + flash process Signed-off-by: Dominik Willner --- .../components/klipper_firmware/menus/klipper_flash_menu.py | 4 +--- kiauh/core/menus/advanced_menu.py | 5 +++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index 9c2394f..455732e 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -432,12 +432,10 @@ class KlipperFlashOverviewMenu(BaseMenu): print(menu, end="") def execute_flash(self, **kwargs): - from core.menus.advanced_menu import AdvancedMenu - start_flash_process(self.flash_options) Logger.print_info("Returning to MCU Flash Menu in 5 seconds ...") time.sleep(5) - KlipperFlashMethodMenu(previous_menu=AdvancedMenu).run() + KlipperFlashMethodMenu().run() def abort_process(self, **kwargs): from core.menus.advanced_menu import AdvancedMenu diff --git a/kiauh/core/menus/advanced_menu.py b/kiauh/core/menus/advanced_menu.py index 2235334..f86788e 100644 --- a/kiauh/core/menus/advanced_menu.py +++ b/kiauh/core/menus/advanced_menu.py @@ -39,6 +39,7 @@ class AdvancedMenu(BaseMenu): self.options = { "3": Option(method=self.build, menu=True), "4": Option(method=self.flash, menu=False), + "5": Option(method=self.build_flash, menu=False), "6": Option(method=self.get_id, menu=False), } @@ -70,6 +71,10 @@ class AdvancedMenu(BaseMenu): def flash(self, **kwargs): KlipperFlashMethodMenu(previous_menu=self.__class__).run() + def build_flash(self, **kwargs): + KlipperBuildFirmwareMenu(previous_menu=KlipperFlashMethodMenu).run() + KlipperFlashMethodMenu(previous_menu=self.__class__).run() + def get_id(self, **kwargs): KlipperSelectMcuConnectionMenu( previous_menu=self.__class__, -- 2.39.5 From 20db7612b8ceab22d4f817ca3b97a61656811768 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Thu, 18 Apr 2024 12:50:41 +0200 Subject: [PATCH 25/27] remove unnecessary .strip() function --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index 7da4922..aec8328 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -85,7 +85,7 @@ class KlipperbackupExtension(BaseExtension): return False with open(original_file_path, 'r') as original_file, open(comparison_file_path, 'r') as comparison_file: original_content = original_file.read() - comparison_content = comparison_file.read().strip() + comparison_content = comparison_file.read() if comparison_content in original_content: modified_content = original_content.replace(comparison_content, '').strip() modified_content = "\n".join(line for line in modified_content.split("\n") if line.strip()) -- 2.39.5 From b020f1096768983c4fcfd132e4f30a727608a188 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Thu, 18 Apr 2024 21:54:34 +0200 Subject: [PATCH 26/27] feat: implement repo rollback feature Signed-off-by: Dominik Willner --- kiauh/core/menus/advanced_menu.py | 14 ++++++++++++ kiauh/utils/git_utils.py | 36 ++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/kiauh/core/menus/advanced_menu.py b/kiauh/core/menus/advanced_menu.py index f86788e..fb4a3ea 100644 --- a/kiauh/core/menus/advanced_menu.py +++ b/kiauh/core/menus/advanced_menu.py @@ -10,6 +10,8 @@ import textwrap from typing import Type, Optional +from components.klipper import KLIPPER_DIR +from components.klipper.klipper import Klipper from components.klipper_firmware.menus.klipper_build_menu import ( KlipperBuildFirmwareMenu, ) @@ -17,12 +19,16 @@ from components.klipper_firmware.menus.klipper_flash_menu import ( KlipperFlashMethodMenu, KlipperSelectMcuConnectionMenu, ) +from components.moonraker import MOONRAKER_DIR +from components.moonraker.moonraker import Moonraker from core.menus import Option from core.menus.base_menu import BaseMenu from utils.constants import COLOR_YELLOW, RESET_FORMAT +from utils.git_utils import rollback_repository # noinspection PyUnusedLocal +# noinspection PyMethodMayBeStatic class AdvancedMenu(BaseMenu): def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): super().__init__() @@ -37,6 +43,8 @@ class AdvancedMenu(BaseMenu): def set_options(self): self.options = { + "1": Option(method=self.klipper_rollback, menu=True), + "2": Option(method=self.moonraker_rollback, menu=True), "3": Option(method=self.build, menu=True), "4": Option(method=self.flash, menu=False), "5": Option(method=self.build_flash, menu=False), @@ -65,6 +73,12 @@ class AdvancedMenu(BaseMenu): )[1:] print(menu, end="") + def klipper_rollback(self, **kwargs): + rollback_repository(KLIPPER_DIR, Klipper) + + def moonraker_rollback(self, **kwargs): + rollback_repository(MOONRAKER_DIR, Moonraker) + def build(self, **kwargs): KlipperBuildFirmwareMenu(previous_menu=self.__class__).run() diff --git a/kiauh/utils/git_utils.py b/kiauh/utils/git_utils.py index 2ed9a4d..4feeaf3 100644 --- a/kiauh/utils/git_utils.py +++ b/kiauh/utils/git_utils.py @@ -2,8 +2,12 @@ import json import urllib.request from http.client import HTTPResponse from json import JSONDecodeError -from typing import List +from subprocess import CalledProcessError, PIPE, run +from typing import List, Type +from core.instance_manager.base_instance import BaseInstance +from core.instance_manager.instance_manager import InstanceManager +from utils.input_utils import get_number_input, get_confirm from utils.logger import Logger @@ -55,3 +59,33 @@ def get_latest_unstable_tag(repo_path: str) -> str: except Exception: Logger.print_error("Error while getting the latest unstable tag") raise + + +def rollback_repository(repo_dir: str, instance: Type[BaseInstance]) -> None: + q1 = "How many commits do you want to roll back" + amount = get_number_input(q1, 1, allow_go_back=True) + + im = InstanceManager(instance) + + Logger.print_warn("Do not continue if you have ongoing prints!", start="\n") + Logger.print_warn( + f"All currently running {im.instance_type.__name__} services will be stopped!" + ) + if not get_confirm( + f"Roll back {amount} commit{'s' if amount > 1 else ''}", + default_choice=False, + allow_go_back=True, + ): + Logger.print_info("Aborting roll back ...") + return + + im.stop_all_instance() + + try: + cmd = ["git", "reset", "--hard", f"HEAD~{amount}"] + run(cmd, cwd=repo_dir, check=True, stdout=PIPE, stderr=PIPE) + Logger.print_ok(f"Rolled back {amount} commits!", start="\n") + except CalledProcessError as e: + Logger.print_error(f"An error occured during repo rollback:\n{e}") + + im.start_all_instance() -- 2.39.5 From 5b8ab4538df876d210db2fcb41d251a586524662 Mon Sep 17 00:00:00 2001 From: Staubgeborener Date: Fri, 19 Apr 2024 08:45:13 +0200 Subject: [PATCH 27/27] comment out git checkout We only need this for testing purpose, otherwise simply use the main branch --- kiauh/extensions/klipper_backup/klipper_backup_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiauh/extensions/klipper_backup/klipper_backup_extension.py b/kiauh/extensions/klipper_backup/klipper_backup_extension.py index aec8328..e10b0ce 100644 --- a/kiauh/extensions/klipper_backup/klipper_backup_extension.py +++ b/kiauh/extensions/klipper_backup/klipper_backup_extension.py @@ -30,7 +30,7 @@ class KlipperbackupExtension(BaseExtension): def install_extension(self, **kwargs) -> None: if not KLIPPERBACKUP_DIR.exists(): subprocess.run(["git", "clone", str(KLIPPERBACKUP_REPO_URL), str(KLIPPERBACKUP_DIR)]) - subprocess.run(["git", "-C", str(KLIPPERBACKUP_DIR), "checkout", "installer-dev"]) + #subprocess.run(["git", "-C", str(KLIPPERBACKUP_DIR), "checkout", "installer-dev"]) # Only for testing subprocess.run(["chmod", "+x", str(KLIPPERBACKUP_DIR / "install.sh")]) subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh")]) -- 2.39.5