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>
This commit is contained in:
dw-0
2024-09-20 12:05:29 +02:00
committed by GitHub
parent 29b5ab00cd
commit 7e87f8af32
23 changed files with 445 additions and 486 deletions

View File

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

View File

@@ -21,13 +21,13 @@ from components.klipper.klipper_dialogs import (
print_instance_overview,
)
from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
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

View File

@@ -0,0 +1,31 @@
# ======================================================================= #
# 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 core.backup_manager import BACKUP_ROOT_DIR
from core.constants import SYSTEMD
# repo
MOBILERAKER_REPO = "https://github.com/Clon1998/mobileraker_companion.git"
# names
MOBILERAKER_SERVICE_NAME = "mobileraker.service"
MOBILERAKER_UPDATER_SECTION_NAME = "update_manager mobileraker"
MOBILERAKER_LOG_NAME = "mobileraker.log"
# directories
MOBILERAKER_DIR = Path.home().joinpath("mobileraker_companion")
MOBILERAKER_ENV_DIR = Path.home().joinpath("mobileraker-env")
MOBILERAKER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("mobileraker-backups")
# files
MOBILERAKER_INSTALL_SCRIPT = MOBILERAKER_DIR.joinpath("scripts/install.sh")
MOBILERAKER_REQ_FILE = MOBILERAKER_DIR.joinpath("scripts/mobileraker-requirements.txt")
MOBILERAKER_SERVICE_FILE = SYSTEMD.joinpath(MOBILERAKER_SERVICE_NAME)

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,29 @@
# ======================================================================= #
# 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")
OE_INSTALLER_LOG_FILE = Path.home().joinpath("octoeverywhere-installer.log")
# filenames
OE_CFG_NAME = "octoeverywhere.conf"
OE_LOG_NAME = "octoeverywhere.log"
OE_SYS_CFG_NAME = "octoeverywhere-system.cfg"

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

@@ -0,0 +1,75 @@
# ======================================================================= #
# 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 __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from subprocess import CalledProcessError, run
from components.moonraker import MOONRAKER_CFG_NAME
from components.moonraker.moonraker import Moonraker
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,
OE_INSTALL_SCRIPT,
OE_LOG_NAME,
OE_SYS_CFG_NAME,
OE_UPDATE_SCRIPT,
)
from utils.sys_utils import get_service_file_path
@dataclass
class Octoeverywhere:
suffix: str
base: BaseInstance = field(init=False, repr=False)
service_file_path: Path = field(init=False)
log_file_name = OE_LOG_NAME
dir: Path = OE_DIR
env_dir: Path = OE_ENV_DIR
data_dir: Path = field(init=False)
store_dir: Path = field(init=False)
cfg_file: Path = field(init=False)
sys_cfg_file: Path = field(init=False)
def __post_init__(self):
self.base: BaseInstance = BaseInstance(Moonraker, self.suffix)
self.base.log_file_name = self.log_file_name
self.service_file_path: Path = get_service_file_path(
Octoeverywhere, self.suffix
)
self.store_dir = self.base.data_dir.joinpath("store")
self.cfg_file = self.base.cfg_dir.joinpath(OE_CFG_NAME)
self.sys_cfg_file = self.base.cfg_dir.joinpath(OE_SYS_CFG_NAME)
self.data_dir = self.base.data_dir
self.sys_cfg_file = self.base.cfg_dir.joinpath(OE_SYS_CFG_NAME)
def create(self) -> None:
Logger.print_status("Creating OctoEverywhere for Klipper Instance ...")
try:
cmd = f"{OE_INSTALL_SCRIPT} {self.base.cfg_dir}/{MOONRAKER_CFG_NAME}"
run(cmd, check=True, shell=True)
except CalledProcessError as e:
Logger.print_error(f"Error creating instance: {e}")
raise
@staticmethod
def update() -> None:
try:
run(OE_UPDATE_SCRIPT.as_posix(), check=True, shell=True, cwd=OE_DIR)
except CalledProcessError as e:
Logger.print_error(f"Error updating OctoEverywhere for Klipper: {e}")
raise

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",