diff --git a/kiauh/components/klipper_firmware/__init__.py b/kiauh/components/klipper_firmware/__init__.py index e69de29..f27ce38 100644 --- a/kiauh/components/klipper_firmware/__init__.py +++ b/kiauh/components/klipper_firmware/__init__.py @@ -0,0 +1,12 @@ +# ======================================================================= # +# 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 components.klipper import KLIPPER_DIR + +SD_FLASH_SCRIPT = KLIPPER_DIR.joinpath("scripts/flash-sdcard.sh") diff --git a/kiauh/components/klipper_firmware/flash_options.py b/kiauh/components/klipper_firmware/flash_options.py index 24564b5..47e51f9 100644 --- a/kiauh/components/klipper_firmware/flash_options.py +++ b/kiauh/components/klipper_firmware/flash_options.py @@ -13,8 +13,8 @@ from typing import Union, List class FlashMethod(Enum): - REGULAR = "REGULAR" - SD_CARD = "SD_CARD" + REGULAR = "Regular" + SD_CARD = "SD Card" class FlashCommand(Enum): @@ -24,7 +24,7 @@ class FlashCommand(Enum): class ConnectionType(Enum): USB = "USB" - USB_DFU = "USB_DFU" + USB_DFU = "USB (DFU)" UART = "UART" @@ -36,6 +36,7 @@ class FlashOptions: _mcu_list: List[str] = field(default_factory=list) _selected_mcu: str = "" _selected_board: str = "" + _selected_baudrate: int = 250000 def __new__(cls, *args, **kwargs): if not cls._instance: @@ -93,3 +94,11 @@ class FlashOptions: @selected_board.setter def selected_board(self, value: str) -> None: self._selected_board = value + + @property + def selected_baudrate(self) -> int: + return self._selected_baudrate + + @selected_baudrate.setter + def selected_baudrate(self, value: int) -> None: + self._selected_baudrate = value diff --git a/kiauh/components/klipper_firmware/flash_utils.py b/kiauh/components/klipper_firmware/flash_utils.py index 75b6610..b064e46 100644 --- a/kiauh/components/klipper_firmware/flash_utils.py +++ b/kiauh/components/klipper_firmware/flash_utils.py @@ -7,15 +7,35 @@ # 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.flash_options import FlashOptions, FlashCommand +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" @@ -49,28 +69,62 @@ def find_usb_dfu_device() -> List[str]: return [] -def flash_device(flash_options: FlashOptions) -> None: +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_command is FlashCommand.FLASH: - command = [ + if flash_options.flash_method is FlashMethod.REGULAR: + cmd = [ "make", flash_options.flash_command.value, f"FLASH_DEVICE={flash_options.selected_mcu}", ] - process = Popen( - command, cwd=KLIPPER_DIR, stdout=PIPE, stderr=STDOUT, text=True - ) + 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!") - log_process(process) + 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") + 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") diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py new file mode 100644 index 0000000..beff44a --- /dev/null +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_error_menu.py @@ -0,0 +1,99 @@ +# ======================================================================= # +# 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 components.klipper_firmware.flash_options import FlashOptions, FlashMethod +from core.menus import FooterType +from core.menus.base_menu import BaseMenu +from utils.constants import COLOR_RED, RESET_FORMAT + + +# noinspection PyUnusedLocal +# noinspection PyMethodMayBeStatic +class KlipperNoFirmwareErrorMenu(BaseMenu): + def __init__(self): + super().__init__() + + self.flash_options = FlashOptions() + self.options = {"": self.go_back} + self.default_options = self.go_back + self.footer_type = FooterType.BLANK + self.input_label_txt = "Press ENTER to go back to [Advanced Menu]" + + def print_menu(self) -> None: + header = "!!! NO FIRMWARE FILE FOUND !!!" + color = COLOR_RED + count = 62 - len(color) - len(RESET_FORMAT) + line1 = f"{color}Unable to find a compiled firmware file!{RESET_FORMAT}" + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | {line1:<62} | + | | + | Make sure, that: | + | ● the folder '~/klipper/out' and its content exist | + | ● the folder contains the following file: | + """ + )[1:] + + if self.flash_options.flash_method is FlashMethod.REGULAR: + menu += "| ● 'klipper.elf' |\n" + menu += "| ● 'klipper.elf.hex' |\n" + else: + menu += "| ● 'klipper.bin' |\n" + + print(menu, end="") + + def go_back(self, **kwargs) -> None: + from core.menus.advanced_menu import AdvancedMenu + + AdvancedMenu().run() + + +# noinspection PyUnusedLocal +# noinspection PyMethodMayBeStatic +class KlipperNoBoardTypesErrorMenu(BaseMenu): + def __init__(self): + super().__init__() + + self.options = {"": self.go_back} + self.default_options = self.go_back + self.footer_type = FooterType.BLANK + self.input_label_txt = "Press ENTER to go back to [Main Menu]" + + def print_menu(self) -> None: + header = "!!! ERROR GETTING BOARD LIST !!!" + color = COLOR_RED + count = 62 - len(color) - len(RESET_FORMAT) + line1 = f"{color}Reading the list of supported boards failed!{RESET_FORMAT}" + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | {line1:<62} | + | | + | Make sure, that: | + | ● the folder '~/klipper' and all its content exist | + | ● the content of folder '~/klipper' is not currupted | + | ● the file '~/klipper/scripts/flash-sd.py' exist | + | ● your current user has access to those files/folders | + | | + | If in doubt or this process continues to fail, please | + | consider to download Klipper again. | + """ + )[1:] + print(menu, end="") + + def go_back(self, **kwargs) -> None: + from core.menus.main_menu import MainMenu + + MainMenu().run() diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py new file mode 100644 index 0000000..16ebdcb --- /dev/null +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_help_menu.py @@ -0,0 +1,127 @@ +# ======================================================================= # +# 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 core.menus.base_menu import BaseMenu +from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW + + +class KlipperFlashMethodHelpMenu(BaseMenu): + def __init__(self, previous_menu: BaseMenu): + super().__init__() + + self.previous_menu: BaseMenu = previous_menu + + def print_menu(self) -> None: + header = " < ? > Help: Flash MCU < ? > " + color = COLOR_YELLOW + count = 62 - len(color) - len(RESET_FORMAT) + subheader1 = f"{COLOR_CYAN}Regular flashing method:{RESET_FORMAT}" + subheader2 = f"{COLOR_CYAN}Updating via SD-Card Update:{RESET_FORMAT}" + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:~^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | {subheader1:<62} | + | The default method to flash controller boards which | + | are connected and updated over USB and not by placing | + | a compiled firmware file onto an internal SD-Card. | + | | + | Common controllers that get flashed that way are: | + | - Arduino Mega 2560 | + | - Fysetc F6 / S6 (used without a Display + SD-Slot) | + | | + | {subheader2:<62} | + | Many popular controller boards ship with a bootloader | + | capable of updating the firmware via SD-Card. | + | Choose this method if your controller board supports | + | this way of updating. This method ONLY works for up- | + | grading firmware. The initial flashing procedure must | + | be done manually per the instructions that apply to | + | your controller board. | + | | + | Common controllers that can be flashed that way are: | + | - BigTreeTech SKR 1.3 / 1.4 (Turbo) / E3 / Mini E3 | + | - Fysetc F6 / S6 (used with a Display + SD-Slot) | + | - Fysetc Spider | + | | + """ + )[1:] + print(menu, end="") + + +class KlipperFlashCommandHelpMenu(BaseMenu): + def __init__(self, previous_menu: BaseMenu): + super().__init__() + + self.previous_menu: BaseMenu = previous_menu + + def print_menu(self) -> None: + header = " < ? > Help: Flash MCU < ? > " + color = COLOR_YELLOW + count = 62 - len(color) - len(RESET_FORMAT) + subheader1 = f"{COLOR_CYAN}make flash:{RESET_FORMAT}" + subheader2 = f"{COLOR_CYAN}make serialflash:{RESET_FORMAT}" + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:~^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | {subheader1:<62} | + | The default command to flash controller board, it | + | will detect selected microcontroller and use suitable | + | tool for flashing it. | + | | + | {subheader2:<62} | + | Special command to flash STM32 microcontrollers in | + | DFU mode but connected via serial. stm32flash command | + | will be used internally. | + | | + """ + )[1:] + print(menu, end="") + + +class KlipperMcuConnectionHelpMenu(BaseMenu): + def __init__(self, previous_menu: BaseMenu): + super().__init__() + + self.previous_menu: BaseMenu = previous_menu + + def print_menu(self) -> None: + header = " < ? > Help: Flash MCU < ? > " + color = COLOR_YELLOW + count = 62 - len(color) - len(RESET_FORMAT) + subheader1 = f"{COLOR_CYAN}USB:{RESET_FORMAT}" + subheader2 = f"{COLOR_CYAN}UART:{RESET_FORMAT}" + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:~^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | {subheader1:<62} | + | Selecting USB as the connection method will scan the | + | USB ports for connected controller boards. This will | + | be similar to the 'ls /dev/serial/by-id/*' command | + | suggested by the official Klipper documentation for | + | determining successfull USB connections! | + | | + | {subheader2:<62} | + | Selecting UART as the connection method will list all | + | possible UART serial ports. Note: This method ALWAYS | + | returns something as it seems impossible to determine | + | if a valid Klipper controller board is connected or | + | not. Because of that, you MUST know which UART serial | + | port your controller board is connected to when using | + | this connection method. | + | | + """ + )[1:] + print(menu, end="") diff --git a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py index cda6301..9b1b7cd 100644 --- a/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py +++ b/kiauh/components/klipper_firmware/menus/klipper_flash_menu.py @@ -20,13 +20,24 @@ from components.klipper_firmware.flash_utils import ( find_usb_device_by_id, find_uart_device, find_usb_dfu_device, - flash_device, + get_sd_flash_board_list, + start_flash_process, + find_firmware_file, +) +from components.klipper_firmware.menus.klipper_flash_error_menu import ( + KlipperNoBoardTypesErrorMenu, + KlipperNoFirmwareErrorMenu, +) +from components.klipper_firmware.menus.klipper_flash_help_menu import ( + KlipperMcuConnectionHelpMenu, + KlipperFlashCommandHelpMenu, + KlipperFlashMethodHelpMenu, ) from core.menus import FooterType from core.menus.base_menu import BaseMenu from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW, COLOR_RED -from utils.input_utils import get_confirm +from utils.input_utils import get_number_input from utils.logger import Logger @@ -48,7 +59,11 @@ class KlipperFlashMethodMenu(BaseMenu): self.flash_options = FlashOptions() def print_menu(self) -> None: - header = " [ Flash MCU ] " + header = " [ MCU Flash Menu ] " + subheader = f"{COLOR_YELLOW}ATTENTION:{RESET_FORMAT}" + subline1 = f"{COLOR_YELLOW}Make sure to select the correct method for the MCU!{RESET_FORMAT}" + subline2 = f"{COLOR_YELLOW}Not all MCUs support both methods!{RESET_FORMAT}" + color = COLOR_CYAN count = 62 - len(color) - len(RESET_FORMAT) menu = textwrap.dedent( @@ -56,9 +71,11 @@ class KlipperFlashMethodMenu(BaseMenu): /=======================================================\\ | {color}{header:~^{count}}{RESET_FORMAT} | |-------------------------------------------------------| - | Please select the flashing method to flash your MCU. | - | Make sure to only select a method your MCU supports. | - | Not all MCUs support both methods! | + | Select the flash method for flashing the MCU. | + | | + | {subheader:<62} | + | {subline1:<62} | + | {subline2:<62} | |-------------------------------------------------------| | | | 1) Regular flashing method | @@ -77,7 +94,10 @@ class KlipperFlashMethodMenu(BaseMenu): self.goto_next_menu() def goto_next_menu(self, **kwargs): - KlipperFlashCommandMenu(previous_menu=self).run() + if find_firmware_file(self.flash_options.flash_method): + KlipperFlashCommandMenu(previous_menu=self).run() + else: + KlipperNoFirmwareErrorMenu().run() def help_menu(self, **kwargs): KlipperFlashMethodHelpMenu(previous_menu=self).run() @@ -256,132 +276,139 @@ class KlipperSelectMcuIdMenu(BaseMenu): selected_mcu = self.mcu_list[index] self.flash_options.selected_mcu = selected_mcu - print(f"{COLOR_CYAN}###### You selected:{RESET_FORMAT}") - print(f"● MCU #{index}: {selected_mcu}\n") + if self.flash_options.flash_method == FlashMethod.SD_CARD: + KlipperSelectSDFlashBoardMenu(previous_menu=self).run() + elif self.flash_options.flash_method == FlashMethod.REGULAR: + KlipperFlashOverviewMenu(previous_menu=self).run() - if get_confirm("Continue", allow_go_back=True): - Logger.print_status(f"Flashing '{selected_mcu}' ...") - flash_device(self.flash_options) - self.goto_next_menu() +# noinspection PyUnusedLocal +# noinspection PyMethodMayBeStatic +class KlipperSelectSDFlashBoardMenu(BaseMenu): + def __init__(self, previous_menu: BaseMenu): + super().__init__() - def goto_next_menu(self, **kwargs): - from core.menus.main_menu import MainMenu + 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 print_menu(self) -> None: + if len(self.available_boards) < 1: + KlipperNoBoardTypesErrorMenu().run() + else: + menu = textwrap.dedent( + """ + /=======================================================\\ + | Please select the type of board that corresponds to | + | the currently selected MCU ID you chose before. | + | | + | The following boards are currently supported: | + |-------------------------------------------------------| + """ + )[1:] + + for i, board in enumerate(self.available_boards): + line = f" {i}) {board}" + menu += f"|{line:<55}|\n" + + print(menu, end="") + + def board_select(self, **kwargs): + board = int(kwargs.get("opt_index")) + self.flash_options.selected_board = self.available_boards[board] + self.baudrate_select() + + def baudrate_select(self, **kwargs): + menu = textwrap.dedent( + """ + /=======================================================\\ + | If your board is flashed with firmware that connects | + | at a custom baud rate, please change it now. | + | | + | If you are unsure, stick to the default 250000! | + \\=======================================================/ + """ + )[1:] + print(menu, end="") + self.flash_options.selected_baudrate = get_number_input( + question="Please set the baud rate", + default=250000, + min_count=0, + allow_go_back=True, + ) + KlipperFlashOverviewMenu(previous_menu=self).run() + + +# noinspection PyUnusedLocal +# noinspection PyMethodMayBeStatic +class KlipperFlashOverviewMenu(BaseMenu): + def __init__(self, previous_menu: BaseMenu): + 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 print_menu(self) -> None: + header = "!!! ATTENTION !!!" + color = COLOR_RED + count = 62 - len(color) - len(RESET_FORMAT) + + method = self.flash_options.flash_method.value + command = self.flash_options.flash_command.value + conn_type = self.flash_options.connection_type.value + mcu = self.flash_options.selected_mcu + board = self.flash_options.selected_board + baudrate = self.flash_options.selected_baudrate + subheader = f"[{COLOR_CYAN}Overview{RESET_FORMAT}]" + menu = textwrap.dedent( + f""" + /=======================================================\\ + | {color}{header:^{count}}{RESET_FORMAT} | + |-------------------------------------------------------| + | Before contuining the flashing process, please check | + | if all parameters were set correctly! Once you made | + | sure everything is correct, start the process. If any | + | parameter needs to be changed, you can go back (B) | + | step by step or abort and start from the beginning. | + |{subheader:-^64}| + + """ + )[1:] + + menu += f" ● MCU: {COLOR_CYAN}{mcu}{RESET_FORMAT}\n" + menu += f" ● Connection: {COLOR_CYAN}{conn_type}{RESET_FORMAT}\n" + menu += f" ● Flash method: {COLOR_CYAN}{method}{RESET_FORMAT}\n" + menu += f" ● Flash command: {COLOR_CYAN}{command}{RESET_FORMAT}\n" + + if self.flash_options.flash_method is FlashMethod.SD_CARD: + menu += f" ● Board type: {COLOR_CYAN}{board}{RESET_FORMAT}\n" + menu += f" ● Baudrate: {COLOR_CYAN}{baudrate}{RESET_FORMAT}\n" + + menu += textwrap.dedent( + """ + |-------------------------------------------------------| + | Y) Start flash process | + | N) Abort - Return to Advanced Menu | + """ + ) + print(menu, end="") + + def execute_flash(self, **kwargs): from core.menus.advanced_menu import AdvancedMenu - AdvancedMenu(previous_menu=MainMenu()).run() + 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() + def abort_process(self, **kwargs): + from core.menus.advanced_menu import AdvancedMenu -class KlipperFlashMethodHelpMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): - super().__init__() - - self.previous_menu: BaseMenu = previous_menu - - def print_menu(self) -> None: - header = " < ? > Help: Flash MCU < ? > " - color = COLOR_YELLOW - count = 62 - len(color) - len(RESET_FORMAT) - subheader1 = f"{COLOR_CYAN}Regular flashing method:{RESET_FORMAT}" - subheader2 = f"{COLOR_CYAN}Updating via SD-Card Update:{RESET_FORMAT}" - menu = textwrap.dedent( - f""" - /=======================================================\\ - | {color}{header:~^{count}}{RESET_FORMAT} | - |-------------------------------------------------------| - | {subheader1:<62} | - | The default method to flash controller boards which | - | are connected and updated over USB and not by placing | - | a compiled firmware file onto an internal SD-Card. | - | | - | Common controllers that get flashed that way are: | - | - Arduino Mega 2560 | - | - Fysetc F6 / S6 (used without a Display + SD-Slot) | - | | - | {subheader2:<62} | - | Many popular controller boards ship with a bootloader | - | capable of updating the firmware via SD-Card. | - | Choose this method if your controller board supports | - | this way of updating. This method ONLY works for up- | - | grading firmware. The initial flashing procedure must | - | be done manually per the instructions that apply to | - | your controller board. | - | | - | Common controllers that can be flashed that way are: | - | - BigTreeTech SKR 1.3 / 1.4 (Turbo) / E3 / Mini E3 | - | - Fysetc F6 / S6 (used with a Display + SD-Slot) | - | - Fysetc Spider | - | | - """ - )[1:] - print(menu, end="") - - -class KlipperFlashCommandHelpMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): - super().__init__() - - self.previous_menu: BaseMenu = previous_menu - - def print_menu(self) -> None: - header = " < ? > Help: Flash MCU < ? > " - color = COLOR_YELLOW - count = 62 - len(color) - len(RESET_FORMAT) - subheader1 = f"{COLOR_CYAN}make flash:{RESET_FORMAT}" - subheader2 = f"{COLOR_CYAN}make serialflash:{RESET_FORMAT}" - menu = textwrap.dedent( - f""" - /=======================================================\\ - | {color}{header:~^{count}}{RESET_FORMAT} | - |-------------------------------------------------------| - | {subheader1:<62} | - | The default command to flash controller board, it | - | will detect selected microcontroller and use suitable | - | tool for flashing it. | - | | - | {subheader2:<62} | - | Special command to flash STM32 microcontrollers in | - | DFU mode but connected via serial. stm32flash command | - | will be used internally. | - | | - """ - )[1:] - print(menu, end="") - - -class KlipperMcuConnectionHelpMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): - super().__init__() - - self.previous_menu: BaseMenu = previous_menu - - def print_menu(self) -> None: - header = " < ? > Help: Flash MCU < ? > " - color = COLOR_YELLOW - count = 62 - len(color) - len(RESET_FORMAT) - subheader1 = f"{COLOR_CYAN}USB:{RESET_FORMAT}" - subheader2 = f"{COLOR_CYAN}UART:{RESET_FORMAT}" - menu = textwrap.dedent( - f""" - /=======================================================\\ - | {color}{header:~^{count}}{RESET_FORMAT} | - |-------------------------------------------------------| - | {subheader1:<62} | - | Selecting USB as the connection method will scan the | - | USB ports for connected controller boards. This will | - | be similar to the 'ls /dev/serial/by-id/*' command | - | suggested by the official Klipper documentation for | - | determining successfull USB connections! | - | | - | {subheader2:<62} | - | Selecting UART as the connection method will list all | - | possible UART serial ports. Note: This method ALWAYS | - | returns something as it seems impossible to determine | - | if a valid Klipper controller board is connected or | - | not. Because of that, you MUST know which UART serial | - | port your controller board is connected to when using | - | this connection method. | - | | - """ - )[1:] - print(menu, end="") + AdvancedMenu().run() diff --git a/kiauh/core/menus/__init__.py b/kiauh/core/menus/__init__.py index 670d68e..ed37c68 100644 --- a/kiauh/core/menus/__init__.py +++ b/kiauh/core/menus/__init__.py @@ -14,12 +14,14 @@ class FooterType(Enum): QUIT = "QUIT" BACK = "BACK" BACK_HELP = "BACK_HELP" + BLANK = "BLANK" NAVI_OPTIONS = { FooterType.QUIT: ["q"], FooterType.BACK: ["b"], FooterType.BACK_HELP: ["b", "h"], + FooterType.BLANK: [], } diff --git a/kiauh/core/menus/advanced_menu.py b/kiauh/core/menus/advanced_menu.py index 5d198ff..3000ba9 100644 --- a/kiauh/core/menus/advanced_menu.py +++ b/kiauh/core/menus/advanced_menu.py @@ -19,10 +19,12 @@ from utils.constants import COLOR_YELLOW, RESET_FORMAT # noinspection PyUnusedLocal class AdvancedMenu(BaseMenu): - def __init__(self, previous_menu: BaseMenu): + def __init__(self): super().__init__() - self.previous_menu: BaseMenu = previous_menu + from core.menus.main_menu import MainMenu + + self.previous_menu: BaseMenu = MainMenu() self.options = { "1": None, "2": None, diff --git a/kiauh/core/menus/base_menu.py b/kiauh/core/menus/base_menu.py index 509b46f..0637f72 100644 --- a/kiauh/core/menus/base_menu.py +++ b/kiauh/core/menus/base_menu.py @@ -92,6 +92,10 @@ def print_back_help_footer(): print(footer, end="") +def print_blank_footer(): + print("\=======================================================/") + + Options = Dict[str, Callable] @@ -119,6 +123,8 @@ class BaseMenu(ABC): print_back_footer() elif self.footer_type is FooterType.BACK_HELP: print_back_help_footer() + elif self.footer_type is FooterType.BLANK: + print_blank_footer() else: raise NotImplementedError("Method for printing footer not implemented.") diff --git a/kiauh/core/menus/main_menu.py b/kiauh/core/menus/main_menu.py index ea18b88..5cf45fd 100644 --- a/kiauh/core/menus/main_menu.py +++ b/kiauh/core/menus/main_menu.py @@ -152,7 +152,7 @@ class MainMenu(BaseMenu): RemoveMenu(previous_menu=self).run() def advanced_menu(self, **kwargs): - AdvancedMenu(previous_menu=self).run() + AdvancedMenu().run() def backup_menu(self, **kwargs): BackupMenu(previous_menu=self).run()