From ecb673a088a9792abc5d809aa13da38d1069cfb3 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Mon, 15 Apr 2024 21:29:13 +0200 Subject: [PATCH] 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: