Compare commits

..

3 Commits

Author SHA1 Message Date
Tovi
902e441ab7 Merge c47d6e221b into 8170057434 2025-08-03 21:10:35 +09:00
Tovi
c47d6e221b translated raspberry pi to chinese 2025-07-19 19:29:44 +08:00
Tovi
be06f3ce00 add chinese readme 2025-07-19 19:01:06 +08:00
22 changed files with 41 additions and 605 deletions

View File

@@ -16,9 +16,8 @@ from typing import List
from utils.fs_utils import get_data_dir from utils.fs_utils import get_data_dir
# suffixes that are not allowed to be used for instances SUFFIX_BLACKLIST: List[str] = ["None", "mcu", "obico", "bambu", "companion"]
# because they would cause conflicts with other components or are reserved
SUFFIX_BLACKLIST: List[str] = ["None", "mcu", "obico", "bambu", "companion", "hmi"]
@dataclass(repr=True) @dataclass(repr=True)
class BaseInstance: class BaseInstance:

View File

@@ -10,17 +10,14 @@ from __future__ import annotations
from typing import List, Literal, Type from typing import List, Literal, Type
from core.logger import Logger, DialogType from core.logger import Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.settings.kiauh_settings import KiauhSettings, Repository from core.settings.kiauh_settings import KiauhSettings, Repository
from core.types.color import Color from core.types.color import Color
from procedures.switch_repo import run_switch_repo_routine from procedures.switch_repo import run_switch_repo_routine
from utils.input_utils import get_string_input, get_number_input, get_confirm
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class RepoSelectMenu(BaseMenu): class RepoSelectMenu(BaseMenu):
def __init__( def __init__(
self, self,
@@ -51,27 +48,26 @@ class RepoSelectMenu(BaseMenu):
def set_options(self) -> None: def set_options(self) -> None:
self.options = {} self.options = {}
if self.repos:
for idx, repo in enumerate(self.repos, start=1): if not self.repos:
self.options[str(idx)] = Option( return
method=self.select_repository, opt_data=repo
) for idx, repo in enumerate(self.repos, start=1):
self.options["a"] = Option(method=self.add_repository) self.options[str(idx)] = Option(
self.options["r"] = Option(method=self.remove_repository) method=self.select_repository, opt_data=repo
self.options["b"] = Option(method=self.go_back) )
def print_menu(self) -> None: def print_menu(self) -> None:
menu = "╟───────────────────────────────────────────────────────╢\n" menu = "╟───────────────────────────────────────────────────────╢\n"
menu += "║ Available Repositories: ║\n" menu += "║ Available Repositories: ║\n"
menu += "╟───────────────────────────────────────────────────────╢\n" menu += "╟───────────────────────────────────────────────────────╢\n"
for idx, repo in enumerate(self.repos, start=1): for idx, repo in enumerate(self.repos, start=1):
url = f"● Repo: {repo.url.replace('.git', '')}" url = f"● Repo: {repo.url.replace('.git', '')}"
branch = f"└► Branch: {repo.branch}" branch = f"└► Branch: {repo.branch}"
menu += f"{idx}) {Color.apply(url, Color.CYAN):<59}\n" menu += f"{idx}) {Color.apply(url, Color.CYAN):<59}\n"
menu += f"{Color.apply(branch, Color.CYAN):<59}\n" menu += f"{Color.apply(branch, Color.CYAN):<59}\n"
menu += "╟───────────────────────────────────────────────────────╢\n"
menu += "║ A) Add repository ║\n"
menu += "║ R) Remove repository ║\n"
menu += "╟───────────────────────────────────────────────────────╢\n" menu += "╟───────────────────────────────────────────────────────╢\n"
print(menu, end="") print(menu, end="")
@@ -81,82 +77,3 @@ class RepoSelectMenu(BaseMenu):
f"Switching to {self.name.capitalize()}'s new source repository ..." f"Switching to {self.name.capitalize()}'s new source repository ..."
) )
run_switch_repo_routine(self.name, repo.url, repo.branch) run_switch_repo_routine(self.name, repo.url, repo.branch)
def add_repository(self, **kwargs) -> None:
while True:
Logger.print_dialog(
DialogType.CUSTOM,
custom_title="Enter the repository URL",
content=[
"NOTE: There is no input validation in place, "
"please check your input for correctness",
],
)
url = get_string_input("Repository URL", allow_special_chars=True).strip()
Logger.print_dialog(
DialogType.CUSTOM,
custom_title="Enter the branch name",
content=[ "Press Enter to use the default branch (master)." ],
center_content=False,
)
branch = get_string_input("Branch", allow_special_chars=True, default="master").strip()
Logger.print_dialog(
DialogType.CUSTOM,
custom_title="Summary",
content=[
f"● URL: {url}",
f"● Branch: {branch}",
],
)
confirm = get_confirm("Save repository")
if confirm:
repo = Repository(url, branch)
if self.name == "klipper":
self.settings.klipper.repositories.append(repo)
self.settings.save()
self.repos = self.settings.klipper.repositories
else:
self.settings.moonraker.repositories.append(repo)
self.settings.save()
self.repos = self.settings.moonraker.repositories
Logger.print_ok("Repository added and saved.")
# Refresh menu to show new repo immediately and update options
self.set_options()
self.run()
break
else:
Logger.print_info("Operation cancelled by user.")
break
def remove_repository(self, **kwargs) -> None:
repos = self.repos
if not repos:
Logger.print_info("No repositories configured.")
return
repo_lines = [f"{idx}) {repo.url} [{repo.branch}]" for idx, repo in enumerate(repos, start=1)]
Logger.print_dialog(
DialogType.CUSTOM,
custom_title="Available Repositories",
content=[*repo_lines],
)
idx = get_number_input("Select the repository to remove", 1, len(repos))
removed = repos.pop(idx - 1)
if self.name == "klipper":
self.settings.klipper.repositories = repos
self.settings.save()
self.repos = self.settings.klipper.repositories
else:
self.settings.moonraker.repositories = repos
self.settings.save()
self.repos = self.settings.moonraker.repositories
Logger.print_ok(f"Removed repository: {removed.url} [{removed.branch}]")
# Refresh menu to show updated repo list and options
self.set_options()
self.run()
def go_back(self, **kwargs) -> None:
from core.menus.settings_menu import SettingsMenu
SettingsMenu().run()

View File

@@ -117,12 +117,20 @@ class SettingsMenu(BaseMenu):
) )
def switch_klipper_repo(self, **kwargs) -> None: def switch_klipper_repo(self, **kwargs) -> None:
name = "Klipper"
repos = self.settings.klipper.repositories repos = self.settings.klipper.repositories
RepoSelectMenu("klipper", repos=repos, previous_menu=self.__class__).run() if not repos:
self._warn_no_repos(name)
return
RepoSelectMenu(name.lower(), repos=repos, previous_menu=self.__class__).run()
def switch_moonraker_repo(self, **kwargs) -> None: def switch_moonraker_repo(self, **kwargs) -> None:
name = "Moonraker"
repos = self.settings.moonraker.repositories repos = self.settings.moonraker.repositories
RepoSelectMenu("moonraker", repos=repos, previous_menu=self.__class__).run() if not repos:
self._warn_no_repos(name)
return
RepoSelectMenu(name.lower(), repos=repos, previous_menu=self.__class__).run()
def toggle_mainsail_release(self, **kwargs) -> None: def toggle_mainsail_release(self, **kwargs) -> None:
self.mainsail_unstable = not self.mainsail_unstable self.mainsail_unstable = not self.mainsail_unstable

View File

@@ -143,31 +143,6 @@ class ExtensionSubmenu(BaseMenu):
""" """
)[1:] )[1:]
menu += f"{description_text}\n" menu += f"{description_text}\n"
# add links if available
website: str = (self.extension.metadata.get("website") or "").strip()
repo: str = (self.extension.metadata.get("repo") or "").strip()
if website or repo:
links_lines: List[str] = ["Links:"]
if website:
links_lines.append(f"{website}")
if repo:
links_lines.append(f"{repo}")
links_text = Logger.format_content(
links_lines,
line_width,
border_left="",
border_right="",
)
menu += textwrap.dedent(
"""
╟───────────────────────────────────────────────────────╢
"""
)[1:]
menu += f"{links_text}\n"
menu += textwrap.dedent( menu += textwrap.dedent(
""" """
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢

View File

@@ -4,7 +4,6 @@
"module": "gcode_shell_cmd_extension", "module": "gcode_shell_cmd_extension",
"maintained_by": "dw-0", "maintained_by": "dw-0",
"display_name": "G-Code Shell Command", "display_name": "G-Code Shell Command",
"description": ["Run a shell commands from gcode."], "description": ["Run a shell commands from gcode."]
"updates": false
} }
} }

View File

@@ -5,8 +5,6 @@
"maintained_by": "Staubgeborener", "maintained_by": "Staubgeborener",
"display_name": "Klipper-Backup", "display_name": "Klipper-Backup",
"description": ["Backup all your Klipper files to GitHub"], "description": ["Backup all your Klipper files to GitHub"],
"website": "https://klipperbackup.xyz",
"repo": "https://github.com/Staubgeborener/klipper-backup",
"updates": true "updates": true
} }
} }

View File

@@ -4,8 +4,6 @@
"module": "mainsail_theme_installer_extension", "module": "mainsail_theme_installer_extension",
"maintained_by": "dw-0", "maintained_by": "dw-0",
"display_name": "Mainsail Theme Installer", "display_name": "Mainsail Theme Installer",
"description": ["Install Mainsail Themes maintained by the Mainsail community."], "description": ["Install Mainsail Themes maintained by the Mainsail community."]
"website": "https://docs.mainsail.xyz/theming/themes",
"updates": false
} }
} }

View File

@@ -7,7 +7,6 @@
"description": [ "description": [
"Companion for Mobileraker, enabling push notification for Klipper using Moonraker." "Companion for Mobileraker, enabling push notification for Klipper using Moonraker."
], ],
"repo": "https://github.com/Clon1998/mobileraker_companion",
"updates": true "updates": true
} }
} }

View File

@@ -11,8 +11,6 @@
"- 25FPS High-Def Webcam Streaming", "- 25FPS High-Def Webcam Streaming",
"- Free 4.9-Star Mobile App" "- Free 4.9-Star Mobile App"
], ],
"website": "https://obico.io",
"repo": "github.com/TheSpaghettiDetective/moonraker-obico",
"updates": true "updates": true
} }
} }

View File

@@ -12,7 +12,6 @@
"- Live Gcode preview", "- Live Gcode preview",
"- And much much more!" "- And much much more!"
], ],
"repo": "https://github.com/crysxd/OctoApp-Plugin",
"updates": true "updates": true
} }
} }

View File

@@ -11,8 +11,6 @@
"- Real-time Notifications", "- Real-time Notifications",
"- Live Streaming, and More!" "- Live Streaming, and More!"
], ],
"website": "https://octoeverywhere.com",
"repo": "github.com/QuinnDamerell/OctoPrint-OctoEverywhere",
"updates": true "updates": true
} }
} }

View File

@@ -1,22 +0,0 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 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
# Constants
OP_DEFAULT_PORT = 5000
# OctoPrint instance naming/prefixes
OP_ENV_PREFIX = "OctoPrint"
OP_BASEDIR_PREFIX = ".octoprint"
# Service/log filenames
OP_LOG_NAME = "octoprint.log"
# Files/paths (computed per-instance where applicable)
OP_SUDOERS_FILE = Path("/etc/sudoers.d/octoprint-shutdown")

View File

@@ -1,18 +0,0 @@
{
"metadata": {
"index": 12,
"module": "octoprint_extension",
"maintained_by": "dw-0",
"display_name": "OctoPrint",
"description": [
"Open-source web interface to control and monitor your 3D printer",
"- Upload and manage G-code, start/pause/cancel prints",
"- Live webcam view and timelapse support",
"- Real-time temperature graphs and printer status",
"- Powerful plugin ecosystem"
],
"website": "https://octoprint.org",
"repo": "https://github.com/OctoPrint/OctoPrint",
"updates": false
}
}

View File

@@ -1,116 +0,0 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 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 textwrap import dedent
from components.klipper.klipper import Klipper
from core.constants import CURRENT_USER
from core.instance_manager.base_instance import BaseInstance
from core.logger import Logger
from extensions.octoprint import (
OP_BASEDIR_PREFIX,
OP_ENV_PREFIX,
OP_LOG_NAME,
)
from utils.fs_utils import create_folders
from utils.sys_utils import create_service_file, get_service_file_path
@dataclass
class Octoprint:
suffix: str
base: BaseInstance = field(init=False, repr=False)
service_file_path: Path = field(init=False)
log_file_name = OP_LOG_NAME
env_dir: Path = field(init=False)
basedir: Path = field(init=False)
cfg_file: Path = field(init=False)
def __post_init__(self):
self.base = BaseInstance(Klipper, self.suffix)
self.base.log_file_name = self.log_file_name
self.service_file_path = get_service_file_path(Octoprint, self.suffix)
# OctoPrint stores its data under ~/.octoprint[_SUFFIX]
self.basedir = (
Path.home().joinpath(OP_BASEDIR_PREFIX)
if self.suffix == ""
else Path.home().joinpath(f"{OP_BASEDIR_PREFIX}_{self.suffix}")
)
self.cfg_file = self.basedir.joinpath("config.yaml")
# OctoPrint virtualenv lives under ~/OctoPrint[_SUFFIX]
self.env_dir = (
Path.home().joinpath(OP_ENV_PREFIX)
if self.suffix == ""
else Path.home().joinpath(f"{OP_ENV_PREFIX}_{self.suffix}")
)
def create(self, port: int) -> None:
Logger.print_status(
f"Creating OctoPrint instance '{self.service_file_path.stem}' ..."
)
# Ensure basedir exists and config.yaml is present
create_folders([self.basedir])
if not self.cfg_file.exists():
Logger.print_status("Creating config.yaml ...")
self.cfg_file.write_text(self._prep_config_yaml())
Logger.print_ok("config.yaml created!")
else:
Logger.print_info("config.yaml already exists. Skipped ...")
create_service_file(self.service_file_path.name, self._prep_service_content(port))
def _prep_service_content(self, port: int) -> str:
basedir = self.basedir.as_posix()
cfg = self.cfg_file.as_posix()
octo_exec = self.env_dir.joinpath("bin/octoprint").as_posix()
return dedent(
f"""\
[Unit]
Description=Starts OctoPrint on startup
After=network-online.target
Wants=network-online.target
[Service]
Environment="LC_ALL=C.UTF-8"
Environment="LANG=C.UTF-8"
Type=simple
User={CURRENT_USER}
ExecStart={octo_exec} --basedir {basedir} --config {cfg} --port={port} serve
[Install]
WantedBy=multi-user.target
"""
)
def _prep_config_yaml(self) -> str:
printer = self.base.comms_dir.joinpath("klippy.serial").as_posix()
restart_service = self.service_file_path.stem
return dedent(
f"""\
serial:
additionalPorts:
- {printer}
disconnectOnErrors: false
port: {printer}
server:
commands:
serverRestartCommand: sudo service {restart_service} restart
systemRestartCommand: sudo shutdown -r now
systemShutdownCommand: sudo shutdown -h now
"""
)

View File

@@ -1,286 +0,0 @@
# ======================================================================= #
# Copyright (C) 2020 - 2025 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
import re
from typing import Dict, List, Optional, Set
from components.klipper.klipper import Klipper
from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogType, Logger
from core.types.color import Color
from core.menus.base_menu import print_back_footer
from extensions.base_extension import BaseExtension
from extensions.octoprint import (
OP_SUDOERS_FILE, OP_DEFAULT_PORT,
)
from extensions.octoprint.octoprint import Octoprint
from utils.common import check_install_dependencies
from utils.fs_utils import run_remove_routines, remove_with_sudo
from utils.input_utils import get_selection_input, get_confirm
from utils.instance_utils import get_instances
from utils.sys_utils import (
create_python_venv,
get_ipv4_addr,
install_python_packages,
)
# noinspection PyMethodMayBeStatic
class OctoprintExtension(BaseExtension):
def install_extension(self, **kwargs) -> None:
Logger.print_status("Installing OctoPrint ...")
klipper_instances: List[Klipper] = get_instances(Klipper)
if not klipper_instances:
Logger.print_dialog(
DialogType.WARNING,
[
"Klipper not found! Please install Klipper first.",
],
)
return
existing_ops: List[Octoprint] = get_instances(Octoprint)
existing_by_suffix: Dict[str, Octoprint] = {op.suffix: op for op in existing_ops}
candidates: List[Klipper] = [k for k in klipper_instances if k.suffix not in existing_by_suffix]
chosen: List[Klipper] = []
if len(klipper_instances) == 1:
k = klipper_instances[0]
if k.suffix in existing_by_suffix:
if not get_confirm(
f"OctoPrint already exists for '{k.service_file_path.stem}'. Reinstall?",
default_choice=True,
allow_go_back=True,
):
Logger.print_info("Aborted OctoPrint installation.")
return
chosen = [k]
else:
while True:
dialog = "╔═══════════════════════════════════════════════════════╗\n"
headline = Color.apply(
"The following Klipper instances were found:", Color.GREEN
)
dialog += f"{headline:^64}\n"
dialog += "╟───────────────────────────────────────────────────────╢\n"
if candidates:
line_all = Color.apply("a) Select all (install for all missing)", Color.YELLOW)
dialog += f"{line_all:<63}\n"
dialog += "║ ║\n"
index_map: Dict[str, Klipper] = {}
for i, k in enumerate(klipper_instances, start=1):
mapping = existing_by_suffix.get(k.suffix)
suffix = f" <-> {mapping.service_file_path.stem}" if mapping else ""
line = Color.apply(f"{i}) {k.service_file_path.stem}{suffix}", Color.CYAN)
dialog += f"{line:<63}\n"
index_map[str(i)] = k
dialog += "╟───────────────────────────────────────────────────────╢\n"
print(dialog, end="")
print_back_footer()
allowed = list(index_map.keys()) + ["b"] + (["a"] if candidates else [])
choice = get_selection_input("Choose instance to install OctoPrint for", allowed)
if choice == "b":
Logger.print_info("Aborted OctoPrint installation.")
return
if choice == "a":
chosen = candidates
break
selected = index_map[choice]
if selected.suffix in existing_by_suffix:
confirm = get_confirm(
f"OctoPrint already exists for '{selected.service_file_path.stem}'. Reinstall?",
default_choice=True,
allow_go_back=True,
)
if not confirm:
# back to menu
continue
chosen = [selected]
break
deps = {
"git",
"wget",
"python3-pip",
"python3-dev",
"libyaml-dev",
"build-essential",
"python3-setuptools",
"python3-virtualenv",
}
check_install_dependencies(deps)
# Determine used ports from existing OctoPrint services and prepare regex
used_ports: Set[int] = set()
port_re = re.compile(r"--port=(\d+)")
for op in existing_ops:
try:
content = op.service_file_path.read_text()
m = port_re.search(content)
if m:
used_ports.add(int(m.group(1)))
except OSError:
pass
# noinspection PyShadowingNames
def read_existing_port(suffix: str) -> Optional[int]:
op = existing_by_suffix.get(suffix)
if not op:
return None
try:
content = op.service_file_path.read_text()
m = port_re.search(content)
return int(m.group(1)) if m else None
except OSError:
return None
def next_free_port(start: int, used: Set[int]) -> int:
p = start
while p in used:
p += 1
used.add(p)
return p
created_ops: List[Octoprint] = []
for k in chosen:
# Keep existing port on reinstall, otherwise assign next free one
existing_port = read_existing_port(k.suffix)
port = existing_port if existing_port is not None else next_free_port(OP_DEFAULT_PORT, used_ports)
instance = Octoprint(suffix=k.suffix)
if create_python_venv(instance.env_dir, force=False):
Logger.print_ok(
f"Virtualenv created: {instance.env_dir}", prefix=False
)
else:
Logger.print_info(
f"Virtualenv exists: {instance.env_dir}. Skipping creation ..."
)
install_python_packages(instance.env_dir, ["octoprint"])
instance.create(port=port)
created_ops.append(instance)
for inst in created_ops:
try:
InstanceManager.enable(inst)
InstanceManager.start(inst)
except Exception as e:
Logger.print_error(
f"Failed to enable/start {inst.service_file_path.name}: {e}"
)
ip = get_ipv4_addr()
lines = ["Access your new OctoPrint instance(s) at:"]
for inst in created_ops:
try:
content = inst.service_file_path.read_text()
m = port_re.search(content)
if m:
# noinspection HttpUrlsUsage
lines.append(f"{inst.service_file_path.stem}: http://{ip}:{m.group(1)}")
except OSError:
pass
Logger.print_dialog(DialogType.SUCCESS, lines, center_content=False)
def remove_extension(self, **kwargs) -> None:
Logger.print_status("Removing OctoPrint ...")
try:
op_instances: List[Octoprint] = get_instances(Octoprint)
if not op_instances:
Logger.print_info("No OctoPrint instances found. Skipped ...")
return
remove_all = False
if len(op_instances) == 1:
to_remove = op_instances
else:
dialog = "╔═══════════════════════════════════════════════════════╗\n"
headline = Color.apply(
"The following OctoPrint instances were found:", Color.GREEN
)
dialog += f"{headline:^64}\n"
dialog += "╟───────────────────────────────────────────────────────╢\n"
select_all = Color.apply("a) Select all", Color.YELLOW)
dialog += f"{select_all:<63}\n"
dialog += "║ ║\n"
for i, inst in enumerate(op_instances, start=1):
line = Color.apply(
f"{i}) {inst.service_file_path.stem}", Color.CYAN
)
dialog += f"{line:<63}\n"
dialog += "╟───────────────────────────────────────────────────────╢\n"
print(dialog, end="")
print_back_footer()
allowed = [str(i) for i in range(1, len(op_instances) + 1)]
allowed.extend(["a", "b"])
choice = get_selection_input("Choose instance to remove", allowed)
if choice == "a":
remove_all = True
to_remove = op_instances
elif choice == "b":
Logger.print_info("Aborted OctoPrint removal.")
return
else:
idx = int(choice) - 1
to_remove = [op_instances[idx]]
for inst in to_remove:
Logger.print_status(
f"Removing instance {inst.service_file_path.stem} ..."
)
try:
InstanceManager.remove(inst)
except Exception as e:
Logger.print_error(
f"Failed to remove service {inst.service_file_path.name}: {e}"
)
# Remove only this instance's env and basedir
if inst.env_dir.exists():
Logger.print_status(f"Removing {inst.env_dir} ...")
run_remove_routines(inst.env_dir)
if inst.basedir.exists():
Logger.print_status(f"Removing {inst.basedir} ...")
run_remove_routines(inst.basedir)
# Remove sudoers file only if no instances remain
remaining = get_instances(Octoprint)
if not remaining and OP_SUDOERS_FILE.exists():
Logger.print_status(f"Removing {OP_SUDOERS_FILE} ...")
remove_with_sudo(OP_SUDOERS_FILE)
Logger.print_dialog(
DialogType.SUCCESS,
[
"Selected OctoPrint instance(s) successfully removed!"
if not remove_all
else "All OctoPrint instances successfully removed!",
],
center_content=True,
)
except Exception as e:
Logger.print_error(f"Error during OctoPrint removal: {e}")

View File

@@ -5,7 +5,6 @@
"maintained_by": "Kragrathea", "maintained_by": "Kragrathea",
"display_name": "PrettyGCode for Klipper", "display_name": "PrettyGCode for Klipper",
"description": ["3D G-Code viewer for Klipper"], "description": ["3D G-Code viewer for Klipper"],
"repo": "https://github.com/Kragrathea/pgcode",
"updates": true "updates": true
} }
} }

View File

@@ -1,16 +1,13 @@
{ {
"metadata": { "metadata": {
"index": 10, "index": 10,
"module": "simply_print_extension", "module": "simply_print_extension",
"maintained_by": "dw-0", "maintained_by": "dw-0",
"display_name": "SimplyPrint", "display_name": "SimplyPrint",
"description": [ "description": [
"3D Printer Cloud Management Software.", "3D Printer Cloud Management Software.",
"\n\n", "\n\n",
"3D printing doesn't have to be a complicated, analog, SD card-filled experience; step into the future of modern 3D printing" "3D printing doesn't have to be a complicated, analog, SD card-filled experience; step into the future of modern 3D printing"
], ]
"website": "https://simplyprint.io", }
"repo": "https://github.com/SimplyPrint",
"updates": false
}
} }

View File

@@ -13,7 +13,6 @@
"\n\n", "\n\n",
"Note: This extension installs Spoolman using Docker. Docker must be installed on your system before installing Spoolman." "Note: This extension installs Spoolman using Docker. Docker must be installed on your system before installing Spoolman."
], ],
"repo": "https://github.com/Donkie/Spoolman",
"updates": true "updates": true
} }
} }

View File

@@ -5,7 +5,7 @@
"maintained_by": "nlef", "maintained_by": "nlef",
"display_name": "Moonraker Telegram Bot", "display_name": "Moonraker Telegram Bot",
"description": ["Control your printer with the Telegram messenger app."], "description": ["Control your printer with the Telegram messenger app."],
"repo": "https://github.com/nlef/moonraker-telegram-bot", "project_url": "https://github.com/nlef/moonraker-telegram-bot",
"updates": true "updates": true
} }
} }

View File

@@ -42,13 +42,10 @@ from utils.sys_utils import (
def get_kiauh_version() -> str: def get_kiauh_version() -> str:
""" """
Helper method to get the current KIAUH version by reading the latest tag Helper method to get the current KIAUH version by reading the latest tag
:return: string of the latest tag or a default value if no tags exist :return: string of the latest tag
""" """
tags = get_local_tags(Path(__file__).parent.parent) lastest_tag: str = get_local_tags(Path(__file__).parent.parent)[-1]
if tags: return lastest_tag
return tags[-1]
else:
return "v?.?.?"
def convert_camelcase_to_kebabcase(name: str) -> str: def convert_camelcase_to_kebabcase(name: str) -> str:

View File

@@ -15,7 +15,6 @@ from extensions.obico.moonraker_obico import MoonrakerObico
from extensions.octoeverywhere.octoeverywhere import Octoeverywhere from extensions.octoeverywhere.octoeverywhere import Octoeverywhere
from extensions.octoapp.octoapp import Octoapp from extensions.octoapp.octoapp import Octoapp
from extensions.telegram_bot.moonraker_telegram_bot import MoonrakerTelegramBot from extensions.telegram_bot.moonraker_telegram_bot import MoonrakerTelegramBot
from extensions.octoprint.octoprint import Octoprint
InstanceType = TypeVar( InstanceType = TypeVar(
"InstanceType", "InstanceType",
@@ -25,5 +24,4 @@ InstanceType = TypeVar(
MoonrakerObico, MoonrakerObico,
Octoeverywhere, Octoeverywhere,
Octoapp, Octoapp,
Octoprint,
) )

View File

@@ -27,7 +27,7 @@ function moonraker_systemd() {
### ###
# any moonraker client that uses "moonraker" in its own name must be blacklisted using # any moonraker client that uses "moonraker" in its own name must be blacklisted using
# this variable, otherwise they will be falsely recognized as moonraker instances # this variable, otherwise they will be falsely recognized as moonraker instances
blacklist="obico|hmi|telegram-bot" blacklist="obico"
ignore="${SYSTEMD}/moonraker-(${blacklist}).service" ignore="${SYSTEMD}/moonraker-(${blacklist}).service"
match="${SYSTEMD}/moonraker(-[0-9a-zA-Z]+)?.service" match="${SYSTEMD}/moonraker(-[0-9a-zA-Z]+)?.service"