mirror of
https://github.com/dw-0/kiauh.git
synced 2025-12-24 16:23:36 +05:00
Compare commits
2 Commits
dbe15e3a32
...
7444ae8cea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7444ae8cea | ||
|
|
103a7b61b3 |
@@ -88,7 +88,6 @@ def print_multi_instance_warning(instances: List[Klipper]) -> None:
|
||||
"The following instances were found:",
|
||||
*_instances,
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -145,7 +145,6 @@ def update_klipper() -> None:
|
||||
"All Klipper instances will be restarted during the update process and "
|
||||
"ongoing prints WILL FAIL.",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
if not get_confirm("Update Klipper now?"):
|
||||
|
||||
@@ -220,7 +220,6 @@ def check_user_groups():
|
||||
"INFO:",
|
||||
"Relog required for group assignments to take effect!",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"):
|
||||
@@ -272,7 +271,6 @@ def handle_disruptive_system_packages() -> None:
|
||||
"Please fix the problem manually. Otherwise, this may have "
|
||||
"undesirable effects on the operation of Klipper."
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -353,7 +353,6 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
|
||||
"\n\n",
|
||||
"If you are unsure, stick to the default 250000!",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
self.flash_options.selected_baudrate = get_number_input(
|
||||
question="Please set the baud rate",
|
||||
|
||||
@@ -62,7 +62,6 @@ def install_klipperscreen() -> None:
|
||||
"KlipperScreens update manager configuration for Moonraker "
|
||||
"will not be added to any moonraker.conf.",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
if not get_confirm(
|
||||
"Continue KlipperScreen installation?",
|
||||
|
||||
@@ -58,7 +58,6 @@ def install_mobileraker() -> None:
|
||||
"Mobileraker's companion's update manager configuration for Moonraker "
|
||||
"will not be added to any moonraker.conf.",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
if not get_confirm(
|
||||
"Continue Mobileraker's companion installation?",
|
||||
|
||||
28
kiauh/components/octoeverywhere/__init__.py
Normal file
28
kiauh/components/octoeverywhere/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# ======================================================================= #
|
||||
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
||||
# #
|
||||
# This file is part of KIAUH - Klipper Installation And Update Helper #
|
||||
# https://github.com/dw-0/kiauh #
|
||||
# #
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||
# ======================================================================= #
|
||||
from pathlib import Path
|
||||
|
||||
# repo
|
||||
OE_REPO = "https://github.com/QuinnDamerell/OctoPrint-OctoEverywhere.git"
|
||||
|
||||
# directories
|
||||
OE_DIR = Path.home().joinpath("octoeverywhere")
|
||||
OE_ENV_DIR = Path.home().joinpath("octoeverywhere-env")
|
||||
OE_STORE_DIR = OE_DIR.joinpath("octoeverywhere-store")
|
||||
|
||||
# files
|
||||
OE_REQ_FILE = OE_DIR.joinpath("requirements.txt")
|
||||
OE_DEPS_JSON_FILE = OE_DIR.joinpath("moonraker-system-dependencies.json")
|
||||
OE_INSTALL_SCRIPT = OE_DIR.joinpath("install.sh")
|
||||
OE_UPDATE_SCRIPT = OE_DIR.joinpath("update.sh")
|
||||
|
||||
# filenames
|
||||
OE_CFG_NAME = "octoeverywhere.conf"
|
||||
OE_LOG_NAME = "octoeverywhere.log"
|
||||
OE_SYS_CFG_NAME = "octoeverywhere-system.cfg"
|
||||
88
kiauh/components/octoeverywhere/octoeverywhere.py
Normal file
88
kiauh/components/octoeverywhere/octoeverywhere.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# ======================================================================= #
|
||||
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
||||
# #
|
||||
# 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
|
||||
from subprocess import CalledProcessError, run
|
||||
from typing import List
|
||||
|
||||
from components.octoeverywhere import (
|
||||
OE_CFG_NAME,
|
||||
OE_DIR,
|
||||
OE_ENV_DIR,
|
||||
OE_INSTALL_SCRIPT,
|
||||
OE_LOG_NAME,
|
||||
OE_STORE_DIR,
|
||||
OE_SYS_CFG_NAME,
|
||||
OE_UPDATE_SCRIPT,
|
||||
)
|
||||
from core.instance_manager.base_instance import BaseInstance
|
||||
from utils.logger import Logger
|
||||
|
||||
|
||||
class Octoeverywhere(BaseInstance):
|
||||
@classmethod
|
||||
def blacklist(cls) -> List[str]:
|
||||
return ["None", "mcu", "bambu", "companion"]
|
||||
|
||||
def __init__(self, suffix: str = ""):
|
||||
super().__init__(instance_type=self, suffix=suffix)
|
||||
self.dir: Path = OE_DIR
|
||||
self.env_dir: Path = OE_ENV_DIR
|
||||
self.store_dir: Path = OE_STORE_DIR
|
||||
self._cfg_file = self.cfg_dir.joinpath(OE_CFG_NAME)
|
||||
self._sys_cfg_file = self.cfg_dir.joinpath(OE_SYS_CFG_NAME)
|
||||
self._log = self.log_dir.joinpath(OE_LOG_NAME)
|
||||
|
||||
@property
|
||||
def cfg_file(self) -> Path:
|
||||
return self._cfg_file
|
||||
|
||||
@property
|
||||
def sys_cfg_file(self) -> Path:
|
||||
return self._sys_cfg_file
|
||||
|
||||
@property
|
||||
def log(self) -> Path:
|
||||
return self._log
|
||||
|
||||
def create(self) -> None:
|
||||
Logger.print_status("Creating OctoEverywhere for Klipper Instance ...")
|
||||
|
||||
try:
|
||||
cmd = f"{OE_INSTALL_SCRIPT} {self.cfg_dir}/moonraker.conf"
|
||||
run(cmd, check=True, shell=True)
|
||||
|
||||
except CalledProcessError as e:
|
||||
Logger.print_error(f"Error creating instance: {e}")
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def update():
|
||||
try:
|
||||
run(str(OE_UPDATE_SCRIPT), check=True, shell=True, cwd=OE_DIR)
|
||||
|
||||
except CalledProcessError as e:
|
||||
Logger.print_error(f"Error updating OctoEverywhere for Klipper: {e}")
|
||||
raise
|
||||
|
||||
def delete(self) -> None:
|
||||
service_file = self.get_service_file_name(extension=True)
|
||||
service_file_path = self.get_service_file_path()
|
||||
|
||||
Logger.print_status(
|
||||
f"Deleting OctoEverywhere for Klipper Instance: {service_file}"
|
||||
)
|
||||
|
||||
try:
|
||||
command = ["sudo", "rm", "-f", service_file_path]
|
||||
run(command, check=True)
|
||||
Logger.print_ok(f"Service file deleted: {service_file_path}")
|
||||
except CalledProcessError as e:
|
||||
Logger.print_error(f"Error deleting service file: {e}")
|
||||
raise
|
||||
231
kiauh/components/octoeverywhere/octoeverywhere_setup.py
Normal file
231
kiauh/components/octoeverywhere/octoeverywhere_setup.py
Normal file
@@ -0,0 +1,231 @@
|
||||
# ======================================================================= #
|
||||
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
||||
# #
|
||||
# This file is part of KIAUH - Klipper Installation And Update Helper #
|
||||
# https://github.com/dw-0/kiauh #
|
||||
# #
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||
# ======================================================================= #
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from components.moonraker.moonraker import Moonraker
|
||||
from components.octoeverywhere import (
|
||||
OE_DEPS_JSON_FILE,
|
||||
OE_DIR,
|
||||
OE_ENV_DIR,
|
||||
OE_INSTALL_SCRIPT,
|
||||
OE_LOG_NAME,
|
||||
OE_REPO,
|
||||
OE_REQ_FILE,
|
||||
OE_SYS_CFG_NAME,
|
||||
)
|
||||
from components.octoeverywhere.octoeverywhere import Octoeverywhere
|
||||
from core.instance_manager.instance_manager import InstanceManager
|
||||
from utils.common import (
|
||||
check_install_dependencies,
|
||||
get_install_status,
|
||||
moonraker_exists,
|
||||
)
|
||||
from utils.config_utils import (
|
||||
remove_config_section,
|
||||
)
|
||||
from utils.fs_utils import run_remove_routines
|
||||
from utils.git_utils import git_clone_wrapper
|
||||
from utils.input_utils import get_confirm
|
||||
from utils.logger import DialogType, Logger
|
||||
from utils.sys_utils import (
|
||||
cmd_sysctl_manage,
|
||||
install_python_requirements,
|
||||
parse_packages_from_file,
|
||||
)
|
||||
from utils.types import ComponentStatus
|
||||
|
||||
|
||||
def get_octoeverywhere_status() -> ComponentStatus:
|
||||
return get_install_status(OE_DIR, OE_ENV_DIR, Octoeverywhere)
|
||||
|
||||
|
||||
def install_octoeverywhere() -> None:
|
||||
Logger.print_status("Installing OctoEverywhere for Klipper ...")
|
||||
|
||||
# check if moonraker is installed. if not, notify the user and exit
|
||||
if not moonraker_exists():
|
||||
return
|
||||
|
||||
force_clone = False
|
||||
oe_im = InstanceManager(Octoeverywhere)
|
||||
oe_instances: List[Octoeverywhere] = oe_im.instances
|
||||
if oe_instances:
|
||||
Logger.print_dialog(
|
||||
DialogType.INFO,
|
||||
[
|
||||
"OctoEverywhere is already installed!",
|
||||
"It is safe to run the installer again to link your "
|
||||
"printer or repair any issues.",
|
||||
],
|
||||
padding_top=0,
|
||||
padding_bottom=0,
|
||||
)
|
||||
if not get_confirm("Re-run OctoEverywhere installation?"):
|
||||
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
|
||||
return
|
||||
else:
|
||||
Logger.print_status("Re-Installing OctoEverywhere for Klipper ...")
|
||||
force_clone = True
|
||||
|
||||
mr_im = InstanceManager(Moonraker)
|
||||
mr_instances: List[Moonraker] = mr_im.instances
|
||||
|
||||
mr_names = [f"● {moonraker.data_dir_name}" for moonraker in mr_instances]
|
||||
if len(mr_names) > 1:
|
||||
Logger.print_dialog(
|
||||
DialogType.INFO,
|
||||
[
|
||||
"The following Moonraker instances were found:",
|
||||
*mr_names,
|
||||
"\n\n",
|
||||
"The setup will apply the same names to OctoEverywhere!",
|
||||
],
|
||||
padding_top=0,
|
||||
padding_bottom=0,
|
||||
)
|
||||
|
||||
if not get_confirm(
|
||||
"Continue OctoEverywhere for Klipper installation?",
|
||||
default_choice=True,
|
||||
allow_go_back=True,
|
||||
):
|
||||
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
|
||||
return
|
||||
|
||||
try:
|
||||
git_clone_wrapper(OE_REPO, OE_DIR, force=force_clone)
|
||||
|
||||
for moonraker in mr_instances:
|
||||
oe_im.current_instance = Octoeverywhere(suffix=moonraker.suffix)
|
||||
oe_im.create_instance()
|
||||
|
||||
mr_im.restart_all_instance()
|
||||
|
||||
Logger.print_dialog(
|
||||
DialogType.SUCCESS,
|
||||
["OctoEverywhere for Klipper successfully installed!"],
|
||||
center_content=True,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
Logger.print_error(
|
||||
f"Error during OctoEverywhere for Klipper installation:\n{e}"
|
||||
)
|
||||
|
||||
|
||||
def update_octoeverywhere() -> None:
|
||||
Logger.print_status("Updating OctoEverywhere for Klipper ...")
|
||||
try:
|
||||
Octoeverywhere.update()
|
||||
Logger.print_dialog(
|
||||
DialogType.SUCCESS,
|
||||
["OctoEverywhere for Klipper successfully updated!"],
|
||||
center_content=True,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
Logger.print_error(f"Error during OctoEverywhere for Klipper update:\n{e}")
|
||||
|
||||
|
||||
def remove_octoeverywhere() -> None:
|
||||
Logger.print_status("Removing OctoEverywhere for Klipper ...")
|
||||
mr_im = InstanceManager(Moonraker)
|
||||
mr_instances: List[Moonraker] = mr_im.instances
|
||||
ob_im = InstanceManager(Octoeverywhere)
|
||||
ob_instances: List[Octoeverywhere] = ob_im.instances
|
||||
|
||||
try:
|
||||
remove_oe_instances(ob_im, ob_instances)
|
||||
remove_oe_dir()
|
||||
remove_oe_env()
|
||||
remove_config_section(f"include {OE_SYS_CFG_NAME}", mr_instances)
|
||||
delete_oe_logs(ob_instances)
|
||||
Logger.print_dialog(
|
||||
DialogType.SUCCESS,
|
||||
["OctoEverywhere for Klipper successfully removed!"],
|
||||
center_content=True,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
Logger.print_error(f"Error during OctoEverywhere for Klipper removal:\n{e}")
|
||||
|
||||
|
||||
def install_oe_dependencies() -> None:
|
||||
oe_deps = []
|
||||
if OE_DEPS_JSON_FILE.exists():
|
||||
with open(OE_DEPS_JSON_FILE, "r") as deps:
|
||||
oe_deps = json.load(deps).get("debian", [])
|
||||
elif OE_INSTALL_SCRIPT.exists():
|
||||
oe_deps = parse_packages_from_file(OE_INSTALL_SCRIPT)
|
||||
|
||||
if not oe_deps:
|
||||
raise ValueError("Error reading OctoEverywhere dependencies!")
|
||||
|
||||
check_install_dependencies(oe_deps)
|
||||
install_python_requirements(OE_ENV_DIR, OE_REQ_FILE)
|
||||
|
||||
|
||||
def remove_oe_instances(
|
||||
instance_manager: InstanceManager,
|
||||
instance_list: List[Octoeverywhere],
|
||||
) -> None:
|
||||
if not instance_list:
|
||||
Logger.print_info("No OctoEverywhere instances found. Skipped ...")
|
||||
return
|
||||
|
||||
for instance in instance_list:
|
||||
Logger.print_status(f"Removing instance {instance.get_service_file_name()} ...")
|
||||
instance_manager.current_instance = instance
|
||||
instance_manager.stop_instance()
|
||||
instance_manager.disable_instance()
|
||||
instance_manager.delete_instance()
|
||||
|
||||
cmd_sysctl_manage("daemon-reload")
|
||||
|
||||
|
||||
def remove_oe_dir() -> None:
|
||||
Logger.print_status("Removing OctoEverywhere for Klipper directory ...")
|
||||
|
||||
if not OE_DIR.exists():
|
||||
Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...")
|
||||
return
|
||||
|
||||
run_remove_routines(OE_DIR)
|
||||
|
||||
|
||||
def remove_oe_env() -> None:
|
||||
Logger.print_status("Removing OctoEverywhere for Klipper environment ...")
|
||||
|
||||
if not OE_ENV_DIR.exists():
|
||||
Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...")
|
||||
return
|
||||
|
||||
run_remove_routines(OE_ENV_DIR)
|
||||
|
||||
|
||||
def delete_oe_logs(instances: List[Octoeverywhere]) -> None:
|
||||
Logger.print_status("Removing OctoEverywhere logs ...")
|
||||
|
||||
all_logfiles = []
|
||||
for instance in instances:
|
||||
all_logfiles = list(instance.log_dir.glob(f"{OE_LOG_NAME}*"))
|
||||
|
||||
install_log = Path.home().joinpath("octoeverywhere-installer.log")
|
||||
if install_log.exists():
|
||||
all_logfiles.append(install_log)
|
||||
|
||||
if not all_logfiles:
|
||||
Logger.print_info("No OctoEverywhere logs found. Skipped ...")
|
||||
return
|
||||
|
||||
for log in all_logfiles:
|
||||
Logger.print_status(f"Remove '{log}'")
|
||||
run_remove_routines(log)
|
||||
@@ -7,82 +7,57 @@
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||
# ======================================================================= #
|
||||
|
||||
import textwrap
|
||||
from typing import List
|
||||
|
||||
from components.webui_client.base_data import BaseWebClient
|
||||
from core.menus.base_menu import print_back_footer
|
||||
from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
|
||||
from utils.logger import DialogType, Logger
|
||||
|
||||
|
||||
def print_moonraker_not_found_dialog():
|
||||
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
|
||||
line2 = f"{COLOR_YELLOW}No local Moonraker installation was found!{RESET_FORMAT}"
|
||||
dialog = textwrap.dedent(
|
||||
f"""
|
||||
/=======================================================\\
|
||||
| {line1:<63}|
|
||||
| {line2:<63}|
|
||||
|-------------------------------------------------------|
|
||||
| It is possible to install Mainsail without a local |
|
||||
| Moonraker installation. If you continue, you need to |
|
||||
| make sure, that Moonraker is installed on another |
|
||||
| machine in your network. Otherwise Mainsail will NOT |
|
||||
| work correctly. |
|
||||
"""
|
||||
)[1:]
|
||||
|
||||
print(dialog, end="")
|
||||
print_back_footer()
|
||||
Logger.print_dialog(
|
||||
DialogType.WARNING,
|
||||
[
|
||||
"No local Moonraker installation was found!",
|
||||
"\n\n",
|
||||
"It is possible to install Mainsail without a local Moonraker installation. "
|
||||
"If you continue, you need to make sure, that Moonraker is installed on "
|
||||
"another machine in your network. Otherwise Mainsail will NOT work "
|
||||
"correctly.",
|
||||
],
|
||||
padding_top=0,
|
||||
padding_bottom=0,
|
||||
)
|
||||
|
||||
|
||||
def print_client_already_installed_dialog(name: str):
|
||||
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
|
||||
line2 = f"{COLOR_YELLOW}{name} seems to be already installed!{RESET_FORMAT}"
|
||||
line3 = f"If you continue, your current {name}"
|
||||
dialog = textwrap.dedent(
|
||||
f"""
|
||||
/=======================================================\\
|
||||
| {line1:<63}|
|
||||
| {line2:<63}|
|
||||
|-------------------------------------------------------|
|
||||
| {line3:<54}|
|
||||
| installation will be overwritten. |
|
||||
"""
|
||||
)[1:]
|
||||
|
||||
print(dialog, end="")
|
||||
print_back_footer()
|
||||
Logger.print_dialog(
|
||||
DialogType.WARNING,
|
||||
[
|
||||
f"{name} seems to be already installed!",
|
||||
f"If you continue, your current {name} installation will be overwritten.",
|
||||
],
|
||||
padding_top=0,
|
||||
padding_bottom=0,
|
||||
)
|
||||
|
||||
|
||||
def print_client_port_select_dialog(name: str, port: int, ports_in_use: List[int]):
|
||||
port = f"{COLOR_CYAN}{port}{RESET_FORMAT}"
|
||||
line1 = f"Please select the port, {name} should be served on."
|
||||
line2 = f"In case you need {name} to be served on a specific"
|
||||
dialog = textwrap.dedent(
|
||||
f"""
|
||||
/=======================================================\\
|
||||
| {line1:<54}|
|
||||
| If you are unsure what to select, hit Enter to apply |
|
||||
| the suggested value of: {port:38} |
|
||||
| |
|
||||
| {line2:<54}|
|
||||
| port, you can set it now. Make sure the port is not |
|
||||
| used by any other application on your system! |
|
||||
"""
|
||||
)[1:]
|
||||
|
||||
if len(ports_in_use) > 0:
|
||||
dialog += "|-------------------------------------------------------|\n"
|
||||
dialog += "| The following ports were found to be in use already: |\n"
|
||||
for port in ports_in_use:
|
||||
port = f"{COLOR_CYAN}● {port}{RESET_FORMAT}"
|
||||
dialog += f"| {port:60} |\n"
|
||||
|
||||
dialog += "\\=======================================================/\n"
|
||||
|
||||
print(dialog, end="")
|
||||
Logger.print_dialog(
|
||||
DialogType.CUSTOM,
|
||||
[
|
||||
f"Please select the port, {name} should be served on. If your are unsure "
|
||||
f"what to select, hit Enter to apply the suggested value of: {port}",
|
||||
"\n\n",
|
||||
f"In case you need {name} to be served on a specific port, you can set it "
|
||||
f"now. Make sure that the port is not already used by another application "
|
||||
f"on your system!",
|
||||
"\n\n",
|
||||
"The following ports were found to be in use already:",
|
||||
*[f"● {port}" for port in ports_in_use],
|
||||
],
|
||||
padding_top=0,
|
||||
padding_bottom=0,
|
||||
)
|
||||
|
||||
|
||||
def print_install_client_config_dialog(client: BaseWebClient) -> None:
|
||||
@@ -100,7 +75,8 @@ def print_install_client_config_dialog(client: BaseWebClient) -> None:
|
||||
"If you already use these macros skip this step. Otherwise you should "
|
||||
"consider to answer with 'Y' to download the recommended macros.",
|
||||
],
|
||||
end="",
|
||||
padding_top=0,
|
||||
padding_bottom=0,
|
||||
)
|
||||
|
||||
|
||||
@@ -115,5 +91,4 @@ def print_ipv6_warning_dialog() -> None:
|
||||
"If you think this warning is a false alarm, and you are sure that "
|
||||
"IPv6 is disabled, you can continue with the installation.",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
@@ -73,10 +73,7 @@ def install_client(client: BaseWebClient) -> None:
|
||||
enable_remotemode = False
|
||||
if not mr_instances:
|
||||
print_moonraker_not_found_dialog()
|
||||
if not get_confirm(
|
||||
f"Continue {client.display_name} installation?",
|
||||
allow_go_back=True,
|
||||
):
|
||||
if not get_confirm(f"Continue {client.display_name} installation?"):
|
||||
return
|
||||
|
||||
# if moonraker is not installed or multiple instances
|
||||
|
||||
@@ -15,6 +15,7 @@ from components.klipper import klipper_setup
|
||||
from components.klipperscreen.klipperscreen import install_klipperscreen
|
||||
from components.mobileraker.mobileraker import install_mobileraker
|
||||
from components.moonraker import moonraker_setup
|
||||
from components.octoeverywhere.octoeverywhere_setup import install_octoeverywhere
|
||||
from components.webui_client import client_setup
|
||||
from components.webui_client.client_config import client_config_setup
|
||||
from components.webui_client.fluidd_data import FluiddData
|
||||
@@ -49,6 +50,7 @@ class InstallMenu(BaseMenu):
|
||||
"7": Option(method=self.install_klipperscreen, menu=False),
|
||||
"8": Option(method=self.install_mobileraker, menu=False),
|
||||
"9": Option(method=self.install_crowsnest, menu=False),
|
||||
"10": Option(method=self.install_octoeverywhere, menu=False),
|
||||
}
|
||||
|
||||
def print_menu(self):
|
||||
@@ -69,8 +71,8 @@ class InstallMenu(BaseMenu):
|
||||
║ 4) [Fluidd] │ Webcam Streamer: ║
|
||||
║ │ 9) [Crowsnest] ║
|
||||
║ Client-Config: │ ║
|
||||
║ 5) [Mainsail-Config] │ ║
|
||||
║ 6) [Fluidd-Config] │ ║
|
||||
║ 5) [Mainsail-Config] │ Remote Access: ║
|
||||
║ 6) [Fluidd-Config] │ 10) [OctoEverywhere] ║
|
||||
║ │ ║
|
||||
╟───────────────────────────┴───────────────────────────╢
|
||||
"""
|
||||
@@ -103,3 +105,6 @@ class InstallMenu(BaseMenu):
|
||||
|
||||
def install_crowsnest(self, **kwargs):
|
||||
install_crowsnest()
|
||||
|
||||
def install_octoeverywhere(self, **kwargs):
|
||||
install_octoeverywhere()
|
||||
|
||||
@@ -16,6 +16,7 @@ from components.klipperscreen.klipperscreen import get_klipperscreen_status
|
||||
from components.log_uploads.menus.log_upload_menu import LogUploadMenu
|
||||
from components.mobileraker.mobileraker import get_mobileraker_status
|
||||
from components.moonraker.moonraker_utils import get_moonraker_status
|
||||
from components.octoeverywhere.octoeverywhere_setup import get_octoeverywhere_status
|
||||
from components.webui_client.client_utils import (
|
||||
get_client_status,
|
||||
get_current_client_config,
|
||||
@@ -54,7 +55,7 @@ class MainMenu(BaseMenu):
|
||||
|
||||
self.kl_status = self.kl_repo = self.mr_status = self.mr_repo = ""
|
||||
self.ms_status = self.fl_status = self.ks_status = self.mb_status = ""
|
||||
self.cn_status = self.cc_status = ""
|
||||
self.cn_status = self.cc_status = self.oe_status = ""
|
||||
self.init_status()
|
||||
|
||||
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None:
|
||||
@@ -74,12 +75,12 @@ class MainMenu(BaseMenu):
|
||||
}
|
||||
|
||||
def init_status(self) -> None:
|
||||
status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn"]
|
||||
status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn", "oe"]
|
||||
for var in status_vars:
|
||||
setattr(
|
||||
self,
|
||||
f"{var}_status",
|
||||
f"{COLOR_RED}Not installed!{RESET_FORMAT}",
|
||||
f"{COLOR_RED}Not installed{RESET_FORMAT}",
|
||||
)
|
||||
|
||||
def fetch_status(self) -> None:
|
||||
@@ -91,6 +92,7 @@ class MainMenu(BaseMenu):
|
||||
self._get_component_status("ks", get_klipperscreen_status)
|
||||
self._get_component_status("mb", get_mobileraker_status)
|
||||
self._get_component_status("cn", get_crowsnest_status)
|
||||
self._get_component_status("oe", get_octoeverywhere_status)
|
||||
|
||||
def _get_component_status(self, name: str, status_fn: callable, *args) -> None:
|
||||
status_data: ComponentStatus = status_fn(*args)
|
||||
@@ -141,6 +143,7 @@ class MainMenu(BaseMenu):
|
||||
║ │ ║
|
||||
║ Community: │ KlipperScreen: {self.ks_status:<{pad2}} ║
|
||||
║ E) [Extensions] │ Mobileraker: {self.mb_status:<{pad2}} ║
|
||||
║ │ OctoEverywhere: {self.oe_status:<{pad2}} ║
|
||||
║ │ Crowsnest: {self.cn_status:<{pad2}} ║
|
||||
╟──────────────────┼────────────────────────────────────╢
|
||||
║ {footer1:^25} │ {footer2:^43} ║
|
||||
|
||||
@@ -17,6 +17,7 @@ from components.mobileraker.mobileraker import remove_mobileraker
|
||||
from components.moonraker.menus.moonraker_remove_menu import (
|
||||
MoonrakerRemoveMenu,
|
||||
)
|
||||
from components.octoeverywhere.octoeverywhere_setup import remove_octoeverywhere
|
||||
from components.webui_client.fluidd_data import FluiddData
|
||||
from components.webui_client.mainsail_data import MainsailData
|
||||
from components.webui_client.menus.client_remove_menu import ClientRemoveMenu
|
||||
@@ -48,6 +49,7 @@ class RemoveMenu(BaseMenu):
|
||||
"5": Option(method=self.remove_klipperscreen, menu=True),
|
||||
"6": Option(method=self.remove_mobileraker, menu=True),
|
||||
"7": Option(method=self.remove_crowsnest, menu=True),
|
||||
"8": Option(method=self.remove_octoeverywhere, menu=True),
|
||||
}
|
||||
|
||||
def print_menu(self):
|
||||
@@ -61,14 +63,16 @@ class RemoveMenu(BaseMenu):
|
||||
╟───────────────────────────────────────────────────────╢
|
||||
║ INFO: Configurations and/or any backups will be kept! ║
|
||||
╟───────────────────────────┬───────────────────────────╢
|
||||
║ Firmware & API: │ Touchscreen GUI: ║
|
||||
║ 1) [Klipper] │ 5) [KlipperScreen] ║
|
||||
║ Firmware & API: │ Android / iOS: ║
|
||||
║ 1) [Klipper] │ 6) [Mobileraker] ║
|
||||
║ 2) [Moonraker] │ ║
|
||||
║ │ Android / iOS: ║
|
||||
║ Klipper Webinterface: │ 6) [Mobileraker] ║
|
||||
║ │ Webcam Streamer: ║
|
||||
║ Klipper Webinterface: │ 7) [Crowsnest] ║
|
||||
║ 3) [Mainsail] │ ║
|
||||
║ 4) [Fluidd] │ Webcam Streamer: ║
|
||||
║ │ 7) [Crowsnest] ║
|
||||
║ 4) [Fluidd] │ Remote Access: ║
|
||||
║ │ 8) [OctoEverywhere] ║
|
||||
║ Touchscreen GUI: │ ║
|
||||
║ 5) [KlipperScreen] │ ║
|
||||
╟───────────────────────────┴───────────────────────────╢
|
||||
"""
|
||||
)[1:]
|
||||
@@ -94,3 +98,6 @@ class RemoveMenu(BaseMenu):
|
||||
|
||||
def remove_crowsnest(self, **kwargs):
|
||||
remove_crowsnest()
|
||||
|
||||
def remove_octoeverywhere(self, **kwargs):
|
||||
remove_octoeverywhere()
|
||||
|
||||
@@ -144,7 +144,6 @@ class SettingsMenu(BaseMenu):
|
||||
f"New {display_name} repository branch:",
|
||||
f"● {branch}",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
if get_confirm("Apply changes?", allow_go_back=True):
|
||||
|
||||
@@ -25,6 +25,10 @@ from components.mobileraker.mobileraker import (
|
||||
)
|
||||
from components.moonraker.moonraker_setup import update_moonraker
|
||||
from components.moonraker.moonraker_utils import get_moonraker_status
|
||||
from components.octoeverywhere.octoeverywhere_setup import (
|
||||
get_octoeverywhere_status,
|
||||
update_octoeverywhere,
|
||||
)
|
||||
from components.webui_client.client_config.client_config_setup import (
|
||||
update_client_config,
|
||||
)
|
||||
@@ -57,7 +61,7 @@ class UpdateMenu(BaseMenu):
|
||||
self.ms_local = self.ms_remote = self.fl_local = self.fl_remote = ""
|
||||
self.mc_local = self.mc_remote = self.fc_local = self.fc_remote = ""
|
||||
self.ks_local = self.ks_remote = self.mb_local = self.mb_remote = ""
|
||||
self.cn_local = self.cn_remote = ""
|
||||
self.cn_local = self.cn_remote = self.oe_local = self.oe_remote = ""
|
||||
|
||||
self.mainsail_data = MainsailData()
|
||||
self.fluidd_data = FluiddData()
|
||||
@@ -82,7 +86,8 @@ class UpdateMenu(BaseMenu):
|
||||
"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),
|
||||
"10": Option(self.update_octoeverywhere, menu=False),
|
||||
"11": Option(self.upgrade_system_packages, menu=False),
|
||||
}
|
||||
|
||||
def print_menu(self):
|
||||
@@ -114,8 +119,9 @@ class UpdateMenu(BaseMenu):
|
||||
║ 7) KlipperScreen │ {self.ks_local:<22} │ {self.ks_remote:<22} ║
|
||||
║ 8) Mobileraker │ {self.mb_local:<22} │ {self.mb_remote:<22} ║
|
||||
║ 9) Crowsnest │ {self.cn_local:<22} │ {self.cn_remote:<22} ║
|
||||
║ 10) OctoEverywhere │ {self.oe_local:<22} │ {self.oe_remote:<22} ║
|
||||
║ ├───────────────┴───────────────╢
|
||||
║ 10) System │ ║
|
||||
║ 11) System │ ║
|
||||
╟───────────────────────┴───────────────────────────────╢
|
||||
"""
|
||||
)[1:]
|
||||
@@ -151,6 +157,9 @@ class UpdateMenu(BaseMenu):
|
||||
def update_crowsnest(self, **kwargs):
|
||||
update_crowsnest()
|
||||
|
||||
def update_octoeverywhere(self, **kwargs):
|
||||
update_octoeverywhere()
|
||||
|
||||
def upgrade_system_packages(self, **kwargs): ...
|
||||
|
||||
def _fetch_update_status(self):
|
||||
@@ -172,6 +181,8 @@ class UpdateMenu(BaseMenu):
|
||||
self._get_update_status("mb", get_mobileraker_status)
|
||||
# crowsnest
|
||||
self._get_update_status("cn", get_crowsnest_status)
|
||||
# octoeverywhere
|
||||
self._get_update_status("oe", get_octoeverywhere_status)
|
||||
|
||||
def _format_local_status(self, local_version, remote_version) -> str:
|
||||
if local_version == remote_version:
|
||||
|
||||
@@ -219,6 +219,5 @@ class KiauhSettings:
|
||||
"● default.kiauh.cfg",
|
||||
"● kiauh.cfg",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
kill()
|
||||
|
||||
@@ -57,6 +57,7 @@ class ObicoExtension(BaseExtension):
|
||||
|
||||
# if obico is already installed, ask if the user wants to repair an
|
||||
# incomplete installation or link to the obico server
|
||||
force_clone = False
|
||||
obico_im = InstanceManager(MoonrakerObico)
|
||||
obico_instances: List[MoonrakerObico] = obico_im.instances
|
||||
if obico_instances:
|
||||
@@ -74,6 +75,7 @@ class ObicoExtension(BaseExtension):
|
||||
return
|
||||
else:
|
||||
Logger.print_status("Re-Installing Obico for Klipper ...")
|
||||
force_clone = True
|
||||
|
||||
# let the user confirm installation
|
||||
kl_im = InstanceManager(Klipper)
|
||||
@@ -89,7 +91,7 @@ class ObicoExtension(BaseExtension):
|
||||
return
|
||||
|
||||
try:
|
||||
git_clone_wrapper(OBICO_REPO, OBICO_DIR)
|
||||
git_clone_wrapper(OBICO_REPO, OBICO_DIR, force=force_clone)
|
||||
self._install_dependencies()
|
||||
|
||||
# ask the user for the obico server url
|
||||
@@ -192,7 +194,6 @@ class ObicoExtension(BaseExtension):
|
||||
"http://server_ip:port",
|
||||
"For instance, 'http://192.168.0.5:3334'.",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
def _print_moonraker_instances(self, mr_instances) -> None:
|
||||
@@ -206,7 +207,6 @@ class ObicoExtension(BaseExtension):
|
||||
"\n\n",
|
||||
"The setup will apply the same names to Obico!",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
def _print_is_already_installed(self) -> None:
|
||||
@@ -214,14 +214,13 @@ class ObicoExtension(BaseExtension):
|
||||
DialogType.INFO,
|
||||
[
|
||||
"Obico is already installed!",
|
||||
"It is save to run the installer again to link your "
|
||||
"It is safe to run the installer again to link your "
|
||||
"printer or repair any issues.",
|
||||
"\n\n",
|
||||
"You can perform the following actions:",
|
||||
"L) Link printer to the Obico server",
|
||||
"R) Repair installation",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
|
||||
def _get_server_url(self) -> None:
|
||||
@@ -324,7 +323,6 @@ class ObicoExtension(BaseExtension):
|
||||
"If you don't want to link the printer now, you can restart the "
|
||||
"linking process later by running this installer again.",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
if not get_confirm("Do you want to link the printers now?"):
|
||||
Logger.print_info("Linking to Obico server skipped ...")
|
||||
|
||||
@@ -150,7 +150,6 @@ def moonraker_exists(name: str = "") -> bool:
|
||||
"No Moonraker instances found!",
|
||||
f"{info}. Please install Moonraker first!",
|
||||
],
|
||||
end="",
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -59,9 +59,9 @@ def create_symlink(source: Path, target: Path, sudo=False) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def remove_with_sudo(file_path: Path) -> None:
|
||||
def remove_with_sudo(file: Path) -> None:
|
||||
try:
|
||||
cmd = ["sudo", "rm", "-f", file_path]
|
||||
cmd = ["sudo", "rm", "-rf", file]
|
||||
run(cmd, stderr=PIPE, check=True)
|
||||
except CalledProcessError as e:
|
||||
Logger.print_error(f"Failed to remove file: {e}")
|
||||
@@ -79,6 +79,30 @@ def remove_file(file_path: Path, sudo=False) -> None:
|
||||
raise
|
||||
|
||||
|
||||
def run_remove_routines(file: Path) -> None:
|
||||
try:
|
||||
if not file.exists():
|
||||
Logger.print_info(f"File '{file}' does not exist. Skipped ...")
|
||||
return
|
||||
|
||||
if file.is_dir():
|
||||
shutil.rmtree(file)
|
||||
elif file.is_file():
|
||||
file.unlink()
|
||||
else:
|
||||
raise OSError(f"File '{file}' is neither a file nor a directory!")
|
||||
Logger.print_ok("Successfully removed!")
|
||||
except OSError as e:
|
||||
Logger.print_error(f"Unable to delete '{file}':\n{e}")
|
||||
try:
|
||||
Logger.print_info("Trying to remove with sudo ...")
|
||||
remove_with_sudo(file)
|
||||
Logger.print_ok("Successfully removed!")
|
||||
except CalledProcessError as e:
|
||||
Logger.print_error(f"Error deleting '{file}' with sudo:\n{e}")
|
||||
Logger.print_error("Remove this directory manually!")
|
||||
|
||||
|
||||
def unzip(filepath: Path, target_dir: Path) -> None:
|
||||
"""
|
||||
Helper function to unzip a zip-archive into a target directory |
|
||||
|
||||
@@ -14,7 +14,7 @@ from utils.logger import Logger
|
||||
|
||||
|
||||
def git_clone_wrapper(
|
||||
repo: str, target_dir: Path, branch: Optional[str] = None
|
||||
repo: str, target_dir: Path, branch: Optional[str] = None, force: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
Clones a repository from the given URL and checks out the specified branch if given.
|
||||
@@ -22,6 +22,7 @@ def git_clone_wrapper(
|
||||
:param repo: The URL of the repository to clone.
|
||||
:param branch: The branch to check out. If None, the default branch will be checked out.
|
||||
:param target_dir: The directory where the repository will be cloned.
|
||||
:param force: Force the cloning of the repository even if it already exists.
|
||||
:return: None
|
||||
"""
|
||||
log = f"Cloning repository from '{repo}'"
|
||||
@@ -29,7 +30,7 @@ def git_clone_wrapper(
|
||||
try:
|
||||
if Path(target_dir).exists():
|
||||
question = f"'{target_dir}' already exists. Overwrite?"
|
||||
if not get_confirm(question, default_choice=False):
|
||||
if not force and not get_confirm(question, default_choice=False):
|
||||
Logger.print_info("Skip cloning of repository ...")
|
||||
return
|
||||
shutil.rmtree(target_dir)
|
||||
|
||||
@@ -90,7 +90,8 @@ class Logger:
|
||||
center_content: bool = False,
|
||||
custom_title: str = None,
|
||||
custom_color: DialogCustomColor = None,
|
||||
end: str = "\n",
|
||||
padding_top: int = 1,
|
||||
padding_bottom: int = 1,
|
||||
) -> None:
|
||||
dialog_color = Logger._get_dialog_color(title, custom_color)
|
||||
dialog_title = Logger._get_dialog_title(title, custom_title)
|
||||
@@ -99,10 +100,12 @@ class Logger:
|
||||
top = Logger._format_top_border(dialog_color)
|
||||
bottom = Logger._format_bottom_border()
|
||||
|
||||
print("\n" * padding_top)
|
||||
print(
|
||||
f"{top}{dialog_title_formatted}{dialog_content}{bottom}",
|
||||
end=end,
|
||||
end="",
|
||||
)
|
||||
print("\n" * padding_bottom)
|
||||
|
||||
@staticmethod
|
||||
def _get_dialog_title(title: DialogType, custom_title: str = None) -> str:
|
||||
@@ -120,18 +123,12 @@ class Logger:
|
||||
|
||||
@staticmethod
|
||||
def _format_top_border(color: str) -> str:
|
||||
return textwrap.dedent(
|
||||
f"""
|
||||
{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
"""
|
||||
)[1:-1]
|
||||
return f"{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
|
||||
|
||||
@staticmethod
|
||||
def _format_bottom_border() -> str:
|
||||
return textwrap.dedent(
|
||||
f"""
|
||||
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
{RESET_FORMAT}"""
|
||||
return (
|
||||
f"\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛{RESET_FORMAT}"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user