Compare commits

..

3 Commits

Author SHA1 Message Date
CODeRUS
b604d93d0c fix: RP2040 firmware detection (#533)
Co-authored-by: dw-0 <th33xitus@gmail.com>
2024-09-21 12:10:20 +02:00
dw-0
7e87f8af32 refactor: implement Mobileraker and OctoEverywhere as community extensions (#532)
* refactor: move mobileraker to extensions

Signed-off-by: Dominik Willner <th33xitus@gmail.com>

* refactor: move octoeverywhere to extensions

Signed-off-by: Dominik Willner <th33xitus@gmail.com>

---------

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-09-20 12:05:29 +02:00
dw-0
29b5ab00cd fix: correctly point to printers config dir (#531)
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-09-15 08:36:49 +02:00
24 changed files with 453 additions and 493 deletions

View File

@@ -17,8 +17,8 @@ from core.constants import (
COLOR_YELLOW,
RESET_FORMAT,
)
from core.instance_type import InstanceType
from core.menus.base_menu import print_back_footer
from utils.instance_type import InstanceType
@unique

View File

@@ -30,9 +30,10 @@ def find_firmware_file() -> bool:
f1 = "klipper.elf.hex"
f2 = "klipper.elf"
f3 = "klipper.bin"
f4 = "klipper.uf2"
fw_file_exists: bool = (
target.joinpath(f1).exists() and target.joinpath(f2).exists()
) or target.joinpath(f3).exists()
) or target.joinpath(f3).exists() or target.joinpath(f4).exists()
return target_exists and fw_file_exists

View File

@@ -1,201 +0,0 @@
# ======================================================================= #
# 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 shutil
from pathlib import Path
from subprocess import CalledProcessError, run
from typing import List
from components.klipper.klipper import Klipper
from components.mobileraker import (
MOBILERAKER_BACKUP_DIR,
MOBILERAKER_DIR,
MOBILERAKER_ENV_DIR,
MOBILERAKER_INSTALL_SCRIPT,
MOBILERAKER_LOG_NAME,
MOBILERAKER_REPO,
MOBILERAKER_REQ_FILE,
MOBILERAKER_SERVICE_FILE,
MOBILERAKER_SERVICE_NAME,
MOBILERAKER_UPDATER_SECTION_NAME,
)
from components.moonraker.moonraker import Moonraker
from core.backup_manager.backup_manager import BackupManager
from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogType, Logger
from core.settings.kiauh_settings import KiauhSettings
from core.types import ComponentStatus
from utils.common import check_install_dependencies, get_install_status
from utils.config_utils import add_config_section, remove_config_section
from utils.git_utils import (
git_clone_wrapper,
git_pull_wrapper,
)
from utils.input_utils import get_confirm
from utils.instance_utils import get_instances
from utils.sys_utils import (
check_python_version,
cmd_sysctl_service,
install_python_requirements,
remove_system_service,
)
def install_mobileraker() -> None:
Logger.print_status("Installing Mobileraker's companion ...")
if not check_python_version(3, 7):
return
mr_instances = get_instances(Moonraker)
if not mr_instances:
Logger.print_dialog(
DialogType.WARNING,
[
"Moonraker not found! Mobileraker's companion will not properly work "
"without a working Moonraker installation.",
"Mobileraker's companion's update manager configuration for Moonraker "
"will not be added to any moonraker.conf.",
],
)
if not get_confirm(
"Continue Mobileraker's companion installation?",
default_choice=False,
allow_go_back=True,
):
return
check_install_dependencies()
git_clone_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR)
try:
run(MOBILERAKER_INSTALL_SCRIPT.as_posix(), shell=True, check=True)
if mr_instances:
patch_mobileraker_update_manager(mr_instances)
InstanceManager.restart_all(mr_instances)
else:
Logger.print_info(
"Moonraker is not installed! Cannot add Mobileraker's "
"companion to update manager!"
)
Logger.print_ok("Mobileraker's companion successfully installed!")
except CalledProcessError as e:
Logger.print_error(f"Error installing Mobileraker's companion:\n{e}")
return
def patch_mobileraker_update_manager(instances: List[Moonraker]) -> None:
add_config_section(
section=MOBILERAKER_UPDATER_SECTION_NAME,
instances=instances,
options=[
("type", "git_repo"),
("path", MOBILERAKER_DIR.as_posix()),
("origin", MOBILERAKER_REPO),
("primary_branch", "main"),
("managed_services", "mobileraker"),
("env", f"{MOBILERAKER_ENV_DIR}/bin/python"),
("requirements", MOBILERAKER_REQ_FILE.as_posix()),
("install_script", MOBILERAKER_INSTALL_SCRIPT.as_posix()),
],
)
def update_mobileraker() -> None:
try:
if not MOBILERAKER_DIR.exists():
Logger.print_info(
"Mobileraker's companion does not seem to be installed! Skipping ..."
)
return
Logger.print_status("Updating Mobileraker's companion ...")
cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "stop")
settings = KiauhSettings()
if settings.kiauh.backup_before_update:
backup_mobileraker_dir()
git_pull_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR)
install_python_requirements(MOBILERAKER_ENV_DIR, MOBILERAKER_REQ_FILE)
cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "start")
Logger.print_ok("Mobileraker's companion updated successfully.", end="\n\n")
except CalledProcessError as e:
Logger.print_error(f"Error updating Mobileraker's companion:\n{e}")
return
def get_mobileraker_status() -> ComponentStatus:
return get_install_status(
MOBILERAKER_DIR,
MOBILERAKER_ENV_DIR,
files=[MOBILERAKER_SERVICE_FILE],
)
def remove_mobileraker() -> None:
Logger.print_status("Removing Mobileraker's companion ...")
try:
if MOBILERAKER_DIR.exists():
Logger.print_status("Removing Mobileraker's companion directory ...")
shutil.rmtree(MOBILERAKER_DIR)
Logger.print_ok("Mobileraker's companion directory successfully removed!")
else:
Logger.print_warn("Mobileraker's companion directory not found!")
if MOBILERAKER_ENV_DIR.exists():
Logger.print_status("Removing Mobileraker's companion environment ...")
shutil.rmtree(MOBILERAKER_ENV_DIR)
Logger.print_ok("Mobileraker's companion environment successfully removed!")
else:
Logger.print_warn("Mobileraker's companion environment not found!")
if MOBILERAKER_SERVICE_FILE.exists():
remove_system_service(MOBILERAKER_SERVICE_NAME)
kl_instances: List[Klipper] = get_instances(Klipper)
for instance in kl_instances:
logfile = instance.base.log_dir.joinpath(MOBILERAKER_LOG_NAME)
if logfile.exists():
Logger.print_status(f"Removing {logfile} ...")
Path(logfile).unlink()
Logger.print_ok(f"{logfile} successfully removed!")
mr_instances: List[Moonraker] = get_instances(Moonraker)
if mr_instances:
Logger.print_status(
"Removing Mobileraker's companion from update manager ..."
)
remove_config_section(MOBILERAKER_UPDATER_SECTION_NAME, mr_instances)
Logger.print_ok(
"Mobileraker's companion successfully removed from update manager!"
)
Logger.print_ok("Mobileraker's companion successfully removed!")
except Exception as e:
Logger.print_error(f"Error removing Mobileraker's companion:\n{e}")
def backup_mobileraker_dir() -> None:
bm = BackupManager()
bm.backup_directory(
MOBILERAKER_DIR.name,
source=MOBILERAKER_DIR,
target=MOBILERAKER_BACKUP_DIR,
)
bm.backup_directory(
MOBILERAKER_ENV_DIR.name,
source=MOBILERAKER_ENV_DIR,
target=MOBILERAKER_BACKUP_DIR,
)

View File

@@ -1,197 +0,0 @@
# ======================================================================= #
# 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 typing import List
from components.moonraker.moonraker import Moonraker
from components.octoeverywhere import (
OE_DEPS_JSON_FILE,
OE_DIR,
OE_ENV_DIR,
OE_INSTALL_SCRIPT,
OE_INSTALLER_LOG_FILE,
OE_REPO,
OE_REQ_FILE,
OE_SYS_CFG_NAME,
)
from components.octoeverywhere.octoeverywhere import Octoeverywhere
from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogType, Logger
from core.types import ComponentStatus
from utils.common import (
check_install_dependencies,
get_install_status,
moonraker_exists,
)
from utils.config_utils import (
remove_config_section,
)
from utils.fs_utils import run_remove_routines
from utils.git_utils import git_clone_wrapper
from utils.input_utils import get_confirm
from utils.instance_utils import get_instances
from utils.sys_utils import (
install_python_requirements,
parse_packages_from_file,
)
def get_octoeverywhere_status() -> ComponentStatus:
return get_install_status(OE_DIR, OE_ENV_DIR, Octoeverywhere)
def install_octoeverywhere() -> None:
Logger.print_status("Installing OctoEverywhere for Klipper ...")
# check if moonraker is installed. if not, notify the user and exit
if not moonraker_exists():
return
force_clone = False
oe_instances: List[Octoeverywhere] = get_instances(Octoeverywhere)
if oe_instances:
Logger.print_dialog(
DialogType.INFO,
[
"OctoEverywhere is already installed!",
"It is safe to run the installer again to link your "
"printer or repair any issues.",
],
)
if not get_confirm("Re-run OctoEverywhere installation?"):
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
return
else:
Logger.print_status("Re-Installing OctoEverywhere for Klipper ...")
force_clone = True
mr_instances: List[Moonraker] = get_instances(Moonraker)
mr_names = [f"{moonraker.data_dir.name}" for moonraker in mr_instances]
if len(mr_names) > 1:
Logger.print_dialog(
DialogType.INFO,
[
"The following Moonraker instances were found:",
*mr_names,
"\n\n",
"The setup will apply the same names to OctoEverywhere!",
],
)
if not get_confirm(
"Continue OctoEverywhere for Klipper installation?",
default_choice=True,
allow_go_back=True,
):
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
return
try:
git_clone_wrapper(OE_REPO, OE_DIR, force=force_clone)
for moonraker in mr_instances:
instance = Octoeverywhere(suffix=moonraker.suffix)
instance.create()
InstanceManager.restart_all(mr_instances)
Logger.print_dialog(
DialogType.SUCCESS,
["OctoEverywhere for Klipper successfully installed!"],
center_content=True,
)
except Exception as e:
Logger.print_error(
f"Error during OctoEverywhere for Klipper installation:\n{e}"
)
def update_octoeverywhere() -> None:
Logger.print_status("Updating OctoEverywhere for Klipper ...")
try:
Octoeverywhere.update()
Logger.print_dialog(
DialogType.SUCCESS,
["OctoEverywhere for Klipper successfully updated!"],
center_content=True,
)
except Exception as e:
Logger.print_error(f"Error during OctoEverywhere for Klipper update:\n{e}")
def remove_octoeverywhere() -> None:
Logger.print_status("Removing OctoEverywhere for Klipper ...")
mr_instances: List[Moonraker] = get_instances(Moonraker)
ob_instances: List[Octoeverywhere] = get_instances(Octoeverywhere)
try:
remove_oe_instances(ob_instances)
remove_oe_dir()
remove_oe_env()
remove_config_section(f"include {OE_SYS_CFG_NAME}", mr_instances)
run_remove_routines(OE_INSTALLER_LOG_FILE)
Logger.print_dialog(
DialogType.SUCCESS,
["OctoEverywhere for Klipper successfully removed!"],
center_content=True,
)
except Exception as e:
Logger.print_error(f"Error during OctoEverywhere for Klipper removal:\n{e}")
def install_oe_dependencies() -> None:
oe_deps = []
if OE_DEPS_JSON_FILE.exists():
with open(OE_DEPS_JSON_FILE, "r") as deps:
oe_deps = json.load(deps).get("debian", [])
elif OE_INSTALL_SCRIPT.exists():
oe_deps = parse_packages_from_file(OE_INSTALL_SCRIPT)
if not oe_deps:
raise ValueError("Error reading OctoEverywhere dependencies!")
check_install_dependencies({*oe_deps})
install_python_requirements(OE_ENV_DIR, OE_REQ_FILE)
def remove_oe_instances(
instance_list: List[Octoeverywhere],
) -> None:
if not instance_list:
Logger.print_info("No OctoEverywhere instances found. Skipped ...")
return
for instance in instance_list:
Logger.print_status(f"Removing instance {instance.service_file_path.stem} ...")
InstanceManager.remove(instance)
def remove_oe_dir() -> None:
Logger.print_status("Removing OctoEverywhere for Klipper directory ...")
if not OE_DIR.exists():
Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...")
return
run_remove_routines(OE_DIR)
def remove_oe_env() -> None:
Logger.print_status("Removing OctoEverywhere for Klipper environment ...")
if not OE_ENV_DIR.exists():
Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...")
return
run_remove_routines(OE_ENV_DIR)

View File

@@ -12,8 +12,8 @@ from pathlib import Path
from subprocess import CalledProcessError
from typing import List
from core.instance_type import InstanceType
from core.logger import Logger
from utils.instance_type import InstanceType
from utils.sys_utils import cmd_sysctl_service

View File

@@ -14,9 +14,7 @@ from typing import Type
from components.crowsnest.crowsnest import install_crowsnest
from components.klipper import klipper_setup
from components.klipperscreen.klipperscreen import install_klipperscreen
from components.mobileraker.mobileraker import install_mobileraker
from components.moonraker import moonraker_setup
from components.octoeverywhere.octoeverywhere_setup import install_octoeverywhere
from components.webui_client import client_setup
from components.webui_client.client_config import client_config_setup
from components.webui_client.fluidd_data import FluiddData
@@ -47,9 +45,7 @@ class InstallMenu(BaseMenu):
"5": Option(method=self.install_mainsail_config),
"6": Option(method=self.install_fluidd_config),
"7": Option(method=self.install_klipperscreen),
"8": Option(method=self.install_mobileraker),
"9": Option(method=self.install_crowsnest),
"10": Option(method=self.install_octoeverywhere),
"8": Option(method=self.install_crowsnest),
}
def print_menu(self) -> None:
@@ -64,15 +60,14 @@ class InstallMenu(BaseMenu):
║ Firmware & API: │ Touchscreen GUI: ║
║ 1) [Klipper] │ 7) [KlipperScreen] ║
║ 2) [Moonraker] │ ║
║ │ Android / iOS:
║ Webinterface: │ 8) [Mobileraker]
║ │ Webcam Streamer:
║ Webinterface: │ 8) [Crowsnest]
║ 3) [Mainsail] │ ║
║ 4) [Fluidd] │ Webcam Streamer:
║ │ 9) [Crowsnest] ║
║ Client-Config: │ ║
║ 5) [Mainsail-Config] │ Remote Access: ║
║ 6) [Fluidd-Config] │ 10) [OctoEverywhere] ║
║ 4) [Fluidd] │
║ │ ║
║ Client-Config: │ ║
║ 5) [Mainsail-Config] │ ║
║ 6) [Fluidd-Config] │ ║
╟───────────────────────────┴───────────────────────────╢
"""
)[1:]
@@ -99,11 +94,5 @@ class InstallMenu(BaseMenu):
def install_klipperscreen(self, **kwargs) -> None:
install_klipperscreen()
def install_mobileraker(self, **kwargs) -> None:
install_mobileraker()
def install_crowsnest(self, **kwargs) -> None:
install_crowsnest()
def install_octoeverywhere(self, **kwargs) -> None:
install_octoeverywhere()

View File

@@ -16,9 +16,7 @@ from components.crowsnest.crowsnest import get_crowsnest_status
from components.klipper.klipper_utils import get_klipper_status
from components.klipperscreen.klipperscreen import get_klipperscreen_status
from components.log_uploads.menus.log_upload_menu import LogUploadMenu
from components.mobileraker.mobileraker import get_mobileraker_status
from components.moonraker.moonraker_utils import get_moonraker_status
from components.octoeverywhere.octoeverywhere_setup import get_octoeverywhere_status
from components.webui_client.client_utils import (
get_client_status,
get_current_client_config,
@@ -59,8 +57,8 @@ class MainMenu(BaseMenu):
self.version = ""
self.kl_status = self.kl_owner = self.kl_repo = ""
self.mr_status = self.mr_owner = self.mr_repo = ""
self.ms_status = self.fl_status = self.ks_status = self.mb_status = ""
self.cn_status = self.cc_status = self.oe_status = ""
self.ms_status = self.fl_status = self.ks_status = ""
self.cn_status = self.cc_status = ""
self._init_status()
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -80,7 +78,7 @@ class MainMenu(BaseMenu):
}
def _init_status(self) -> None:
status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn", "oe"]
status_vars = ["kl", "mr", "ms", "fl", "ks", "cn"]
for var in status_vars:
setattr(
self,
@@ -96,9 +94,7 @@ class MainMenu(BaseMenu):
self._get_component_status("fl", get_client_status, FluiddData())
self.cc_status = get_current_client_config([MainsailData(), FluiddData()])
self._get_component_status("ks", get_klipperscreen_status)
self._get_component_status("mb", get_mobileraker_status)
self._get_component_status("cn", get_crowsnest_status)
self._get_component_status("oe", get_octoeverywhere_status)
def _get_component_status(self, name: str, status_fn: Callable, *args) -> None:
status_data: ComponentStatus = status_fn(*args)
@@ -155,8 +151,6 @@ class MainMenu(BaseMenu):
║ Community: │ Client-Config: {self.cc_status:<{pad2}}
║ E) [Extensions] │ ║
║ │ KlipperScreen: {self.ks_status:<{pad2}}
║ │ Mobileraker: {self.mb_status:<{pad2}}
║ │ OctoEverywhere: {self.oe_status:<{pad2}}
║ │ Crowsnest: {self.cn_status:<{pad2}}
╟──────────────────┼────────────────────────────────────╢
{footer1:^25}{footer2:^43}

View File

@@ -14,11 +14,9 @@ from typing import Type
from components.crowsnest.crowsnest import remove_crowsnest
from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu
from components.klipperscreen.klipperscreen import remove_klipperscreen
from components.mobileraker.mobileraker import remove_mobileraker
from components.moonraker.menus.moonraker_remove_menu import (
MoonrakerRemoveMenu,
)
from components.octoeverywhere.octoeverywhere_setup import remove_octoeverywhere
from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData
from components.webui_client.menus.client_remove_menu import ClientRemoveMenu
@@ -46,9 +44,8 @@ class RemoveMenu(BaseMenu):
"3": Option(method=self.remove_mainsail),
"4": Option(method=self.remove_fluidd),
"5": Option(method=self.remove_klipperscreen),
"6": Option(method=self.remove_mobileraker),
"7": Option(method=self.remove_crowsnest),
"8": Option(method=self.remove_octoeverywhere),
"6": Option(method=self.remove_crowsnest),
"7": Option(method=self.remove_octoeverywhere),
}
def print_menu(self) -> None:
@@ -62,16 +59,13 @@ class RemoveMenu(BaseMenu):
╟───────────────────────────────────────────────────────╢
║ INFO: Configurations and/or any backups will be kept! ║
╟───────────────────────────┬───────────────────────────╢
║ Firmware & API: │ Android / iOS:
║ 1) [Klipper] │ 6) [Mobileraker]
║ Firmware & API: │ Touchscreen GUI:
║ 1) [Klipper] │ 5) [KlipperScreen]
║ 2) [Moonraker] │ ║
║ │ Webcam Streamer: ║
║ Klipper Webinterface: │ 7) [Crowsnest] ║
║ Klipper Webinterface: │ 6) [Crowsnest] ║
║ 3) [Mainsail] │ ║
║ 4) [Fluidd] │ Remote Access:
║ │ 8) [OctoEverywhere] ║
║ Touchscreen GUI: │ ║
║ 5) [KlipperScreen] │ ║
║ 4) [Fluidd] │
╟───────────────────────────┴───────────────────────────╢
"""
)[1:]
@@ -92,11 +86,5 @@ class RemoveMenu(BaseMenu):
def remove_klipperscreen(self, **kwargs) -> None:
remove_klipperscreen()
def remove_mobileraker(self, **kwargs) -> None:
remove_mobileraker()
def remove_crowsnest(self, **kwargs) -> None:
remove_crowsnest()
def remove_octoeverywhere(self, **kwargs) -> None:
remove_octoeverywhere()

View File

@@ -20,16 +20,8 @@ from components.klipperscreen.klipperscreen import (
get_klipperscreen_status,
update_klipperscreen,
)
from components.mobileraker.mobileraker import (
get_mobileraker_status,
update_mobileraker,
)
from components.moonraker.moonraker_setup import update_moonraker
from components.moonraker.moonraker_utils import get_moonraker_status
from components.octoeverywhere.octoeverywhere_setup import (
get_octoeverywhere_status,
update_octoeverywhere,
)
from components.webui_client.client_config.client_config_setup import (
update_client_config,
)
@@ -76,9 +68,7 @@ class UpdateMenu(BaseMenu):
self.fluidd_local = self.fluidd_remote = ""
self.fluidd_config_local = self.fluidd_config_remote = ""
self.klipperscreen_local = self.klipperscreen_remote = ""
self.mobileraker_local = self.mobileraker_remote = ""
self.crowsnest_local = self.crowsnest_remote = ""
self.octoeverywhere_local = self.octoeverywhere_remote = ""
self.mainsail_data = MainsailData()
self.fluidd_data = FluiddData()
@@ -89,10 +79,8 @@ class UpdateMenu(BaseMenu):
"mainsail_config": {"installed": False, "local": None, "remote": None},
"fluidd": {"installed": False, "local": None, "remote": None},
"fluidd_config": {"installed": False, "local": None, "remote": None},
"mobileraker": {"installed": False, "local": None, "remote": None},
"klipperscreen": {"installed": False, "local": None, "remote": None},
"crowsnest": {"installed": False, "local": None, "remote": None},
"octoeverywhere": {"installed": False, "local": None, "remote": None},
}
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -110,10 +98,8 @@ class UpdateMenu(BaseMenu):
"5": Option(self.update_mainsail_config),
"6": Option(self.update_fluidd_config),
"7": Option(self.update_klipperscreen),
"8": Option(self.update_mobileraker),
"9": Option(self.update_crowsnest),
"10": Option(self.update_octoeverywhere),
"11": Option(self.upgrade_system_packages),
"8": Option(self.update_crowsnest),
"9": Option(self.upgrade_system_packages),
}
def print_menu(self) -> None:
@@ -157,11 +143,9 @@ class UpdateMenu(BaseMenu):
║ │ │ ║
║ Other: ├───────────────┼───────────────╢
║ 7) KlipperScreen │ {self.klipperscreen_local:<22}{self.klipperscreen_remote:<22}
║ 8) Mobileraker{self.mobileraker_local:<22}{self.mobileraker_remote:<22}
║ 9) Crowsnest │ {self.crowsnest_local:<22}{self.crowsnest_remote:<22}
║ 10) OctoEverywhere │ {self.octoeverywhere_local:<22}{self.octoeverywhere_remote:<22}
║ 8) Crowsnest {self.crowsnest_local:<22}{self.crowsnest_remote:<22}
║ ├───────────────┴───────────────╢
11) System │ {sysupgrades:^{padding}}
9) System │ {sysupgrades:^{padding}}
╟───────────────────────┴───────────────────────────────╢
"""
)[1:]
@@ -198,18 +182,10 @@ class UpdateMenu(BaseMenu):
if self._check_is_installed("klipperscreen"):
update_klipperscreen()
def update_mobileraker(self, **kwargs) -> None:
if self._check_is_installed("mobileraker"):
update_mobileraker()
def update_crowsnest(self, **kwargs) -> None:
if self._check_is_installed("crowsnest"):
update_crowsnest()
def update_octoeverywhere(self, **kwargs) -> None:
if self._check_is_installed("octoeverywhere"):
update_octoeverywhere()
def upgrade_system_packages(self, **kwargs) -> None:
self._run_system_updates()
@@ -225,9 +201,7 @@ class UpdateMenu(BaseMenu):
"fluidd_config", get_client_config_status, self.fluidd_data
)
self._set_status_data("klipperscreen", get_klipperscreen_status)
self._set_status_data("mobileraker", get_mobileraker_status)
self._set_status_data("crowsnest", get_crowsnest_status)
self._set_status_data("octoeverywhere", get_octoeverywhere_status)
update_system_package_lists(silent=True)
self.packages = get_upgradable_packages()

View File

@@ -1,6 +1,6 @@
{
"metadata": {
"index": 3,
"index": 4,
"module": "klipper_backup_extension",
"maintained_by": "Staubgeborener",
"display_name": "Klipper-Backup",

View File

@@ -13,7 +13,7 @@ import shutil
import textwrap
import urllib.request
from dataclasses import dataclass
from typing import Any, Dict, List, Type, Union
from typing import Any, Dict, List, Type
from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import (
@@ -21,14 +21,13 @@ from components.klipper.klipper_dialogs import (
print_instance_overview,
)
from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
from core.instance_manager.base_instance import BaseInstance
from core.instance_type import InstanceType
from core.logger import Logger
from core.menus import Option
from core.menus.base_menu import BaseMenu
from extensions.base_extension import BaseExtension
from utils.git_utils import git_clone_wrapper
from utils.input_utils import get_selection_input
from utils.instance_type import InstanceType
from utils.instance_utils import get_instances
@@ -60,8 +59,8 @@ class MainsailThemeInstallerExtension(BaseExtension):
return
for printer in printer_list:
Logger.print_status(f"Uninstalling theme from {printer.cfg_dir} ...")
theme_dir = printer.cfg_dir.joinpath(".theme")
Logger.print_status(f"Uninstalling theme from {printer.base.cfg_dir} ...")
theme_dir = printer.base.cfg_dir.joinpath(".theme")
if not theme_dir.exists():
Logger.print_info(f"{theme_dir} not found. Skipping ...")
continue
@@ -116,6 +115,7 @@ class MainsailThemeInstallMenu(BaseMenu):
j: str = f" {i}" if i < 10 else f"{i}"
row: str = f"{j}) [{theme.name}]"
menu += f"{row:<53}\n"
menu += "╟───────────────────────────────────────────────────────╢\n"
print(menu, end="")
def load_themes(self) -> List[ThemeData]:
@@ -158,7 +158,7 @@ class MainsailThemeInstallMenu(BaseMenu):
return
for printer in printer_list:
git_clone_wrapper(theme_repo_url, printer.cfg_dir.joinpath(".theme"))
git_clone_wrapper(theme_repo_url, printer.base.cfg_dir.joinpath(".theme"))
if len(theme_data.short_note) > 1:
Logger.print_warn("Info from the creator:", prefix=False, start="\n")
@@ -167,7 +167,7 @@ class MainsailThemeInstallMenu(BaseMenu):
def get_printer_selection(
instances: List[InstanceType], is_install: bool
) -> Union[List[BaseInstance], None]:
) -> List[InstanceType] | None:
options = [str(i) for i in range(len(instances))]
options.extend(["a", "b"])

View File

@@ -6,6 +6,7 @@
# #
# This file may be distributed under the terms of the GNU GPLv3 license #
# ======================================================================= #
from pathlib import Path
from core.backup_manager import BACKUP_ROOT_DIR

View File

@@ -0,0 +1,12 @@
{
"metadata": {
"index": 3,
"module": "mobileraker_extension",
"maintained_by": "Clon1998",
"display_name": "Mobileraker",
"description": [
"Companion for Mobileraker, enabling push notification for Klipper using Moonraker."
],
"updates": true
}
}

View File

@@ -0,0 +1,192 @@
# ======================================================================= #
# 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 shutil
from pathlib import Path
from subprocess import CalledProcessError, run
from typing import List
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from core.backup_manager.backup_manager import BackupManager
from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogType, Logger
from core.settings.kiauh_settings import KiauhSettings
from extensions.base_extension import BaseExtension
from extensions.mobileraker import (
MOBILERAKER_BACKUP_DIR,
MOBILERAKER_DIR,
MOBILERAKER_ENV_DIR,
MOBILERAKER_INSTALL_SCRIPT,
MOBILERAKER_LOG_NAME,
MOBILERAKER_REPO,
MOBILERAKER_REQ_FILE,
MOBILERAKER_SERVICE_FILE,
MOBILERAKER_SERVICE_NAME,
MOBILERAKER_UPDATER_SECTION_NAME,
)
from utils.common import check_install_dependencies
from utils.config_utils import add_config_section, remove_config_section
from utils.git_utils import git_clone_wrapper, git_pull_wrapper
from utils.input_utils import get_confirm
from utils.instance_utils import get_instances
from utils.sys_utils import (
check_python_version,
cmd_sysctl_service,
install_python_requirements,
remove_system_service,
)
# noinspection PyMethodMayBeStatic
class MobilerakerExtension(BaseExtension):
def install_extension(self, **kwargs) -> None:
Logger.print_status("Installing Mobileraker's companion ...")
if not check_python_version(3, 7):
return
mr_instances = get_instances(Moonraker)
if not mr_instances:
Logger.print_dialog(
DialogType.WARNING,
[
"Moonraker not found! Mobileraker's companion will not properly "
"work without a working Moonraker installation.",
"Mobileraker's companion's update manager configuration for "
"Moonraker will not be added to any moonraker.conf.",
],
)
if not get_confirm(
"Continue Mobileraker's companion installation?",
default_choice=False,
allow_go_back=True,
):
return
check_install_dependencies()
git_clone_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR)
try:
run(MOBILERAKER_INSTALL_SCRIPT.as_posix(), shell=True, check=True)
if mr_instances:
self._patch_mobileraker_update_manager(mr_instances)
InstanceManager.restart_all(mr_instances)
else:
Logger.print_info(
"Moonraker is not installed! Cannot add Mobileraker's "
"companion to update manager!"
)
Logger.print_ok("Mobileraker's companion successfully installed!")
except CalledProcessError as e:
Logger.print_error(f"Error installing Mobileraker's companion:\n{e}")
return
def update_extension(self, **kwargs) -> None:
try:
if not MOBILERAKER_DIR.exists():
Logger.print_info(
"Mobileraker's companion doesn't seem to be installed! Skipping ..."
)
return
Logger.print_status("Updating Mobileraker's companion ...")
cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "stop")
settings = KiauhSettings()
if settings.kiauh.backup_before_update:
self._backup_mobileraker_dir()
git_pull_wrapper(MOBILERAKER_REPO, MOBILERAKER_DIR)
install_python_requirements(MOBILERAKER_ENV_DIR, MOBILERAKER_REQ_FILE)
cmd_sysctl_service(MOBILERAKER_SERVICE_NAME, "start")
Logger.print_ok("Mobileraker's companion updated successfully.", end="\n\n")
except CalledProcessError as e:
Logger.print_error(f"Error updating Mobileraker's companion:\n{e}")
return
def remove_extension(self, **kwargs) -> None:
Logger.print_status("Removing Mobileraker's companion ...")
try:
if MOBILERAKER_DIR.exists():
Logger.print_status("Removing Mobileraker's companion directory ...")
shutil.rmtree(MOBILERAKER_DIR)
Logger.print_ok(
"Mobileraker's companion directory successfully removed!"
)
else:
Logger.print_warn("Mobileraker's companion directory not found!")
if MOBILERAKER_ENV_DIR.exists():
Logger.print_status("Removing Mobileraker's companion environment ...")
shutil.rmtree(MOBILERAKER_ENV_DIR)
Logger.print_ok(
"Mobileraker's companion environment successfully removed!"
)
else:
Logger.print_warn("Mobileraker's companion environment not found!")
if MOBILERAKER_SERVICE_FILE.exists():
remove_system_service(MOBILERAKER_SERVICE_NAME)
kl_instances: List[Klipper] = get_instances(Klipper)
for instance in kl_instances:
logfile = instance.base.log_dir.joinpath(MOBILERAKER_LOG_NAME)
if logfile.exists():
Logger.print_status(f"Removing {logfile} ...")
Path(logfile).unlink()
Logger.print_ok(f"{logfile} successfully removed!")
mr_instances: List[Moonraker] = get_instances(Moonraker)
if mr_instances:
Logger.print_status(
"Removing Mobileraker's companion from update manager ..."
)
remove_config_section(MOBILERAKER_UPDATER_SECTION_NAME, mr_instances)
Logger.print_ok(
"Mobileraker's companion successfully removed from update manager!"
)
Logger.print_ok("Mobileraker's companion successfully removed!")
except Exception as e:
Logger.print_error(f"Error removing Mobileraker's companion:\n{e}")
def _patch_mobileraker_update_manager(self, instances: List[Moonraker]) -> None:
add_config_section(
section=MOBILERAKER_UPDATER_SECTION_NAME,
instances=instances,
options=[
("type", "git_repo"),
("path", MOBILERAKER_DIR.as_posix()),
("origin", MOBILERAKER_REPO),
("primary_branch", "main"),
("managed_services", "mobileraker"),
("env", f"{MOBILERAKER_ENV_DIR}/bin/python"),
("requirements", MOBILERAKER_REQ_FILE.as_posix()),
("install_script", MOBILERAKER_INSTALL_SCRIPT.as_posix()),
],
)
def _backup_mobileraker_dir(self) -> None:
bm = BackupManager()
bm.backup_directory(
MOBILERAKER_DIR.name,
source=MOBILERAKER_DIR,
target=MOBILERAKER_BACKUP_DIR,
)
bm.backup_directory(
MOBILERAKER_ENV_DIR.name,
source=MOBILERAKER_ENV_DIR,
target=MOBILERAKER_BACKUP_DIR,
)

View File

@@ -0,0 +1,16 @@
{
"metadata": {
"index": 7,
"module": "octoeverywhere_extension",
"maintained_by": "QuinnDamerell",
"display_name": "OctoEverywhere for Klipper",
"description": [
"Cloud Empower Your Klipper 3D Printers With:",
"- Free, Private, And Secure Remote Access",
"- AI Print Failure Detection",
"- Real-time Notifications",
"- Live Streaming, and More!"
],
"updates": true
}
}

View File

@@ -14,7 +14,9 @@ from subprocess import CalledProcessError, run
from components.moonraker import MOONRAKER_CFG_NAME
from components.moonraker.moonraker import Moonraker
from components.octoeverywhere import (
from core.instance_manager.base_instance import BaseInstance
from core.logger import Logger
from extensions.octoeverywhere import (
OE_CFG_NAME,
OE_DIR,
OE_ENV_DIR,
@@ -23,8 +25,6 @@ from components.octoeverywhere import (
OE_SYS_CFG_NAME,
OE_UPDATE_SCRIPT,
)
from core.instance_manager.base_instance import BaseInstance
from core.logger import Logger
from utils.sys_utils import get_service_file_path

View File

@@ -0,0 +1,191 @@
# ======================================================================= #
# 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 typing import List
from components.moonraker.moonraker import Moonraker
from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogType, Logger
from extensions.base_extension import BaseExtension
from extensions.octoeverywhere import (
OE_DEPS_JSON_FILE,
OE_DIR,
OE_ENV_DIR,
OE_INSTALL_SCRIPT,
OE_INSTALLER_LOG_FILE,
OE_REPO,
OE_REQ_FILE,
OE_SYS_CFG_NAME,
)
from extensions.octoeverywhere.octoeverywhere import Octoeverywhere
from utils.common import (
check_install_dependencies,
moonraker_exists,
)
from utils.config_utils import (
remove_config_section,
)
from utils.fs_utils import run_remove_routines
from utils.git_utils import git_clone_wrapper
from utils.input_utils import get_confirm
from utils.instance_utils import get_instances
from utils.sys_utils import (
install_python_requirements,
parse_packages_from_file,
)
# noinspection PyMethodMayBeStatic
class OctoeverywhereExtension(BaseExtension):
def install_extension(self, **kwargs) -> None:
Logger.print_status("Installing OctoEverywhere for Klipper ...")
# check if moonraker is installed. if not, notify the user and exit
if not moonraker_exists():
return
force_clone = False
oe_instances: List[Octoeverywhere] = get_instances(Octoeverywhere)
if oe_instances:
Logger.print_dialog(
DialogType.INFO,
[
"OctoEverywhere is already installed!",
"It is safe to run the installer again to link your "
"printer or repair any issues.",
],
)
if not get_confirm("Re-run OctoEverywhere installation?"):
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
return
else:
Logger.print_status("Re-Installing OctoEverywhere for Klipper ...")
force_clone = True
mr_instances: List[Moonraker] = get_instances(Moonraker)
mr_names = [f"{moonraker.data_dir.name}" for moonraker in mr_instances]
if len(mr_names) > 1:
Logger.print_dialog(
DialogType.INFO,
[
"The following Moonraker instances were found:",
*mr_names,
"\n\n",
"The setup will apply the same names to OctoEverywhere!",
],
)
if not get_confirm(
"Continue OctoEverywhere for Klipper installation?",
default_choice=True,
allow_go_back=True,
):
Logger.print_info("Exiting OctoEverywhere for Klipper installation ...")
return
try:
git_clone_wrapper(OE_REPO, OE_DIR, force=force_clone)
for moonraker in mr_instances:
instance = Octoeverywhere(suffix=moonraker.suffix)
instance.create()
InstanceManager.restart_all(mr_instances)
Logger.print_dialog(
DialogType.SUCCESS,
["OctoEverywhere for Klipper successfully installed!"],
center_content=True,
)
except Exception as e:
Logger.print_error(
f"Error during OctoEverywhere for Klipper installation:\n{e}"
)
def update_extension(self, **kwargs) -> None:
Logger.print_status("Updating OctoEverywhere for Klipper ...")
try:
Octoeverywhere.update()
Logger.print_dialog(
DialogType.SUCCESS,
["OctoEverywhere for Klipper successfully updated!"],
center_content=True,
)
except Exception as e:
Logger.print_error(f"Error during OctoEverywhere for Klipper update:\n{e}")
def remove_extension(self, **kwargs) -> None:
Logger.print_status("Removing OctoEverywhere for Klipper ...")
mr_instances: List[Moonraker] = get_instances(Moonraker)
ob_instances: List[Octoeverywhere] = get_instances(Octoeverywhere)
try:
self._remove_oe_instances(ob_instances)
self._remove_oe_dir()
self._remove_oe_env()
remove_config_section(f"include {OE_SYS_CFG_NAME}", mr_instances)
run_remove_routines(OE_INSTALLER_LOG_FILE)
Logger.print_dialog(
DialogType.SUCCESS,
["OctoEverywhere for Klipper successfully removed!"],
center_content=True,
)
except Exception as e:
Logger.print_error(f"Error during OctoEverywhere for Klipper removal:\n{e}")
def _install_oe_dependencies(self) -> None:
oe_deps = []
if OE_DEPS_JSON_FILE.exists():
with open(OE_DEPS_JSON_FILE, "r") as deps:
oe_deps = json.load(deps).get("debian", [])
elif OE_INSTALL_SCRIPT.exists():
oe_deps = parse_packages_from_file(OE_INSTALL_SCRIPT)
if not oe_deps:
raise ValueError("Error reading OctoEverywhere dependencies!")
check_install_dependencies({*oe_deps})
install_python_requirements(OE_ENV_DIR, OE_REQ_FILE)
def _remove_oe_instances(
self,
instance_list: List[Octoeverywhere],
) -> None:
if not instance_list:
Logger.print_info("No OctoEverywhere instances found. Skipped ...")
return
for instance in instance_list:
Logger.print_status(
f"Removing instance {instance.service_file_path.stem} ..."
)
InstanceManager.remove(instance)
def _remove_oe_dir(self) -> None:
Logger.print_status("Removing OctoEverywhere for Klipper directory ...")
if not OE_DIR.exists():
Logger.print_info(f"'{OE_DIR}' does not exist. Skipped ...")
return
run_remove_routines(OE_DIR)
def _remove_oe_env(self) -> None:
Logger.print_status("Removing OctoEverywhere for Klipper environment ...")
if not OE_ENV_DIR.exists():
Logger.print_info(f"'{OE_ENV_DIR}' does not exist. Skipped ...")
return
run_remove_routines(OE_ENV_DIR)

View File

@@ -1,6 +1,6 @@
{
"metadata": {
"index": 5,
"index": 8,
"module": "pretty_gcode_extension",
"maintained_by": "Kragrathea",
"display_name": "PrettyGCode for Klipper",

View File

@@ -1,6 +1,6 @@
{
"metadata": {
"index": 4,
"index": 5,
"module": "moonraker_telegram_bot_extension",
"maintained_by": "nlef",
"display_name": "Moonraker Telegram Bot",

View File

@@ -12,11 +12,11 @@ import tempfile
from pathlib import Path
from typing import List, Tuple
from core.instance_type import InstanceType
from core.logger import Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
SimpleConfigParser,
)
from utils.instance_type import InstanceType
ConfigOption = Tuple[str, str]

View File

@@ -10,9 +10,9 @@ from subprocess import DEVNULL, PIPE, CalledProcessError, check_output, run
from typing import List, Type
from core.instance_manager.instance_manager import InstanceManager
from core.instance_type import InstanceType
from core.logger import Logger
from utils.input_utils import get_confirm, get_number_input
from utils.instance_type import InstanceType
from utils.instance_utils import get_instances

View File

@@ -11,8 +11,8 @@ from typing import TypeVar
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from components.octoeverywhere.octoeverywhere import Octoeverywhere
from extensions.obico.moonraker_obico import MoonrakerObico
from extensions.octoeverywhere.octoeverywhere import Octoeverywhere
from extensions.telegram_bot.moonraker_telegram_bot import MoonrakerTelegramBot
InstanceType = TypeVar(

View File

@@ -14,7 +14,7 @@ from typing import List
from core.constants import SYSTEMD
from core.instance_manager.base_instance import SUFFIX_BLACKLIST
from core.instance_type import InstanceType
from utils.instance_type import InstanceType
def get_instances(instance_type: type) -> List[InstanceType]: