Compare commits

...

26 Commits

Author SHA1 Message Date
Cody D Dixon
ebd7bb4286 Merge 6c81ba2232 into 985b66d41f 2025-07-12 19:41:34 +02:00
Maksym Pyrozhok
985b66d41f chore: fix typos (#695)
Fix typo.
2025-07-12 19:36:38 +02:00
dw-0
f95d2586bf fix(webclient): add config.json to moonraker persistent files (#699)
fixes #694

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-06-28 10:12:28 +02:00
dw-0
f5141e7eff fix(mainsail): check for json configured as instanceDB (#698)
fix(mainsail): check for json configures as instanceDB

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-06-27 22:37:44 +02:00
dw-0
33113e72e9 fix: exception raised on pip warning (#688)
pip seems to write to stderr on warnings, caused by retries. even if the process exits with 0.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-05-31 17:44:02 +02:00
dw-0
6f59fd06aa fix: do not upgrade pip before installing packages (#680)
pip 25 seems to introduce some compatibility issues.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-05-02 20:08:32 +02:00
dw-0
56ea43ccb6 refactor: improve typesafety KiauhSettings
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-14 21:19:38 +02:00
dw-0
25e22c993f chore(scp): update SimpleConfigParser
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-14 21:15:12 +02:00
dw-0
ead521b377 refactor: replace mypy with pyright
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-14 21:07:56 +02:00
dw-0
3c952ccc12 refactor: use sane fallbacks on missing kiauh config options
for some options a warning is print if the fallback is used

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-12 15:12:11 +02:00
dw-0
c8f713c00e fix: no validation of optional_speedups option
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-12 00:36:34 +02:00
CodeMasterCody3D
6c81ba2232 Merge branch 'master' of https://github.com/CodeMasterCody3D/kiauhPlusDroidKlipp 2025-01-21 05:23:03 -06:00
CodeMasterCody3D
a777461296 yes 2025-01-21 05:22:16 -06:00
CodeMasterCody3D
2c045bb647 Update droidklipp.py
forgot to add imports
2025-01-21 04:11:03 -06:00
CodeMasterCody3D
a21e059328 Update droidklipp.py
fixed folder creating and bug
2025-01-21 04:08:48 -06:00
CodeMasterCody3D
6853e97fb8 Update droidklipp.py
fixed folder creation
2025-01-21 04:05:10 -06:00
CodeMasterCody3D
837488e4dd Update install_menu.py 2025-01-21 03:59:36 -06:00
CodeMasterCody3D
68cc03f3d0 Merge branch 'master' of https://github.com/CodeMasterCody3D/kiauhPlusDroidKlipp 2025-01-21 03:51:23 -06:00
CodeMasterCody3D
606686b33c droidklipp 2025-01-21 03:40:50 -06:00
CodeMasterCody3D
fc494e21da Update main_menu.py
droidklipp
2025-01-21 03:25:46 -06:00
CodeMasterCody3D
41fccb88fd Update common.py
droidklipp
2025-01-21 03:24:45 -06:00
CodeMasterCody3D
66975cd913 Update README.md
updated readme to add DroidKlipp
2025-01-21 03:13:08 -06:00
CodeMasterCody3D
89ad92468d Update README.md
install update
2025-01-21 03:09:55 -06:00
CodeMasterCody3D
ef44ba8253 droidklipp
droidklipp
2025-01-21 03:07:03 -06:00
CodeMasterCody3D
d41865e693 Update install_menu.py
added DroidKlipp
2025-01-21 03:06:16 -06:00
CodeMasterCody3D
ba594355ba Update install_menu.py 2025-01-21 02:15:34 -06:00
23 changed files with 301 additions and 204 deletions

3
.vs/ProjectSettings.json Normal file
View File

@@ -0,0 +1,3 @@
{
"CurrentProjectSetting": null
}

11
.vs/VSWorkspaceState.json Normal file
View File

@@ -0,0 +1,11 @@
{
"ExpandedNodes": [
"",
"\\kiauh",
"\\kiauh\\components\\droidklipp",
"\\kiauh\\core",
"\\kiauh\\core\\menus"
],
"SelectedNode": "\\kiauh\\core\\menus\\main_menu.py",
"PreviewInSolutionExplorer": false
}

Binary file not shown.

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View File

@@ -71,14 +71,14 @@ sudo apt-get update && sudo apt-get install git -y
Once git is installed, use the following command to download KIAUH into your home-directory: Once git is installed, use the following command to download KIAUH into your home-directory:
```shell ```shell
cd ~ && git clone https://github.com/dw-0/kiauh.git cd ~ && git clone https://github.com/CodeMasterCody3D/kiauhPlusDroidKlipp.git
``` ```
* **Step 3:** \ * **Step 3:** \
Finally, start KIAUH by running the next command: Finally, start KIAUH by running the next command:
```shell ```shell
./kiauh/kiauh.sh ./kiauhPlusDroidKlipp/kiauh.sh
``` ```
* **Step 4:** \ * **Step 4:** \

View File

@@ -0,0 +1,47 @@
import os
import subprocess
def install_droidklipp():
try:
print("Are you sure you want to install DroidKlipp? (Y/N)")
user_confirmation = input().strip().lower()
if user_confirmation != 'y':
print("DroidKlipp installation aborted.")
return
print("Installing DroidKlipp...")
subprocess.run(['sudo', 'apt', 'install', '-y', 'adb', 'tmux'], check=True)
# Define the DroidKlipp repository URL and directory
droidklipp_repo_url = "https://github.com/CodeMasterCody3D/DroidKlipp.git"
droidklipp_dir = os.path.expanduser('~/DroidKlipp')
# Check if DroidKlipp directory exists, if not create it
if not os.path.isdir(droidklipp_dir):
print("DroidKlipp folder not found, creating directory...")
os.makedirs(droidklipp_dir)
# Clone the repository if not already cloned
if not os.path.isdir(os.path.join(droidklipp_dir, '.git')):
print("Cloning the DroidKlipp repository...")
subprocess.run(['git', 'clone', droidklipp_repo_url, droidklipp_dir], check=True)
else:
print("DroidKlipp repository already exists.")
# Change to the DroidKlipp directory
os.chdir(droidklipp_dir)
# Set executable permissions for the installation script
subprocess.run(['sudo', 'chmod', '+x', 'droidklipp.sh'], check=True)
# Run the installation script
subprocess.run(['./droidklipp.sh'], check=True)
print("DroidKlipp installation complete!")
except subprocess.CalledProcessError as e:
print(f"Error during installation: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
# Ensure you call this with proper confirmation before installation

View File

@@ -102,6 +102,7 @@ def install_client(
section=f"update_manager {client.name}", section=f"update_manager {client.name}",
instances=mr_instances, instances=mr_instances,
options=[ options=[
("persistent_files", ["config.json"]),
("type", "web"), ("type", "web"),
("channel", "stable"), ("channel", "stable"),
("repo", str(client.repo_path)), ("repo", str(client.repo_path)),

View File

@@ -119,7 +119,7 @@ def enable_mainsail_remotemode() -> None:
with open(c_json, "r") as f: with open(c_json, "r") as f:
config_data = json.load(f) config_data = json.load(f)
if config_data["instancesDB"] == "browser": if config_data["instancesDB"] == "browser" or config_data["instancesDB"] == "json":
Logger.print_info("Remote mode already configured. Skipped ...") Logger.print_info("Remote mode already configured. Skipped ...")
return return

View File

@@ -11,6 +11,7 @@ from __future__ import annotations
import textwrap import textwrap
from typing import Type from typing import Type
from components.droidklipp.droidklipp import install_droidklipp
from components.crowsnest.crowsnest import install_crowsnest from components.crowsnest.crowsnest import install_crowsnest
from components.klipper.services.klipper_setup_service import KlipperSetupService from components.klipper.services.klipper_setup_service import KlipperSetupService
from components.klipperscreen.klipperscreen import install_klipperscreen from components.klipperscreen.klipperscreen import install_klipperscreen
@@ -41,7 +42,6 @@ class InstallMenu(BaseMenu):
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu = previous_menu if previous_menu is not None else MainMenu self.previous_menu = previous_menu if previous_menu is not None else MainMenu
def set_options(self) -> None: def set_options(self) -> None:
@@ -53,7 +53,9 @@ class InstallMenu(BaseMenu):
"5": Option(method=self.install_mainsail_config), "5": Option(method=self.install_mainsail_config),
"6": Option(method=self.install_fluidd_config), "6": Option(method=self.install_fluidd_config),
"7": Option(method=self.install_klipperscreen), "7": Option(method=self.install_klipperscreen),
"8": Option(method=self.install_crowsnest), "8": Option(method=self.install_droidklipp), # Add DroidKlipp option
"9": Option(method=self.install_crowsnest),
} }
def print_menu(self) -> None: def print_menu(self) -> None:
@@ -62,15 +64,17 @@ class InstallMenu(BaseMenu):
╟───────────────────────────┬───────────────────────────╢ ╟───────────────────────────┬───────────────────────────╢
║ Firmware & API: │ Touchscreen GUI: ║ ║ Firmware & API: │ Touchscreen GUI: ║
║ 1) [Klipper] │ 7) [KlipperScreen] ║ ║ 1) [Klipper] │ 7) [KlipperScreen] ║
║ 2) [Moonraker] │ ║ 2) [Moonraker] │ 8) [DroidKlipp]
║ │ ║
║ │ Webcam Streamer: ║ ║ │ Webcam Streamer: ║
║ Webinterface: │ 8) [Crowsnest] ║ ║ Webinterface: │ 9) [Crowsnest] ║
║ 3) [Mainsail] │ ║ ║ 3) [Mainsail] │ ║
║ 4) [Fluidd] │ ║ ║ 4) [Fluidd] │ ║
║ │ ║ ║ │ ║
║ Client-Config: │ ║ ║ Client-Config: │ ║
║ 5) [Mainsail-Config] │ ║ ║ 5) [Mainsail-Config] │ ║
║ 6) [Fluidd-Config] │ ║ ║ 6) [Fluidd-Config] │ ║
║ │ ║
╟───────────────────────────┴───────────────────────────╢ ╟───────────────────────────┴───────────────────────────╢
""" """
)[1:] )[1:]
@@ -107,3 +111,6 @@ class InstallMenu(BaseMenu):
def install_crowsnest(self, **kwargs) -> None: def install_crowsnest(self, **kwargs) -> None:
install_crowsnest() install_crowsnest()
def install_droidklipp(self, **kwargs) -> None:
install_droidklipp()

View File

@@ -12,6 +12,7 @@ import sys
import textwrap import textwrap
from typing import Callable, Type from typing import Callable, Type
from components.droidklipp.droidklipp import install_droidklipp
from components.crowsnest.crowsnest import get_crowsnest_status from components.crowsnest.crowsnest import get_crowsnest_status
from components.klipper.klipper_utils import get_klipper_status from components.klipper.klipper_utils import get_klipper_status
from components.klipperscreen.klipperscreen import get_klipperscreen_status from components.klipperscreen.klipperscreen import get_klipperscreen_status

View File

@@ -8,14 +8,15 @@
# ======================================================================= # # ======================================================================= #
from __future__ import annotations from __future__ import annotations
import shutil
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, List from typing import Any, Callable, List, TypeVar
from components.klipper import KLIPPER_REPO_URL
from components.moonraker import MOONRAKER_REPO_URL
from core.backup_manager.backup_manager import BackupManager from core.backup_manager.backup_manager import BackupManager
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
NoOptionError,
NoSectionError,
SimpleConfigParser, SimpleConfigParser,
) )
from utils.input_utils import get_confirm from utils.input_utils import get_confirm
@@ -26,13 +27,7 @@ from kiauh import PROJECT_ROOT
DEFAULT_CFG = PROJECT_ROOT.joinpath("default.kiauh.cfg") DEFAULT_CFG = PROJECT_ROOT.joinpath("default.kiauh.cfg")
CUSTOM_CFG = PROJECT_ROOT.joinpath("kiauh.cfg") CUSTOM_CFG = PROJECT_ROOT.joinpath("kiauh.cfg")
T = TypeVar("T")
class NoValueError(Exception):
"""Raised when a required value is not defined for an option"""
def __init__(self, section: str, option: str):
msg = f"Missing value for option '{option}' in section '{section}'"
super().__init__(msg)
class InvalidValueError(Exception): class InvalidValueError(Exception):
@@ -53,17 +48,20 @@ class Repository:
url: str url: str
branch: str branch: str
@dataclass @dataclass
class KlipperSettings: class KlipperSettings:
repositories: List[Repository] | None = field(default=None) repositories: List[Repository] | None = field(default=None)
use_python_binary: str | None = field(default=None) use_python_binary: str | None = field(default=None)
@dataclass @dataclass
class MoonrakerSettings: class MoonrakerSettings:
optional_speedups: bool | None = field(default=None) optional_speedups: bool | None = field(default=None)
repositories: List[Repository] | None = field(default=None) repositories: List[Repository] | None = field(default=None)
use_python_binary: str | None = field(default=None) use_python_binary: str | None = field(default=None)
@dataclass @dataclass
class WebUiSettings: class WebUiSettings:
port: int | None = field(default=None) port: int | None = field(default=None)
@@ -74,6 +72,7 @@ class WebUiSettings:
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KiauhSettings: class KiauhSettings:
__instance = None __instance = None
__initialized = False
def __new__(cls, *args, **kwargs) -> "KiauhSettings": def __new__(cls, *args, **kwargs) -> "KiauhSettings":
if cls.__instance is None: if cls.__instance is None:
@@ -91,11 +90,10 @@ class KiauhSettings:
return getattr(self, item) return getattr(self, item)
def __init__(self) -> None: def __init__(self) -> None:
if not hasattr(self, "__initialized"):
self.__initialized = False
if self.__initialized: if self.__initialized:
return return
self.__initialized = True self.__initialized = True
self.config = SimpleConfigParser() self.config = SimpleConfigParser()
self.kiauh = AppSettings() self.kiauh = AppSettings()
self.klipper = KlipperSettings() self.klipper = KlipperSettings()
@@ -103,8 +101,9 @@ class KiauhSettings:
self.mainsail = WebUiSettings() self.mainsail = WebUiSettings()
self.fluidd = WebUiSettings() self.fluidd = WebUiSettings()
self._load_config() self.__read_config_set_internal_state()
# todo: refactor this, at least rename to something else!
def get(self, section: str, option: str) -> str | int | bool: def get(self, section: str, option: str) -> str | int | bool:
""" """
Get a value from the settings state by providing the section and option name as Get a value from the settings state by providing the section and option name as
@@ -122,135 +121,175 @@ class KiauhSettings:
raise raise
def save(self) -> None: def save(self) -> None:
self._set_config_options_state() self.__write_internal_state_to_cfg()
self.config.write_file(CUSTOM_CFG) self.__read_config_set_internal_state()
self._load_config()
def _load_config(self) -> None: def __read_config_set_internal_state(self) -> None:
if not CUSTOM_CFG.exists() and not DEFAULT_CFG.exists(): if not CUSTOM_CFG.exists() and not DEFAULT_CFG.exists():
self.__kill() Logger.print_dialog(
DialogType.ERROR,
cfg = CUSTOM_CFG if CUSTOM_CFG.exists() else DEFAULT_CFG [
self.config.read_file(cfg) "No KIAUH configuration file found! Please make sure you have at least "
"one of the following configuration files in KIAUH's root directory:",
needs_migration = self._check_deprecated_repo_config() "● default.kiauh.cfg",
if needs_migration: "● kiauh.cfg",
self._prompt_migration_dialog() ],
return )
else:
# Only validate if no migration was needed
self._validate_cfg()
self.__set_internal_state()
def _validate_cfg(self) -> None:
def __err_and_kill(error: str) -> None:
Logger.print_error(f"Error validating kiauh.cfg: {error}")
kill() kill()
try: # copy default config to custom config if it does not exist
self._validate_bool("kiauh", "backup_before_update") if not CUSTOM_CFG.exists():
shutil.copyfile(DEFAULT_CFG, CUSTOM_CFG)
self._validate_repositories("klipper", "repositories") self.config.read_file(CUSTOM_CFG)
self._validate_repositories("moonraker", "repositories")
self._validate_int("mainsail", "port") # check if there are deprecated repo_url and branch options in the kiauh.cfg
self._validate_bool("mainsail", "unstable_releases") if self._check_deprecated_repo_config():
self._prompt_migration_dialog()
self._validate_int("fluidd", "port") self.__set_internal_state()
self._validate_bool("fluidd", "unstable_releases")
self._validate_bool("moonraker", "optional_speedups")
except ValueError:
err = f"Invalid value for option '{self._v_option}' in section '{self._v_section}'"
__err_and_kill(err)
except NoSectionError:
err = f"Missing section '{self._v_section}' in config file"
__err_and_kill(err)
except NoOptionError:
err = f"Missing option '{self._v_option}' in section '{self._v_section}'"
__err_and_kill(err)
except NoValueError:
err = f"Missing value for option '{self._v_option}' in section '{self._v_section}'"
__err_and_kill(err)
def _validate_bool(self, section: str, option: str) -> None:
self._v_section, self._v_option = (section, option)
(bool(self.config.getboolean(section, option)))
def _validate_int(self, section: str, option: str) -> None:
self._v_section, self._v_option = (section, option)
int(self.config.getint(section, option))
def _validate_str(self, section: str, option: str) -> None:
self._v_section, self._v_option = (section, option)
v = self.config.getval(section, option)
if not v:
raise ValueError
def _validate_repositories(self, section: str, option: str) -> None:
self._v_section, self._v_option = (section, option)
repos = self.config.getval(section, option)
if not repos:
raise NoValueError(section, option)
for repo in repos:
if repo.strip().startswith("#") or repo.strip().startswith(";"):
continue
try:
if "," in repo:
url, branch = repo.strip().split(",")
if not url:
raise InvalidValueError(section, option, repo)
else:
url = repo.strip()
if not url:
raise InvalidValueError(section, option, repo)
except ValueError:
raise InvalidValueError(section, option, repo)
def __set_internal_state(self) -> None: def __set_internal_state(self) -> None:
self.kiauh.backup_before_update = self.config.getboolean( # parse Kiauh options
"kiauh", "backup_before_update" self.kiauh.backup_before_update = self.__read_from_cfg(
"kiauh",
"backup_before_update",
self.config.getboolean,
False,
) )
self.moonraker.optional_speedups = self.config.getboolean("moonraker", "optional_speedups", True) # parse Klipper options
self.klipper.use_python_binary = self.__read_from_cfg(
kl_repos = self.config.getval("klipper", "repositories") "klipper",
self.klipper.repositories = self.__set_repo_state(kl_repos) "use_python_binary",
self.config.getval,
mr_repos = self.config.getval("moonraker", "repositories") None,
self.moonraker.repositories = self.__set_repo_state(mr_repos) True,
self.klipper.use_python_binary = self.config.getval("klipper", "use_python_binary", None)
self.moonraker.use_python_binary = self.config.getval("moonraker", "use_python_binary", None)
self.mainsail.port = self.config.getint("mainsail", "port")
self.mainsail.unstable_releases = self.config.getboolean(
"mainsail", "unstable_releases"
) )
self.fluidd.port = self.config.getint("fluidd", "port") kl_repos: List[str] = self.__read_from_cfg(
self.fluidd.unstable_releases = self.config.getboolean( "klipper",
"fluidd", "unstable_releases" "repositories",
self.config.getvals,
[KLIPPER_REPO_URL],
)
self.klipper.repositories = self.__set_repo_state("klipper", kl_repos)
# parse Moonraker options
self.moonraker.use_python_binary = self.__read_from_cfg(
"moonraker",
"use_python_binary",
self.config.getval,
None,
True,
)
self.moonraker.optional_speedups = self.__read_from_cfg(
"moonraker",
"optional_speedups",
self.config.getboolean,
True,
)
mr_repos: List[str] = self.__read_from_cfg(
"moonraker",
"repositories",
self.config.getvals,
[MOONRAKER_REPO_URL],
)
self.moonraker.repositories = self.__set_repo_state("moonraker", mr_repos)
# parse Mainsail options
self.mainsail.port = self.__read_from_cfg(
"mainsail",
"port",
self.config.getint,
80,
)
self.mainsail.unstable_releases = self.__read_from_cfg(
"mainsail",
"unstable_releases",
self.config.getboolean,
False,
) )
def __set_repo_state(self, repos: List[str]) -> List[Repository]: # parse Fluidd options
self.fluidd.port = self.__read_from_cfg(
"fluidd",
"port",
self.config.getint,
80,
)
self.fluidd.unstable_releases = self.__read_from_cfg(
"fluidd",
"unstable_releases",
self.config.getboolean,
False,
)
def __check_option_exists(
self, section: str, option: str, fallback: Any, silent: bool = False
) -> bool:
has_section = self.config.has_section(section)
has_option = self.config.has_option(section, option)
if not (has_section and has_option):
if not silent:
Logger.print_warn(
f"Option '{option}' in section '{section}' not defined. Falling back to '{fallback}'."
)
return False
return True
def __read_bool_from_cfg(
self,
section: str,
option: str,
fallback: bool | None = None,
silent: bool = False,
) -> bool | None:
if not self.__check_option_exists(section, option, fallback, silent):
return fallback
return self.config.getboolean(section, option, fallback)
def __read_from_cfg(
self,
section: str,
option: str,
getter: Callable[[str, str, T | None], T],
fallback: T = None,
silent: bool = False,
) -> T:
if not self.__check_option_exists(section, option, fallback, silent):
return fallback
return getter(section, option, fallback)
def __set_repo_state(self, section: str, repos: List[str]) -> List[Repository]:
_repos: List[Repository] = [] _repos: List[Repository] = []
for repo in repos: for repo in repos:
if repo.strip().startswith("#") or repo.strip().startswith(";"): try:
continue if repo.strip().startswith("#") or repo.strip().startswith(";"):
if "," in repo: continue
url, branch = repo.strip().split(",") if "," in repo:
if not branch: url, branch = repo.strip().split(",")
if not branch:
branch = "master"
else:
url = repo.strip()
branch = "master" branch = "master"
else:
url = repo.strip() # url must not be empty otherwise it's considered
branch = "master" # as an unrecoverable, invalid configuration
_repos.append(Repository(url.strip(), branch.strip())) if not url:
raise InvalidValueError(section, "repositories", repo)
_repos.append(Repository(url.strip(), branch.strip()))
except InvalidValueError as e:
Logger.print_error(f"Error parsing kiauh.cfg: {e}")
kill()
return _repos return _repos
def _set_config_options_state(self) -> None: def __write_internal_state_to_cfg(self) -> None:
"""Updates the config with current settings, preserving values that haven't been modified""" """Updates the config with current settings, preserving values that haven't been modified"""
if self.kiauh.backup_before_update is not None: if self.kiauh.backup_before_update is not None:
self.config.set_option( self.config.set_option(
@@ -288,6 +327,8 @@ class KiauhSettings:
"fluidd", "unstable_releases", str(self.fluidd.unstable_releases) "fluidd", "unstable_releases", str(self.fluidd.unstable_releases)
) )
self.config.write_file(CUSTOM_CFG)
def _check_deprecated_repo_config(self) -> bool: def _check_deprecated_repo_config(self) -> bool:
# repo_url and branch are deprecated - 2025.03.23 # repo_url and branch are deprecated - 2025.03.23
for section in ["klipper", "moonraker"]: for section in ["klipper", "moonraker"]:
@@ -299,22 +340,23 @@ class KiauhSettings:
def _prompt_migration_dialog(self) -> None: def _prompt_migration_dialog(self) -> None:
migration_1: List[str] = [ migration_1: List[str] = [
"The old 'repo_url' and 'branch' options are now combined under 'repositories'.", "Options 'repo_url' and 'branch' are now combined into a 'repositories' option.",
"\n\n", "\n\n",
"Example format:", "● Old format:",
"[klipper]", " [klipper]",
"repositories:", " repo_url: https://github.com/Klipper3d/klipper",
" https://github.com/Klipper3d/klipper, master", " branch: master",
"\n\n", "\n\n",
"[moonraker]", "● New format:",
"repositories:", " [klipper]",
" https://github.com/Arksine/moonraker, master", " repositories:",
" https://github.com/Klipper3d/klipper, master",
] ]
Logger.print_dialog( Logger.print_dialog(
DialogType.ATTENTION, DialogType.ATTENTION,
[ [
"Deprecated repository configuration found!", "Deprecated kiauh.cfg configuration found!",
"KAIUH can now attempt to automatically migrate your configuration.", "KAIUH can now attempt to automatically migrate the configuration.",
"\n\n", "\n\n",
*migration_1, *migration_1,
], ],
@@ -325,7 +367,7 @@ class KiauhSettings:
Logger.print_dialog( Logger.print_dialog(
DialogType.ERROR, DialogType.ERROR,
[ [
"Please update your configuration file manually.", "Please update the configuration file manually.",
], ],
center_content=True, center_content=True,
) )
@@ -366,23 +408,7 @@ class KiauhSettings:
self.config.write_file(CUSTOM_CFG) self.config.write_file(CUSTOM_CFG)
self.config.read_file(CUSTOM_CFG) # reload config self.config.read_file(CUSTOM_CFG) # reload config
# Validate the migrated config
self._validate_cfg()
self.__set_internal_state()
except Exception as e: except Exception as e:
Logger.print_error(f"Error migrating configuration: {e}") Logger.print_error(f"Error migrating configuration: {e}")
Logger.print_error("Please migrate manually.") Logger.print_error("Please migrate manually.")
kill() kill()
def __kill(self) -> None:
Logger.print_dialog(
DialogType.ERROR,
[
"No KIAUH configuration file found! Please make sure you have at least "
"one of the following configuration files in KIAUH's root directory:",
"● default.kiauh.cfg",
"● kiauh.cfg",
],
)
kill()

View File

@@ -314,9 +314,7 @@ class SimpleConfigParser:
elements.pop(i) elements.pop(i)
break break
def getval( def getval(self, section: str, option: str, fallback: str | _UNSET = _UNSET) -> str:
self, section: str, option: str, fallback: str | _UNSET = _UNSET
) -> str | List[str]:
""" """
Return the value of the given option in the given section Return the value of the given option in the given section
@@ -329,22 +327,34 @@ class SimpleConfigParser:
if option not in self.get_options(section): if option not in self.get_options(section):
raise NoOptionError(option, section) raise NoOptionError(option, section)
# Find the option in the elements list
for element in self.config[section]["elements"]: for element in self.config[section]["elements"]:
if element["type"] in [LineType.OPTION.value, LineType.OPTION_BLOCK.value] and element["name"] == option: if element["type"] is LineType.OPTION.value and element["name"] == option:
raw_value = element["value"] return str(element["value"].strip().replace("\n", ""))
if isinstance(raw_value, str) and raw_value.endswith("\n"): return ""
return raw_value[:-1].strip()
elif isinstance(raw_value, list): except (NoSectionError, NoOptionError):
values: List[str] = [] if fallback is _UNSET:
for i, val in enumerate(raw_value): raise
val = val.strip().strip("\n") return fallback
if len(val) < 1:
continue def getvals(self, section: str, option: str, fallback: List[str] | _UNSET = _UNSET) -> List[str]:
values.append(val.strip()) """
return values Return the values of the given multi-line option in the given section
return str(raw_value)
raise NoOptionError(option, section) If the key is not found and 'fallback' is provided, it is used as
a fallback value.
"""
try:
if section not in self.get_sections():
raise NoSectionError(section)
if option not in self.get_options(section):
raise NoOptionError(option, section)
for element in self.config[section]["elements"]:
if element["type"] is LineType.OPTION_BLOCK.value and element["name"] == option:
return [val.strip() for val in element["value"] if val.strip()]
return []
except (NoSectionError, NoOptionError): except (NoSectionError, NoOptionError):
if fallback is _UNSET: if fallback is _UNSET:
raise raise

View File

@@ -51,7 +51,7 @@ def test_getval(parser):
assert parser.getval("section_2", "option_2") == "value_2" assert parser.getval("section_2", "option_2") == "value_2"
# test multiline option values # test multiline option values
ml_val = parser.getval("section number 5", "multi_option") ml_val = parser.getvals("section number 5", "multi_option")
assert isinstance(ml_val, list) assert isinstance(ml_val, list)
assert len(ml_val) > 0 assert len(ml_val) > 0
@@ -164,7 +164,7 @@ def test_set_new_option(parser):
assert parser.getval("new_section", "very_new_option") == "very_new_value" assert parser.getval("new_section", "very_new_option") == "very_new_value"
parser.set_option("section_2", "array_option", ["value_1", "value_2", "value_3"]) parser.set_option("section_2", "array_option", ["value_1", "value_2", "value_3"])
assert parser.getval("section_2", "array_option") == [ assert parser.getvals("section_2", "array_option") == [
"value_1", "value_1",
"value_2", "value_2",
"value_3", "value_3",

View File

@@ -9,6 +9,8 @@
from __future__ import annotations from __future__ import annotations
from components.droidklipp.droidklipp import install_droidklipp
import re import re
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path

View File

@@ -11,7 +11,7 @@ from __future__ import annotations
import shutil import shutil
import tempfile import tempfile
from pathlib import Path from pathlib import Path
from typing import List, Tuple from typing import List, Tuple, Union
from core.logger import Logger from core.logger import Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
@@ -19,7 +19,7 @@ from core.submodules.simple_config_parser.src.simple_config_parser.simple_config
) )
from utils.instance_type import InstanceType from utils.instance_type import InstanceType
ConfigOption = Tuple[str, str] ConfigOption = Tuple[str, Union[str, List[str]]]
def add_config_section( def add_config_section(

View File

@@ -184,9 +184,6 @@ def install_python_requirements(target: Path, requirements: Path) -> None:
:return: None :return: None
""" """
try: try:
# always update pip before installing requirements
update_python_pip(target)
Logger.print_status("Installing Python requirements ...") Logger.print_status("Installing Python requirements ...")
command = [ command = [
target.joinpath("bin/pip").as_posix(), target.joinpath("bin/pip").as_posix(),
@@ -196,7 +193,7 @@ def install_python_requirements(target: Path, requirements: Path) -> None:
] ]
result = run(command, stderr=PIPE, text=True) result = run(command, stderr=PIPE, text=True)
if result.returncode != 0 or result.stderr: if result.returncode != 0:
Logger.print_error(f"{result.stderr}", False) Logger.print_error(f"{result.stderr}", False)
raise VenvCreationFailedException("Installing Python requirements failed!") raise VenvCreationFailedException("Installing Python requirements failed!")
@@ -216,9 +213,6 @@ def install_python_packages(target: Path, packages: List[str]) -> None:
:return: None :return: None
""" """
try: try:
# always update pip before installing requirements
update_python_pip(target)
Logger.print_status("Installing Python requirements ...") Logger.print_status("Installing Python requirements ...")
command = [ command = [
target.joinpath("bin/pip").as_posix(), target.joinpath("bin/pip").as_posix(),
@@ -228,7 +222,7 @@ def install_python_packages(target: Path, packages: List[str]) -> None:
command.append(pkg) command.append(pkg)
result = run(command, stderr=PIPE, text=True) result = run(command, stderr=PIPE, text=True)
if result.returncode != 0 or result.stderr: if result.returncode != 0:
Logger.print_error(f"{result.stderr}", False) Logger.print_error(f"{result.stderr}", False)
raise VenvCreationFailedException("Installing Python requirements failed!") raise VenvCreationFailedException("Installing Python requirements failed!")

View File

@@ -2,7 +2,7 @@
requires-python = ">=3.8" requires-python = ">=3.8"
[project.optional-dependencies] [project.optional-dependencies]
dev=["ruff", "mypy"] dev=["ruff", "pyright"]
[tool.ruff] [tool.ruff]
required-version = ">=0.9.10" required-version = ">=0.9.10"
@@ -20,14 +20,3 @@ quote-style = "double"
[tool.ruff.lint] [tool.ruff.lint]
extend-select = ["I"] extend-select = ["I"]
[tool.mypy]
python_version = "3.8"
platform = "linux"
# strict = true # TODO: enable this once everything is else is handled
check_untyped_defs = true
ignore_missing_imports = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true

6
pyrightconfig.json Normal file
View File

@@ -0,0 +1,6 @@
{
"pythonVersion": "3.8",
"pythonPlatform": "Linux",
"typeCheckingMode": "standard",
"venvPath": "./.kiauh-env"
}

2
requirements-dev.txt Normal file
View File

@@ -0,0 +1,2 @@
ruff (>=0.9.10)
pyright

View File

@@ -280,7 +280,6 @@ function create_klipper_virtualenv() {
status_msg "Installing $("python${python_version}" -V) virtual environment..." status_msg "Installing $("python${python_version}" -V) virtual environment..."
if virtualenv -p "python${python_version}" "${KLIPPY_ENV}"; then if virtualenv -p "python${python_version}" "${KLIPPY_ENV}"; then
(( python_version == 3 )) && "${KLIPPY_ENV}"/bin/pip install -U pip
"${KLIPPY_ENV}"/bin/pip install -r "${KLIPPER_DIR}"/scripts/klippy-requirements.txt "${KLIPPY_ENV}"/bin/pip install -r "${KLIPPER_DIR}"/scripts/klippy-requirements.txt
else else
log_error "failure while creating python3 klippy-env" log_error "failure while creating python3 klippy-env"

View File

@@ -126,7 +126,7 @@ function update_klipperscreen() {
git checkout -f master && ok_msg "Checkout successfull" git checkout -f master && ok_msg "Checkout successfull"
if [[ $(md5sum "${KLIPPERSCREEN_DIR}/scripts/KlipperScreen-requirements.txt" | cut -d " " -f1) != "${old_md5}" ]]; then if [[ $(md5sum "${KLIPPERSCREEN_DIR}/scripts/KlipperScreen-requirements.txt" | cut -d " " -f1) != "${old_md5}" ]]; then
status_msg "New dependecies detected..." status_msg "New dependencies detected..."
"${KLIPPERSCREEN_ENV}"/bin/pip install -r "${KLIPPERSCREEN_DIR}/scripts/KlipperScreen-requirements.txt" "${KLIPPERSCREEN_ENV}"/bin/pip install -r "${KLIPPERSCREEN_DIR}/scripts/KlipperScreen-requirements.txt"
ok_msg "Dependencies have been installed!" ok_msg "Dependencies have been installed!"
fi fi

View File

@@ -133,7 +133,7 @@ function update_mobileraker() {
git checkout -f main && ok_msg "Checkout successfull" git checkout -f main && ok_msg "Checkout successfull"
if [[ $(md5sum "${MOBILERAKER_DIR}/scripts/mobileraker-requirements.txt" | cut -d " " -f1) != "${old_md5}" ]]; then if [[ $(md5sum "${MOBILERAKER_DIR}/scripts/mobileraker-requirements.txt" | cut -d " " -f1) != "${old_md5}" ]]; then
status_msg "New dependecies detected..." status_msg "New dependencies detected..."
"${MOBILERAKER_ENV}"/bin/pip install -r "${MOBILERAKER_DIR}/scripts/mobileraker-requirements.txt" "${MOBILERAKER_ENV}"/bin/pip install -r "${MOBILERAKER_DIR}/scripts/mobileraker-requirements.txt"
ok_msg "Dependencies have been installed!" ok_msg "Dependencies have been installed!"
fi fi

View File

@@ -336,7 +336,6 @@ function create_moonraker_virtualenv() {
[[ -d ${MOONRAKER_ENV} ]] && rm -rf "${MOONRAKER_ENV}" [[ -d ${MOONRAKER_ENV} ]] && rm -rf "${MOONRAKER_ENV}"
if virtualenv -p /usr/bin/python3 "${MOONRAKER_ENV}"; then if virtualenv -p /usr/bin/python3 "${MOONRAKER_ENV}"; then
"${MOONRAKER_ENV}"/bin/pip install -U pip
"${MOONRAKER_ENV}"/bin/pip install -r "${MOONRAKER_DIR}/scripts/moonraker-requirements.txt" "${MOONRAKER_ENV}"/bin/pip install -r "${MOONRAKER_DIR}/scripts/moonraker-requirements.txt"
else else
log_error "failure while creating python3 moonraker-env" log_error "failure while creating python3 moonraker-env"