Compare commits

..

800 Commits

Author SHA1 Message Date
dw-0
6637f07067 Merge 7ec055f562 into b6c6edb622 2024-03-21 16:34:28 +01:00
dw-0
7ec055f562 refactor(webclients): always remove config sections
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-20 22:10:51 +01:00
dw-0
9eb0531cdf fix(moonraker): patch klippy_uds_address section when converting single to multi instance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-20 21:40:03 +01:00
dw-0
84cda99af8 fix(klipper): patch virtual_sdcard section when converting single to multi instance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-20 21:39:20 +01:00
dw-0
5f823c2d3a refactor(klipper): use correct virtual_sdcard path after multi-conversion
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-18 22:18:45 +01:00
dw-0
758a783ede refactor(moonraker): allow re-running installer if instance count already matches
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 21:01:30 +01:00
dw-0
682baaa105 refactor(kiauh): remove unused function
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 20:56:14 +01:00
dw-0
601ccb2191 fix(webclients): add symlink to added klipper instances
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 20:53:49 +01:00
dw-0
c0caab13b3 fix(kiauh): typo
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 20:34:21 +01:00
dw-0
7c754de08e refactor(utils): re-arrange message printing
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 20:31:30 +01:00
dw-0
9dc556e7e4 fix(webclients): correctly remove symlink
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 20:26:01 +01:00
dw-0
655b781aef refactor(KIAUH): move util functions into util modules
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 11:42:27 +01:00
dw-0
14aafd558a refactor(moonraker): add existing client and client configs to example config upon creation
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 11:33:49 +01:00
dw-0
bd1aa1ae2b refactor(klipper): add existing client configs to example config upon creation
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-03 11:04:06 +01:00
dw-0
8df75dc8d0 fix(webclients): print to screen if symlink does not exist
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-02 21:47:27 +01:00
dw-0
5c37b68463 fix(webclients): default to port 80 if none/invalid configured in kiauh.cfg
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-02 21:25:17 +01:00
dw-0
1620efe56c refactor(KIAUH): full refactor of client and client-config installation
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-03-02 17:22:37 +01:00
dw-0
7fd91e6cef refactor(KIAUH): allow reading ipv6 configured ports with possible default_server suffixes
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-24 15:46:02 +01:00
dw-0
750cb7b307 refactor(KIAUH): update NGINX config to match mainsails structure
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-24 15:44:19 +01:00
dw-0
384503c4f5 feat(Fluidd): add Fluidd
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-24 15:26:32 +01:00
dw-0
b6c6edb622 refactor(Mobileraker): update companion install script (#431) (#433) 2024-02-24 14:53:41 +01:00
dw-0
2a4fcf3a3a refactor(KIAUH): add application root to sys path
shortens imports and helps with auto imports from IDE

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-18 22:30:28 +01:00
dw-0
573dc7c3c9 refactor(Mainsail): use urllib.request instead of requests module
requests is actually not part of the python 3.8 standard library, hence we use urllib.request now, which is.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-18 22:08:21 +01:00
dw-0
05b4ef2d18 refactor(utils): raise exception if pip not found in venv
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-11 20:19:29 +01:00
dw-0
863c62511c fix(klipper): add python3-venv dependency for creating venv
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-11 20:19:29 +01:00
dw-0
be5f345a7c style: reformat code
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-10 16:21:19 +01:00
dw-0
948927cfd3 feat: implement backup menu and backup methods for existing components
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-10 11:47:27 +01:00
dw-0
34ebe5d15e refactor(BackupManager): backup_file method only takes in single files now
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-10 11:40:38 +01:00
dw-0
3bef6ecb85 feat(BackupManager): allow to ignore folders
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-10 00:50:45 +01:00
dw-0
5ace920d3e feat(extensions): implement initial extension feature and first extension
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-09 15:47:13 +01:00
dw-0
2f34253bad refactor(kiauh): handle menus based on if they need instantiation or not
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-08 22:41:52 +01:00
dw-0
0447bc4405 refactor(kiauh): allow menus to link options to letters
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-02-05 21:57:19 +01:00
dw-0
7cb2231584 chore(kiauh): rename "res" to "assets"
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-29 21:28:01 +01:00
dw-0
5a3d21c40b chore(kiauh): rename "modules" to "components"
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-29 21:20:26 +01:00
dw-0
099d47df2f fix: revert "refactor(Mobileraker): update companion install script (#431)" (#432)
This reverts commit ba1cdb3739.
2024-01-29 17:17:09 +01:00
Patrick Schmidt
ba1cdb3739 refactor(Mobileraker): update companion install script (#431) 2024-01-29 17:09:30 +01:00
dw-0
ad56b51e70 feat(LogUpload): implement log upload feature
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-28 22:27:38 +01:00
dw-0
c6999f1990 refactor(kiauh): if self.options is an empty dict, return invalid input error message.
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-28 22:26:54 +01:00
dw-0
bc30cf418b refactor(kiauh): add option index parameter to method calls from menus
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-28 22:25:22 +01:00
dw-0
ee81ee4c0c fix(Mainsail): correctly handle invalid config value for default_port
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-28 15:35:00 +01:00
dw-0
35911604af chore(kiauh): update copyright
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-28 12:54:37 +01:00
dw-0
77f1089041 chore(kiauh): reformat code / rename method
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-28 12:54:35 +01:00
Anna
8e7d4db988 fix: OctoPrint logo in README.md (#424)
* Fix OctoPrint logo in Readme

Fixed the broken link to the OctoPrint logo in the Readme under the Sources section. Now uses the logo present in the octoprint docs directory.

* Update OctoPrint logo to "raw" cdn link

* Changed ?raw=true link to raw.githubusercontent.com

---------

Co-authored-by: dw-0 <th33xitus@gmail.com>
2024-01-23 22:47:37 +01:00
jneilliii
8f960495ba fix: OctoPrint logo in README.md (#423) 2024-01-22 17:57:37 +01:00
dw-0
095823bf28 chore(kiauh): update FUNDING.yml
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-01 20:02:29 +01:00
dw-0
397038e43e chore(kiauh): add .idea to .gitignore
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-01 19:55:47 +01:00
dw-0
061e222664 chore(kiauh): update copyright
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-01 19:54:13 +01:00
dw-0
3f5ff50d69 refactor(kiauh): use sudo true instead of sudo -v to prevent password prompts
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-01-01 19:51:56 +01:00
dw-0
7820155094 refactor(Klipper): add some exception handling
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-29 19:47:45 +01:00
dw-0
c28d5c28b9 refactor(KIAUH): use pythons own venv module to create a venv
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-29 19:39:45 +01:00
dw-0
cda6d31a7c fix(RepoManager): check if git dir exists
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-29 18:57:53 +01:00
dw-0
9a657daffd feat(KIAUH): show Mainsail in UpdateMenu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-28 18:05:43 +01:00
dw-0
85b4b68f16 refactor(Klipper/Moonraker): install new packages during updates
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-28 13:49:28 +01:00
dw-0
dfbce3b489 feat(KIAUH): show commit in UpdateMenu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-28 13:38:24 +01:00
dw-0
f3b0e45e39 refactor(Klipper): refactor klipper_setup to reduce cognitive complexity
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-27 23:49:34 +01:00
dw-0
83e5d9c0d5 refactor(Klipper/Moonraker): remove obsolete method calls
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-27 15:58:37 +01:00
dw-0
8f44187568 feat(Moonraker): enable Mainsail remote mode after multi instance setup
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-27 15:42:46 +01:00
dw-0
625a808484 fix(InstanceManager): return an updated list when getting the instances property
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-27 14:09:51 +01:00
dw-0
ad0dbf63b8 refactor(Mainsail): enable remote mode if moonraker multi instance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-27 00:11:11 +01:00
dw-0
9dedf38079 refactor(KIAUH): big refactor of instance handling
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-26 23:37:35 +01:00
dw-0
1b4c76d080 fix(KIAUH): more file path handling improvements
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:31:18 +01:00
dw-0
d20d82aeac fix(Mainsail): proper check if config exists
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:06:40 +01:00
dw-0
16a28ffda0 fix(Klipper/Moonraker): config files now always have a Path, are never None anymore
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:03:44 +01:00
dw-0
a9367cc064 fix(Klipper): remove obsolete method parameter
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 22:01:18 +01:00
dw-0
b165d88855 fix(Moonraker): missing return statement if all requirements met
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 20:57:22 +01:00
dw-0
6c59d58193 refactor(KIAUH): use red dash instead of "Unknown" if repo info not available
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 20:44:04 +01:00
dw-0
b4f5c3c1ac refactor(Mainsail): remove mainsail.zip after extracting content
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 20:16:38 +01:00
dw-0
b69ecbc9b5 fix(KIAUH): wrong logic in status detection
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 19:56:43 +01:00
dw-0
fc9fa39eee refactor(Mainsail): use same wording in MainsailRemoveMenu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 19:43:30 +01:00
dw-0
142b4498a3 refactor(Klipper): rework remove process
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 19:39:19 +01:00
dw-0
012b6c4bb7 refactor(Moonraker): rework remove process
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 15:12:08 +01:00
dw-0
8aeb01aca0 refactor(KIAUH): use pathlib instead of os where possible. consistent use of pathlib.
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-25 01:16:16 +01:00
dw-0
da533fdd67 refactor(KIAUH): use util functions for Klipper and Moonraker to get their status
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-24 14:35:25 +01:00
dw-0
8cb0754296 feat(KIAUH): show Mainsail install status
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-24 14:23:57 +01:00
dw-0
7a6590e86a refactor(Mainsail): rework config.json backup
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-24 13:53:55 +01:00
dw-0
2f0feb317e refactor(BackupManager): rework backup structure and implement single file backup method
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-24 12:58:41 +01:00
dw-0
b9479db766 feat(KIAUH): show installation status of Klipper and Moonraker in MainMenu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-23 19:59:39 +01:00
dw-0
14132fc34b refactor(ConfigManager): automatically read config upon ConfigManager instance init
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-23 19:36:03 +01:00
dw-0
3d5e83d5ab refactor(Mainsail): remove specific methods and replace by generic ones
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-22 22:54:29 +01:00
dw-0
edd5f5c6fd refactor(KIAUH): refactor RemoveMenu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-22 22:43:29 +01:00
dw-0
8ff0b9d81d refactor(Mainsail): refactor methods for removing and checking files
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-22 22:39:43 +01:00
dw-0
22e8e314db fix(Mainsail): implement missing mainsail cfg symlinking
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-22 21:41:15 +01:00
dw-0
12bd8eb799 feat(KIAUH): move filesystem related methods to own module
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-22 21:25:20 +01:00
dw-0
4915896099 feat(Mainsail): remove Mainsail
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-21 22:53:41 +01:00
dw-0
cd38970bbd refactor(Mainsail): move some functions to a mainsail utils module
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-20 22:35:29 +01:00
dw-0
b8640f45a6 refactor(Klipper): refactor example printer.cfg creation
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 23:30:38 +01:00
dw-0
5fb4444f03 refactor(Moonraker): refactor example moonraker.conf creation
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 23:30:15 +01:00
dw-0
926ba1acb4 feat(ConfigManager): implement own ConfigParser write() method
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 21:49:09 +01:00
dw-0
c2e7ee98df feat(Mainsail): implement Mainsail installer
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 18:08:18 +01:00
dw-0
3865266da1 refactor(RepoManager): default to master branch if none is provided
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 18:07:18 +01:00
dw-0
b83f642a13 refactor(ConfigManager): logging can be silenced
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 18:03:42 +01:00
dw-0
30b4414469 feat(Klipper): create example printer.cfg if wanted
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 18:00:16 +01:00
dw-0
1178d3c730 refactor(Moonraker): skip selection dialog if there is only 1 klipper instance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 16:18:44 +01:00
dw-0
59d8867c8c fix(kiauh): copy&paste issue in repo url for Moonraker
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 16:12:43 +01:00
dw-0
80a953a587 fix(Moonraker): typo in python version check
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 16:10:20 +01:00
dw-0
a80f0bb0e8 feat(utils): add several util methods
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 14:42:53 +01:00
dw-0
78cefddb2e feat(InstanceManager): add restart service method
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 13:26:22 +01:00
dw-0
b20613819e feat(Logger): add "start" parameter
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 13:00:46 +01:00
dw-0
5ebe941125 readme: don't recommend a specific architecture anymore
Fixes #403 

Both 32bit and 64bit images were reported to work fine. So we don't need a strict 32bit recommendation anymore which may confuses users, why they should pick 32bit over 64bit.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 12:18:04 +01:00
dw-0
f5eb9486cc refactor(kiauh): use sudo true instead of sudo -v to prevent password prompts
Fixes #395 

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 12:10:36 +01:00
dw-0
1836beab42 feat(klipper): add getter for specific properties
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-17 00:01:25 +01:00
dw-0
545397f598 feat(kiauh): fix typo in check_package_install
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-16 17:38:01 +01:00
dw-0
f709cf84e7 feat(kiauh): add helper methods for downloading files
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-16 15:41:48 +01:00
dw-0
f62c10dc8b feat(kiauh): add helper methods to check for installed packages
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-16 15:38:23 +01:00
Stefan Dej
7a9e752f9c fix: fix typo in example.printer.cfg (#412)
rename virtual_sd_card to virtual_sdcard

Signed-off-by: Stefan Dej <meteyou@gmail.com>
2023-12-12 08:21:51 +01:00
CODeRUS
30bc56b198 feat(advanced): add selection between flash/serialflash methods (#410) 2023-12-10 16:57:59 +01:00
dw-0
b2567995de feat(klipper): add virtual_sd_card_block to example printer.cfg (#411) 2023-12-10 15:12:28 +01:00
dw-0
e121ba8a62 feat(Moonraker): add python version check
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-04 22:35:36 +01:00
dw-0
9a1a66aa64 docs(utils): add docstrings
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-04 21:35:28 +01:00
dw-0
420b193f4b feat(Moonraker): implement Moonraker
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-03 23:13:28 +01:00
dw-0
de20f0c412 refactor(ConfigManager): allow to take in any config file
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-03 23:10:43 +01:00
dw-0
57f34b07c6 refactor(utils): add more util functions
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-03 23:09:43 +01:00
dw-0
e35e44a76a refactor(kiauh): move create_folders to BaseInstance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-03 23:08:41 +01:00
dw-0
bfb10c742b refactor(kiauh): reword print_info to print_status and implement new print_info method
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-03 23:06:30 +01:00
dw-0
458c89a78a fix(InstanceManager): print service name instead of suffix only
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-12-02 16:52:30 +01:00
dw-0
6128e35d45 refactor(kiauh): rework menu formatting logic
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-15 22:58:54 +01:00
dw-0
279d000bb0 refactor(kiauh): specify python3 in shebang
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-14 21:31:25 +01:00
dw-0
a4a3d5eecb feat(BackupManager): implement simple backup manager
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-14 21:28:13 +01:00
dw-0
1392ca9f82 refactor(klipper): pass the script path as a Path to the parse function
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-13 20:07:21 +01:00
dw-0
47121f6875 refactor(utils): clean up, add comments
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-13 20:06:48 +01:00
dw-0
d0d2404132 refactor(kiauh): move core modules to core package
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 23:28:05 +01:00
dw-0
6ed5395f17 feat(klipper): check for brltty-udev too
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 01:26:43 +01:00
dw-0
be805c169b feat(klipper): allow keeping klipper and klipper-env dir during uninstall
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 01:13:39 +01:00
dw-0
eaf12db27e fix(klipper): allow go back when asked for new instances
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 00:39:10 +01:00
dw-0
fe8767113b refactor(klipper): rework dialogs
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 00:37:25 +01:00
dw-0
2148d95cf4 fix(InstanceManager): return None for suffix if there is none
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 00:10:52 +01:00
dw-0
682be48e8d fix(InstanceManager): instance_service should be service file name
remove debug prints

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-12 00:01:21 +01:00
dw-0
68369753fd refactor(InstanceManager): rework
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-11 23:56:27 +01:00
dw-0
44ed3b6ddf feat(kiauh): add .iml to gitignore
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-11 23:53:42 +01:00
dw-0
e12e578098 refactor(klipper): rewrite dialogs
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-05 23:13:16 +01:00
dw-0
515a42f098 feat(klipper): implement update function
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-05 16:15:19 +01:00
dw-0
f9ecad0eca refactor(klipper): use name "klipper" for single instance setup
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-05 16:12:17 +01:00
dw-0
fb09acf660 refactor(utils): reduce complexity
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-02 22:52:34 +01:00
dw-0
093da73dd1 refactor(klipper): use constants for commonly used strings
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-11-02 22:37:17 +01:00
dw-0
c9e8c4807e feat(klipper): convert single to multi instance
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-31 20:54:44 +01:00
dw-0
6fcd7a3f08 refactor(klipper): improve disruptive package handling
fix #293

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-31 15:09:31 +01:00
dw-0
09e874214b feat(ConfigManager): implement ConfigManager
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-29 17:19:26 +01:00
dw-0
623bd7553b feat(RepoManager): implement RepoManager
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-29 13:25:21 +01:00
dw-0
1e0c74b549 style: rename input functions
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-29 01:29:27 +02:00
dw-0
358c666da9 feat(style): use black code style / formatter
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-29 00:31:34 +02:00
dw-0
84a530be7d fix(klipper): handle disruptive system packages/services
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-28 23:52:51 +02:00
dw-0
bfff3019cb fix(InstanceManager): fix TypeError if instance name is None
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-28 22:10:07 +02:00
dw-0
2a100c2934 feat(klipper): check for required user-groups
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-28 21:31:31 +02:00
dw-0
25dfbb83df fix(kiauh): display custom repo dialog again after user interaction
fix #394

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-28 20:48:57 +02:00
dw-0
ce0daa52ae feat(klipper): implement instance manager and klipper installer in python
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-10-26 13:58:31 +02:00
Sineos
899b204dc7 refactor: show full path of mcu (#391) 2023-10-15 10:08:41 +02:00
Kenneth Jiang
5cf4b018fc refactor: change wording now that moonraker-obico can link printer using auto-discovery (#389) 2023-10-12 20:26:30 +02:00
dw-0
ae9d1b98da feat(klipper): remove/disable brltty and ModemManager during installation (#387) 2023-10-01 18:30:01 +02:00
Kenneth Jiang
16d3388ff2 refactor: make the 'Obico for Klipper' name consistent (#385) 2023-09-29 20:20:21 +02:00
th33xitus
b88d0085ba fix(backups): fix broken config target folder name
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-09-04 21:17:10 +02:00
th33xitus
0b6613e464 fix(backups): fix broken moonraker database target folder name
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-09-04 21:02:22 +02:00
th33xitus
d99cda544a fix(backups): add backup before update function for KlipperScreen
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-08-30 19:48:55 +02:00
th33xitus
a50dce20de fix(backups): add backup function for OctoEverywhere
fix #380

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-08-30 19:48:53 +02:00
th33xitus
f45da66e9e fix(crowsnest): uninstaller exited KIAUH if crowsnest not installed
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-08-26 18:31:37 +02:00
th33xitus
2822499344 refactor(copyright): update copyright comment
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-08-26 18:11:00 +02:00
th33xitus
c777ba3e6b refactor(log-upload): update log upload functions and re-enable access
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-08-26 18:06:14 +02:00
th33xitus
9f410450d7 refactor(backups): update backup functions for config and moonraker database and enable backup-before-update again
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-08-23 23:22:23 +02:00
dw-0
0497d49066 Update README.md 2023-08-15 16:42:38 +02:00
dw-0
229da227b0 Update FUNDING.yml
use updated ko-fi username
2023-08-14 23:06:44 +02:00
th33xitus
65854c8da6 fix(updates): make update all function also update system again
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-07-22 23:24:29 +02:00
Kenneth Jiang
5985646633 refactor(obico): move dependency handling to obico's own install script (#361) 2023-07-10 17:44:05 +02:00
th33xitus
979c39dc02 refactor(mainsail/fluidd): allow reading of version from release_info.json
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-07-01 20:59:46 +02:00
th33xitus
197058bd00 changelog: update Changelog
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-06-17 21:19:49 +02:00
th33xitus
d3b5122ebb refactor(UI): move version and added changelog link to own row in main menu
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-06-17 21:11:23 +02:00
th33xitus
8ce4daf403 refactor(klipper): pre-select python3 for klipper install
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-06-17 19:51:41 +02:00
th33xitus
b0a65fe14e refactor(UI): remove warn message
more than half a year is gone, the warning should not be that important anymore

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-06-17 19:49:57 +02:00
Patrick Schmidt
98866caefa feat: add Mobileraker (#343)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2023-06-17 19:34:26 +02:00
phizev
345b7b66a3 refactor: use service specific directories in templates (#355) 2023-06-17 18:22:16 +02:00
cravl-dev
8eb2924832 refactor: update package lists only when stale (#346)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2023-06-17 18:08:24 +02:00
Kyriel Abad
5d7debd65e readme: fix typos in README.md (#352) 2023-06-10 23:00:23 +02:00
th33xitus
7df3dd489f fix(mainsail/fluidd): show correct version number in update menu
fixes #350

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-06-07 21:48:17 +02:00
marbocub
0cd058320f feat: allow to install Mainsail/Fluidd without Moonraker (#347)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2023-06-06 21:09:47 +02:00
th33xitus
bcbb185bd7 readme: update README.md
also fixes #327
2023-06-04 21:57:56 +02:00
CODeRUS
477f3ca72c feat: flash DFU device in HID mode (#337)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2023-06-04 21:23:32 +02:00
th33xitus
c19acb1694 fix(mainsail/fluidd): fall back to latest stable url if fetching tags fails (#348) 2023-06-04 19:12:53 +02:00
Jookia
8228943850 fix(klipper): if set, use custom branch to check for update (#332) 2023-04-16 08:56:18 +02:00
Stefan Dej
5b890fb0fb refactor(mainsail): update themes.csv url for mainsail themes (#329) 2023-04-13 17:59:14 +02:00
Piotr Banasik
7989cec8d4 fix(crowsnest): override installers BASE_USER to current user (#317) 2023-03-19 11:50:06 +01:00
th33xitus
858301aa9a fix: temporary disable backup_before_update
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-03-18 18:15:11 +01:00
Quinn Damerell
ae9e79c579 feat(octoeverywhere): add OE to the "update all" action (#311) 2023-03-11 23:52:46 +01:00
th33xitus
1215446a6c feat(octoeverywhere): implement update function (#310) 2023-03-11 20:25:17 +01:00
th33xitus
8526acf8b6 chore: update copyright notice
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-03-10 20:06:06 +01:00
th33xitus
cc27aaec7c readme: add prerequisites to readme (#309) 2023-03-08 20:07:00 +01:00
Quinn Damerell
1e9493461c feat: add OctoEverywhere for Klipper (#300)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2023-03-05 17:19:20 +01:00
th33xitus
31616ebad5 fix(crowsnest): silence grep error output in main menu
fixes #308

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-03-04 16:01:06 +01:00
Stephan Wendel
faf56ed1b1 refactor(crowsnest): improve performance in crowsnest.sh (#306) 2023-02-28 18:37:43 +01:00
th33xitus
d6837af2a2 refactor(klipper): implement blacklisted service names (#304) 2023-02-28 18:29:21 +01:00
Pedro Lamas
afe6f7499a feat(fluidd): use fluidd-config repo for downloading fluidd macros (#305) 2023-02-28 16:33:32 +01:00
Pedro Lamas
e3ed223b5c fix: always use system home directory (#303) 2023-02-28 15:30:42 +01:00
th33xitus
fd27db28d4 feat(mainsail): use mainsail-config repo for downloading mainsail macros (#301) 2023-02-26 16:29:01 +01:00
Michael Bravo
68a02ad3f5 fix(mainsail): set increased read timeout on API endpoint (#294) 2023-02-25 12:59:31 +01:00
CODeRUS
99b7672dc9 fix(telegram bot): Fix service restart (#296) 2023-02-14 19:40:11 +01:00
th33xitus
bb3ec79756 revert(mjpg-streamer): re-add uninstall option for mjpg-streamer
fix #291

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-02-05 08:57:15 +01:00
Kenneth Jiang
ce595abd60 refactor(obico): remove "-s" option from moonraker-obico/install.sh invocation (#292) 2023-02-05 08:50:06 +01:00
th33xitus
c79dc280e3 refactor(moonraker): update cors domains in moonraker.conf template
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-02-05 08:47:46 +01:00
Stephan Wendel
7aa186e8b9 feat: replace mjpg-streamer with crowsnest (#286)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2023-02-03 21:59:59 +01:00
th33xitus
8493269c6f refactor: hide currently still disabled functions from the menus
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2023-01-08 11:05:14 +01:00
th33xitus
150ef0142f refactor(mainsail/fluidd): move tag urls from globals to respective files
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-12-14 19:40:12 +01:00
th33xitus
f70faa52cc fix(klipper): update python dialog description
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-12-08 19:25:58 +01:00
CODeRUS
e796f74640 refactor(klipper): recommend Python3 for Klipper installation (#246) 2022-12-07 18:42:51 +01:00
Thomas Lété
2c9f5bed60 fix: pull mainsail.cfg from correct location (#272) 2022-12-07 17:34:01 +01:00
th33xitus
e9c23ca93e fix(klipper): use correct value for py_ver variable in update function
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-11-14 19:50:55 +01:00
th33xitus
67afa26ed7 refactor(klipper): full refactor of klipper install/remove part (#264) 2022-11-13 15:51:22 +01:00
th33xitus
54be7e4e21 refactor(obico): update obico installer (#254) 2022-10-31 16:06:45 +01:00
th33xitus
811c071b74 refactor(telegram-bot): update telegram-bot installer (#251) 2022-10-30 18:30:20 +01:00
th33xitus
6116fc92cf fix: allow for any amount of whitespaces after config section in update manager
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-10-30 18:27:13 +01:00
th33xitus
5524a40f04 refactor(klipperscreen): re-enable klipperscreen installer
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-10-25 15:24:25 +02:00
th33xitus
cb3661b8b5 refactor(prettygcode): re-enable pretty gcode installer
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-10-25 15:23:57 +02:00
th33xitus
2cec90b29c refactor(octoprint): update octoprint installer
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-10-24 10:35:30 +02:00
th33xitus
d2c009df9a fix(klipper): network.target is now network-online.target
Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-10-22 23:28:01 +02:00
th33xitus
046178f801 refactor!: update KIAUH for recent moonraker changes (#245) 2022-10-20 11:20:34 +02:00
cobyn
442980dbd0 fix(flash_klipper): spelling mistake (#242) 2022-10-05 17:22:31 +02:00
th33xitus
798e56f4dc refactor(mainsail): replace deprecated remote mode with new instanceDB
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-09-18 23:45:02 +02:00
th33xitus
9d90daec7f fix(switch_klipper_repo): use of case didn't allow selections of 2 and above
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-09-10 20:25:05 +02:00
megapro17
f25726cfed fix: typo in mjpg-streamer dialog (#239) 2022-09-08 09:15:24 +02:00
th33xitus
f46b099b74 refactor(klipper): more verbose error message if SysVinit script is detected
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-09-03 14:14:37 +02:00
th33xitus
03be46f012 refactor(moonraker): skip polkit script execution if polkit rules already installed
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-09-03 13:41:02 +02:00
th33xitus
c11e628c55 fix(moonraker): do not exit if moonraker_polkit exits with an error during moonraker update
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-09-03 11:34:45 +02:00
th33xitus
4c8d43e365 refactor: use OS independent method to check/set for correct home folder permissions
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-08-27 22:08:01 +02:00
Kenneth Jiang
9d7144b493 feat: add Obico for Klipper to KIAUH (#227)
Co-authored-by: th33xitus <th33xitus@googlemail.com>
2022-08-15 19:44:04 +02:00
th33xitus
6df388f42b refactor: install webcamd from self provided resources
with the upcoming release of a new MainsailOS image, mjpg-streamer is replaced by crowsnest. it is therefore not possible anymore to pull the files from the mainsailOS repository. hence adding them to the KIAUH repository.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-29 19:46:39 +02:00
th33xitus
1d7fb010af refactor: remove unused kiauh_macros.cfg
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-24 08:51:48 +02:00
th33xitus
d4207d710c fix: restart moonraker after moonraker.conf patch (fixes #225)
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-22 10:16:00 +02:00
th33xitus
6cb8d70b63 fix: replace generic nginx webui cfg by their respective ones
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-22 10:06:35 +02:00
th33xitus
ae011963da fix: install mainsail theme to correct folder (fixes #222)
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-20 22:41:42 +02:00
th33xitus
491d6f40bb refactor: move ini initialization to main_menu()
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-20 22:34:07 +02:00
th33xitus
8bbe2f79ea feat: save multi instance klipper names/identifier to kiauh.ini
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2022-07-20 22:33:33 +02:00
th33xitus
0bdf61a714 fix: do not install py2 packages if venv is py3 (fixes #211)
Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-06-30 20:41:36 +02:00
th33xitus
b07a83c8ad fix: update all function (#204) (#217)
* fix: update all function (fixes #204)
* fix: replace non-existing `print_unkown_cmd` function

Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-06-30 11:08:09 +02:00
th33xitus
39e22acbed fix(octoprint.sh): script exiting without error message (fixes #216)
- the script was exiting without notifying the user that klipper has to be installed first

Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-06-26 08:48:38 +02:00
th33xitus
8ba46fa4ac fix(pretty_gcode.sh): use main instead of master (fixes #203)
- pgc uses main instead of master. so checking for origin/master leads to an error

Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-05-31 17:27:52 +02:00
th33xitus
d6b95c9d10 refactor(nginx.sh): refactor set_nginx_cfg()
- implements two dedicated nginx configs for mainsail and fluidd

Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-05-30 18:38:45 +02:00
th33xitus
f3a769e03e README.md: reworded a feature description
Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-05-29 22:02:03 +02:00
th33xitus
646e5acd3a README.md: update README.md
Signed-off-by: Dominik Willner th33xitus@gmail.com
2022-05-29 20:35:07 +02:00
th33xitus
fcf059df73 feat: KIAUH v4.0.0 (#191) 2022-05-29 20:11:15 +02:00
th33xitus
4bf9e8f0a8 fix(moonraker.conf): typos in cors domains 2022-05-10 20:15:50 +02:00
th33xitus
dd58229fee fix: bug in remove_klipperscreen() 2022-03-06 11:56:31 +01:00
Alex Zellner
6c4635fa4e fix(gcode_shell_command): py3 virtual env compatibility (#175) 2022-02-26 19:28:06 +01:00
th33xitus
4517415e9d fix: CRLF to LF 2022-02-26 19:17:22 +01:00
th33xitus
5c45bc7617 readme: fix link to user profile 2022-02-26 17:46:47 +01:00
th33xitus
d8ce465126 readme: add shields to readme 2022-02-26 17:44:59 +01:00
th33xitus
2f8c95a8c7 readme: update readme 2022-02-26 17:24:02 +01:00
th33xitus
4c083ceade fix: broken set_klipper_cfg_path function (#163) 2022-02-26 16:51:43 +01:00
th33xitus
259a6919f0 fix: link for theme data csv 2022-02-10 09:14:44 +01:00
th33xitus
4f7a49d85a fix: bug not updating all when selecting to do so.
* also make the confirm message more verbose and make it clear that KIAUH won't update any held back packages or any dist upgrades.
2022-02-07 14:47:36 +01:00
th33xitus
005a5061a7 chore: apply changes made in mainsailOS 0.6.1 2022-02-03 16:57:55 +01:00
th33xitus
634d795557 fix: bug not successfully copying new upstreams.conf and common_vars.conf 2022-01-31 12:00:57 +01:00
th33xitus
a14e321df9 fix: apply required changes due to introduction of PolicyKit and PackageKit in Moonraker 2022-01-29 23:06:02 +01:00
th33xitus
1682642e47 fix: revert 7e9d18b
Due to some investigations made in #158, the default IPv6 configuration will be removed again
2022-01-11 08:51:32 +01:00
th33xitus
7e9d18b54c script: add webui ipv6 listen directive (fixes #158) 2022-01-08 22:16:38 +01:00
th33xitus
d049d4c770 script: remove unused functions 2022-01-08 21:48:38 +01:00
th33xitus
108cda3cd6 shellcheck: apply fixes for SC2086 2022-01-08 21:45:55 +01:00
th33xitus
d7de58f538 feat: check for apache2 and prompt for action 2022-01-08 20:57:47 +01:00
th33xitus
572afa0396 fix: typo in bug_report.yml 2021-12-30 19:52:22 +01:00
th33xitus
63a5e1e323 update bug_report.yml 2021-12-30 19:49:14 +01:00
th33xitus
8c68eaa995 doc: update changelog.md 2021-12-30 19:26:28 +01:00
th33xitus
e8c0b3cf39 fix: formatting / add INFO prefix 2021-12-26 12:18:45 +01:00
th33xitus
cfad7a1fb0 fix: add check for membership of group 'tty'
It's possible that being part of group 'tty' is also needed to be able to flash an MCU
2021-12-25 00:52:08 +01:00
th33xitus
4113732daa fix: check if group dialout exists
first check if group dialout exists on the system. then check if the current user is not part of it. only then ask for adding the user to that group
2021-12-24 23:33:05 +01:00
th33xitus
95808a0d5b shellcheck: apply several fixes
apply fix for SC2219, SC2086, SC2236
2021-12-24 23:02:38 +01:00
th33xitus
e551c02507 chore: remove unused code 2021-12-24 22:52:21 +01:00
th33xitus
1f40686ea1 fix: improve moonraker service detection (fixes #156) 2021-12-24 22:50:50 +01:00
th33xitus
9b3d96545b refactor: improve error message 2021-12-24 16:22:46 +01:00
th33xitus
a632fae8f6 fix: check if user is in group "dialout" before starting flash or build+flash routine 2021-12-24 16:17:07 +01:00
th33xitus
4e3a701db4 fix: warn_msg should be status_msg 2021-12-24 14:49:23 +01:00
th33xitus
0c760b5aa2 fix: check membership of "video" usergroup (should fix #135) 2021-12-24 14:36:30 +01:00
th33xitus
3bc2f3b498 doc: document passing parameters to gcode_shell_commands 2021-12-24 01:08:09 +01:00
th33xitus
b92cfc3984 fix: klipper_cfg_loc was an empty string
if the config location wasn't already set before installing mjpg-streamer, klipper_cfg_loc was empty, even after calling the check_klipper_cfg_path function beforehand

moonraker.conf: set refresh_interval to 24hrs
2021-12-24 00:22:15 +01:00
th33xitus
01790b5c11 moonraker.conf: pre-configure clients for pre-release updates
moonraker.conf: set refresh_interval to 24hrs
2021-12-23 17:17:48 +01:00
th33xitus
8c7891e360 fix: libjpeg dependencies for ubuntu
chore: several shellcheck fixes
2021-12-23 16:50:48 +01:00
th33xitus
852f7c056a fix: wrong logic in if condition 2021-12-23 16:45:06 +01:00
th33xitus
8cffd07aef fix: klipper service detection
refactor: move $INSTANCE_COUNT into setup function

refactor: creating $PKGLIST

script: add short description to the dialog for selecting the amount of klipper instances

chore: several shellcheck fixes
2021-12-23 15:03:52 +01:00
th33xitus
40745e90df refactor: move $INSTANCE_COUNT into setup function 2021-12-23 14:19:06 +01:00
th33xitus
a43645cca0 refactor: better klipper service detection 2021-12-23 10:35:18 +01:00
th33xitus
4d834db5df refactor: unlink moonraker install from klipper instances
chore: several shellcheck fixes
2021-12-23 10:35:09 +01:00
th33xitus
771191ab69 chore: update .gitignore 2021-12-22 22:39:17 +01:00
th33xitus
7afe943ecc shellcheck: apply fix for SC2086 2021-12-19 17:14:24 +01:00
th33xitus
b06c17c184 fix: confirm flash condition (fixes #155) 2021-12-19 17:05:41 +01:00
Pedro Lamas
3af46b45ee github: adds GitHub issue templates (#150)
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
2021-12-05 14:37:08 +01:00
th33xitus
b58e79634c fix: update URLs 2021-11-29 11:13:47 +01:00
Yoshito Komatsu
1ef9b0f58f fix: use [[:digit:]] instead of \d (#146) 2021-11-29 11:09:24 +01:00
th33xitus
8333ae1dc4 fix: also get pre-release versions
previous endpoint didn't allow kiauh to get pre-release versions of mainsail/fluidd
2021-11-20 15:17:21 +01:00
th33xitus
e0ae312a9e fix: missing declaration for OctoPrint backup (fixes #143) 2021-11-17 18:30:33 +01:00
th33xitus
4ce1ce72d3 chore: update moonraker.conf template 2021-11-14 11:29:14 +01:00
th33xitus
60842a330d chore: update webui links 2021-11-14 11:21:34 +01:00
th33xitus
05a59e9261 fix: correct ls command to find kiauh_macros.cfg 2021-11-11 14:15:00 +01:00
th33xitus
36a8757cfd chore: use ls to look for webcamd installation 2021-11-11 13:26:01 +01:00
th33xitus
fe4625d3e1 chore: refactor how moonraker is detected 2021-11-11 13:21:57 +01:00
th33xitus
19ddf3e023 fix: silence grep 2021-11-11 13:02:36 +01:00
th33xitus
ba888b1f97 chore: use ls within remove_nginx method 2021-11-11 12:51:09 +01:00
th33xitus
0284a36e7f chore: use ls to look for nginx installation 2021-11-11 12:43:13 +01:00
th33xitus
22f705e06c chore: manage moonraker service enable/start with do_action_service method 2021-11-11 12:28:20 +01:00
th33xitus
4c34245da0 chore: refactor how klipper services are count 2021-11-11 12:12:43 +01:00
th33xitus
f7cb3d6c97 script: refactor exit code evaluation in flash_mcu.sh 2021-10-31 13:53:22 +01:00
th33xitus
aaf4f7dd5c script: add logrotate rule for webcam service (#127) 2021-10-31 11:23:48 +01:00
Noah Pan
26bac791aa service: remove webcamd service restart interval. (#132) 2021-10-31 10:52:22 +01:00
th33xitus
aa4bdfc7b2 fix: invalid command bug in rollback UI (fixes #131) 2021-10-29 14:04:39 +02:00
th33xitus
311f3be864 fix: wrong check for moonraker multi-instance
The previous command also counted an available `moonraker-telegram-bot.service` and caused further issues with enabling the mainsail remoteMode accidentially.
2021-10-26 20:55:31 +02:00
th33xitus
511df1a889 fix: use correct themes.csv url 2021-10-25 22:22:32 +02:00
th33xitus
8d3ddc273a fix: allow non-single digit instance counts containing a 0 #129 2021-10-22 23:52:58 +02:00
th33xitus
f231fa9c69 gcode_shell_command: make RUN_SHELL_COMMAND accept optional parameters
Thanks @lixxbox for that code
2021-10-17 20:13:48 +02:00
th33xitus
9b6925e9c4 fix: add missing function call in install_webui 2021-10-02 11:26:43 +02:00
th33xitus
7f8ee7939c script: move nginx functions from install_moonraker.sh to install_klipper_webui.sh
nginx was removed as a dependency from moonraker. moonraker didn't make use of it, only mainsail/fluidd do. so moving all nginx related functions to  is reasonable
2021-10-01 23:58:03 +02:00
th33xitus
4d4c49d4c9 script: only display dialogs if files/service not found 2021-10-01 22:53:32 +02:00
th33xitus
7692227946 fix: function setup_moonraker_nginx_cfg 2021-10-01 21:51:33 +02:00
th33xitus
3da993a67c fix: breaking bug 2021-09-29 18:55:13 +02:00
th33xitus
6b74c59d15 script: add function to match nginx configurations
this should fix a common issue where mainsail/fluidd was installed with a more up2date nginx config which didn't match with a previously configured upstreams.conf.
this should also cover cases where the upstreams.conf is more up2date than the corresponding interface configs.
2021-09-29 14:44:01 +02:00
th33xitus
9cd27f7052 fix: backup nginx configs now works correctly 2021-09-28 18:22:50 +02:00
th33xitus
fc4fe130cd script: read webui ports from nginx config, write them to kiauh.ini 2021-09-28 18:22:50 +02:00
th33xitus
2a46b00cda Updating README.md / changelog.md 2021-09-28 17:25:40 +02:00
th33xitus
560186a40b chore: little UI tweaks, renaming "MTelegramBot" to "Telegram Bot" 2021-09-28 17:24:52 +02:00
th33xitus
75bca847f8 fix: remove telegram bot log also from ~/klipper_logs upon removal 2021-09-28 16:56:17 +02:00
Evgenii Shavrin
bb1f2eadca feat: add moonraker-telegram-bot by nlef (#117)
Adds installation and removing feature for https://github.com/nlef/moonraker-telegram-bot
2021-09-28 16:52:59 +02:00
Richard Mitchell
6d87716b1d gcode_shell_command: Fix issue with partial lines being repeated (#118)
Without clearing the partial line on a full line being sent the self.partial_output was repeated for every subsequent line.
2021-09-27 17:44:11 +02:00
th33xitus
1e8c379623 Create FUNDING.yml 2021-09-24 12:21:30 +02:00
th33xitus
6a8991d51e Update changelog.md 2021-09-24 12:19:41 +02:00
th33xitus
fb4367bb41 script: refactoring of the flash routine
script: uart devices can be detected/selected now

script: first implementation of a little help page
2021-09-24 11:59:50 +02:00
th33xitus
9463b719e4 script: update klipper github repository URL 2021-09-24 08:31:11 +02:00
th33xitus
65bf3d5251 fix: fix of previous commit and small refactor 2021-09-12 20:46:39 +02:00
AKA "Thom Wiley
68327262fc chore: change submenu "quit" to "back"
Changing the "Quit" option to "Back" as it is more intuitive, causes less confusion and is in fact the more fitting description of what is happening.
2021-09-12 20:41:27 +02:00
th33xitus
14ef39b87c fix: typo and missing function call for PrettyGCode 2021-08-25 10:14:05 +02:00
th33xitus
969d3b5dab script: add --allow-releaseinfo-change option to apt-get update 2021-08-25 10:05:31 +02:00
th33xitus
05842f8e1d feat: add PrettyGCode for Klipper 2021-08-10 12:25:05 +02:00
th33xitus
39219c105e script: add additional dependency check - should fix #97
Before starting the firmware building process, check for dependencies again.
2021-07-25 18:42:22 +02:00
th33xitus
7984c28fe5 script: add more default trusted_clients to moonraker.conf template
the previous method of adding the host network range was too unreliable in some cases and didn't cover some popular usecases
2021-07-11 19:13:37 +02:00
th33xitus
b44e855a98 script: simplify python dependency installation during klipper and moonraker update 2021-07-11 14:45:58 +02:00
th33xitus
52ab909ba5 script: update moonraker.conf, apply recent changes to the update manager section 2021-07-10 09:27:13 +02:00
th33xitus
4d7e10e5c3 script: update nginx configurations 2021-07-10 09:22:43 +02:00
th33xitus
d3726733e5 fix: small flash routine rework + user input validation 2021-07-04 20:09:25 +02:00
th33xitus
765f016ea2 fix: menu action for removing mainsail theme was missing 2021-07-01 21:38:14 +02:00
th33xitus
5deb987b8a script: patch log_path to moonraker.conf if missing 2021-06-29 22:58:21 +02:00
th33xitus
47d1321979 script: mainsail theme installer overhaul 2021-06-22 17:12:38 +02:00
th33xitus
b47a9cf7ed feat: add voron toolhead theme to mainsail theme installer 2021-06-22 12:07:27 +02:00
th33xitus
a9f23e9b23 script: remove else statement from do_action_service
Caused spamming of error messages when starting from a complete new Pi image.
2021-06-21 20:43:01 +02:00
th33xitus
814acbe92a script: add virtualenv to the moonraker dependency check #95 2021-06-21 08:44:37 +02:00
th33xitus
991dd79d01 script: add simple check for DietPi distro when creating the klipper dependency array 2021-06-18 18:28:27 +02:00
th33xitus
40875dfe49 script: simplify service action methods 2021-06-18 18:03:47 +02:00
th33xitus
806c6fd275 Update changelog.md 2021-06-15 17:36:08 +02:00
th33xitus
e9706b52d8 Update changelog.md 2021-06-15 17:22:39 +02:00
th33xitus
412e084d6d script: add short changelog url to kiauh update message 2021-06-15 16:40:10 +02:00
th33xitus
3ebee823ad script: remove KlipperScreen.log symlink dialog. 2021-06-15 16:12:58 +02:00
th33xitus
72312422e3 fix: performance improvement for loading the advanced menu ui 2021-06-15 15:56:13 +02:00
th33xitus
54089582e4 script: rewrite the auto mainsailOS/fluiddPi migration function
No auto update anymore when updating Klipper. The user now must start this function. It's available in the Advanced Menu.
2021-06-15 15:56:13 +02:00
th33xitus
9fd3f930df script: advanced_menu.sh, switch version becomes switch branch 2021-06-15 15:56:13 +02:00
th33xitus
98f0aa4b8f script: update log-upload function 2021-06-15 15:56:13 +02:00
th33xitus
19b37e4dc4 script: update klipper.service raw file link 2021-06-15 15:56:13 +02:00
th33xitus
d344b1c5f6 feat: auto mainsailOS / fluiddPi migration
script: make use of moonrakers new log_path configuration option

script: create webui nginx log symlinks upon installation/update
2021-06-15 15:56:13 +02:00
jordanruthe
f804fcb65d update: Fix detached head on KlipperScreen update (#93) 2021-06-13 17:39:12 +02:00
th33xitus
0edfc746d4 script: improve dependency checks on klipper/moonraker update 2021-06-08 17:55:40 +02:00
th33xitus
02ef0578e3 script: reading klipper/moonraker dependencies from their respective install scripts 2021-06-08 17:54:23 +02:00
th33xitus
5d11cd212a script: update install_moonraker.sh (#89) 2021-06-04 14:01:54 +02:00
cron410
bf33c77db7 script: install_klipper.sh prompt phrasing (#90) 2021-06-04 13:55:05 +02:00
th33xitus
0815d7778c feat: add cyberpunk theme to mainsail theme installer 2021-05-25 18:53:07 +02:00
th33xitus
9a3814f480 fix: typo in SYSTEMDDIR variable 2021-05-25 18:49:45 +02:00
th33xitus
a83585fb06 fix: improve pattern matching 2021-05-25 18:39:37 +02:00
th33xitus
dc27fe47e1 Merge branch 'b3tuning-install-mjpg-steamer-fix' 2021-05-25 12:16:27 +02:00
th33xitus
11b3d7a961 fix: replace config dir in webcamd file 2021-05-25 12:15:01 +02:00
th33xitus
9e0cdb0715 fix: remove webcamd file when removing mjpgstreamer 2021-05-25 12:13:36 +02:00
th33xitus
8abda56749 fix: typo in WEBCAM_TXT_SRC 2021-05-25 12:04:13 +02:00
James Hildebrand
cf20fc3c48 Issue 85 fix 2021-05-24 16:57:21 -07:00
th33xitus
6c8845d7b4 script: show found services in error message 2021-05-23 11:38:32 +02:00
th33xitus
fb0a30814d fix: validate user input for amount of Klipper instances to install 2021-05-23 10:44:51 +02:00
th33xitus
87f229c62d Update README.md 2021-05-23 10:08:16 +02:00
th33xitus
5b1da45688 fix: typo in dialog of get_mcu_id 2021-05-23 09:58:34 +02:00
th33xitus
545b978e80 update kiauh_macros.cfg
Update macros to reflect recent changes to Klipper

f10247a498
2021-05-21 08:55:11 +02:00
th33xitus
44f5609de6 script: remove deprecated moonraker.conf entries 2021-04-20 21:53:13 +02:00
th33xitus
f2a26d9b3d fix: false positive error message during klipper setup
add fix some status.sh functions too
2021-04-15 13:03:36 +02:00
Evgenii Shavrin
bb2cdab02b script: better moonraker service detection (#79)
More accurate service search
2021-04-13 12:21:16 +02:00
th33xitus
d6596d0a3d script: change kiauh update notification to a dialog 2021-04-08 18:07:30 +02:00
th33xitus
865fbc07dc script: updating webui nginx config and moonraker.conf template 2021-04-05 08:59:19 +02:00
th33xitus
40ba33eb19 fix: fix a bug that potentially caused writing a wrong config directory path to the moonraker.service file under special conditions 2021-04-03 19:29:59 +02:00
th33xitus
49b77162b0 fix: typo 2021-03-31 10:06:11 +02:00
th33xitus
adf087e3e5 script: activate [history] in moonraker.conf template 2021-03-31 09:16:31 +02:00
th33xitus
8220647564 script: rework mjpg-streamer service setup 2021-03-28 17:00:25 +02:00
th33xitus
57075ff525 script: rework klipper service setup 2021-03-28 16:45:16 +02:00
th33xitus
2f9de620bc script: rework moonraker service setup 2021-03-28 15:51:50 +02:00
th33xitus
d658c3a4cd fix: update nginx config 2021-03-28 15:46:48 +02:00
th33xitus
67f6a2c599 fix: broken update menu 2021-03-27 00:04:25 +01:00
th33xitus
f369c132d2 fix #74 - remove leftovers 2021-03-26 21:48:12 +01:00
th33xitus
cce6ac3c88 feat: add system upgrade 2021-03-26 18:36:05 +01:00
th33xitus
b9fe29068d script: apt-get update before apt-get install in dependency_check() 2021-03-25 21:40:16 +01:00
th33xitus
104089ea3d feat: add mjpg-streamer installer 2021-03-25 21:35:24 +01:00
th33xitus
48d97dab01 feat: backup moonraker database 2021-03-24 11:19:50 +01:00
th33xitus
e4a56564a1 fix: command for removing .octoprint dirs while uninstalling OctoPrint 2021-03-24 11:04:43 +01:00
th33xitus
6de8ce3278 script: add database_path to moonraker.conf template 2021-03-22 22:04:27 +01:00
th33xitus
b46457328f feat: followup to 7c7dd4e 2021-03-22 13:34:52 +01:00
th33xitus
7c7dd4ec3c feat: add mainsail theme installer 2021-03-22 10:55:24 +01:00
th33xitus
749293dde0 fix: visual bug 2021-03-17 16:13:53 +01:00
th33xitus
85f8ab4f24 fix: typo 2021-03-17 16:11:56 +01:00
th33xitus
e894496a6e script: add [history] to moonraker.conf template 2021-03-14 21:30:03 +01:00
th33xitus
19b220b772 fix: #71 2021-03-14 21:15:02 +01:00
th33xitus
a4eaa8952a script: remove unnecessary cors domains from moonraker.conf template 2021-03-12 11:20:38 +01:00
th33xitus
74a9bf783b script: deactivate moonrakers debug logging in moonraker.conf template 2021-03-11 22:08:05 +01:00
th33xitus
7597e999a1 fix: add 'octoprint_compat' entry to moonraker.conf template to enable slicer uploads 2021-03-05 09:12:00 +01:00
th33xitus
64d73ca86a refactor: determine installed services, combine single and multi remove into one step 2021-02-19 11:09:46 +01:00
th33xitus
d3283f7ab7 fix: try to fix a bug where the script errors out, telling that there is already a moonraker service installed but actually there isn't 2021-02-19 11:05:43 +01:00
th33xitus
cb443f6fee fix: rework remove processes 2021-02-17 20:50:36 +01:00
th33xitus
7162256e3f fix: remove klipperscreen log from tmp and user defined config folder 2021-02-17 20:31:26 +01:00
th33xitus
938b9d24f5 feature: add sdcard updates 2021-02-06 14:56:57 +01:00
th33xitus
38a3517c89 fix: autocommit.sh example uses .version files for getting mainsail and fluidd version 2021-02-06 12:16:04 +01:00
th33xitus
0b15cd0d9b fix: delete doubled status_msg in gcode_shell_command extension setup 2021-02-06 12:16:04 +01:00
th33xitus
9d7752a6c5 fix: typo in example pause macro 2021-02-04 19:36:57 +01:00
th33xitus
d1dff4d3b5 fix: logic error in build+flash function 2021-02-01 20:48:05 +01:00
th33xitus
49b7fe395b fix: detect an existing "legacy" klipper init.d installation 2021-01-31 18:34:05 +01:00
th33xitus
51469d8992 Update screenshot for KIAUH v3.0 2021-01-31 16:28:43 +01:00
th33xitus
16e6dde998 publish KIAUH v3.0 2021-01-31 16:20:52 +01:00
th33xitus
431f58af4b fix: code cleanup 2021-01-31 16:16:44 +01:00
th33xitus
c3af82745b Update docs 2021-01-31 16:09:00 +01:00
th33xitus
26eecbc94b fix: refactor main menu, add dwc and octoprint commands 2021-01-31 15:59:50 +01:00
th33xitus
fa8a9ff7ba fix: don't use systemctl commands anymore for reading the status if a service is installed and how much instances. instead only count the service files in /etc/systemd/system. this significantly improves performance of the script on low power hardware like a pi zero 2021-01-31 14:04:23 +01:00
th33xitus
dc08d8e7e3 fix: should fix #65 2021-01-31 13:32:16 +01:00
th33xitus
d0b895a469 fix.: make warning more clear 2021-01-30 20:42:32 +01:00
th33xitus
5aaebc2bec fix: add new cors domain to allow other ports than 80 only too. 2021-01-30 19:34:41 +01:00
th33xitus
538c179671 fix: visual bug in the webui installer 2021-01-30 19:33:38 +01:00
th33xitus
dc6fea8c3b fix: enable mainsail remoteMode if its already installed before moonraker 2021-01-30 18:19:49 +01:00
th33xitus
82227f8d0b fix: bug while creating the kiauh_macros.cfg 2021-01-30 16:13:20 +01:00
th33xitus
31c3f344e5 fix: add dialog: ask user to remove moonraker after klipper 2021-01-30 15:54:28 +01:00
th33xitus
a7b7b362f3 fix: typo in moonraker instance count status 2021-01-30 15:43:59 +01:00
th33xitus
5f142da1b6 fix: activate remoteMode for mainsail when moonraker multi-instances are found 2021-01-30 14:57:38 +01:00
th33xitus
99209f97de fix: refactor webui installer 2021-01-30 14:19:51 +01:00
th33xitus
8a64fe495e fix: add inactive klipperscreen entry to moonraker.conf 2021-01-30 12:52:58 +01:00
th33xitus
c3468c04e4 fix: bug when creating the local network ip range for moonraker.conf 2021-01-30 10:15:24 +01:00
th33xitus
2ef3273b74 fix: make remove function now also remove disabled octoprint services 2021-01-29 20:23:55 +01:00
th33xitus
47cf96ef74 fix: more verbose error message 2021-01-29 20:12:53 +01:00
th33xitus
17524bf49b fix: lock changing config folder function until klipper was installed through kiauh. 2021-01-29 20:10:04 +01:00
th33xitus
0d29bf9f31 fix: wording, description dialog 2021-01-29 18:00:47 +01:00
th33xitus
6105122467 fix: better explanation in the macro dialog 2021-01-29 17:13:19 +01:00
th33xitus
55dad5ffb6 fix: switch back to stephans repo, PR was merged. 2021-01-29 15:14:13 +01:00
th33xitus
53aa86d138 fix: missed updating a file name 2021-01-29 13:28:10 +01:00
th33xitus
a0e53655bd fix: bug with sourcing the kiauh.ini and improve config change function 2021-01-29 12:17:42 +01:00
th33xitus
69d134d9d1 fix: rework .octoprint folder remove function to be able to remove multiple instances 2021-01-28 23:42:38 +01:00
th33xitus
7224040f8a fix: check for multiple installed and enabled octoprint services 2021-01-28 23:41:54 +01:00
th33xitus
1570fd588e fix: display disabled octoprint services as installed, and show the number of instances 2021-01-28 23:40:37 +01:00
th33xitus
0eb2d066be fix: rework dwc and octoprint service handling 2021-01-28 23:39:42 +01:00
th33xitus
61d1c46694 fix: dependency check was missing for moonraker installation 2021-01-28 22:57:35 +01:00
Keith Sachs
18e85235c1 Update install_moonraker.sh (#64)
added libjpeg-dev and zlib1g-dev to moonraker dependencies, this makes kiauh work for armbian
2021-01-28 22:48:14 +01:00
th33xitus
04f204e4c7 fix: re-arrange mainsail and fluidd 2021-01-28 18:56:11 +01:00
th33xitus
f8aa88f480 fix: rework menus and UI 2021-01-28 18:54:02 +01:00
th33xitus
0c4f1a5378 fix: rework/refactor remove functions 2021-01-28 18:52:53 +01:00
th33xitus
4652e5c177 fix: added one more cors domain and changed ip-range for local network 2021-01-28 18:02:21 +01:00
th33xitus
a6156ebdf3 fix: make kiauh.ini a hidden file in users home folder 2021-01-28 17:56:23 +01:00
th33xitus
1c92987b42 fix: logic error 2021-01-28 17:50:35 +01:00
th33xitus
d01140638a fix: some typos when creating minimal cfg 2021-01-28 16:48:36 +01:00
th33xitus
254e1b8244 fix: create mainsail or fluidd nginx config from template 2021-01-28 16:45:12 +01:00
th33xitus
19aed15db0 fix: typos 2021-01-28 16:44:05 +01:00
th33xitus
b2b19f446b fix: use $klipper_cfg_loc in klipperscreen system check 2021-01-28 16:25:25 +01:00
th33xitus
8ad7f5d47c fix: remove update manager question. thanks to the recent changes to the update manager, mainsail and fluidd can be supported at the same time. 2021-01-28 16:21:24 +01:00
th33xitus
69eb96d006 fix: typo in moonraker backup function 2021-01-28 16:20:35 +01:00
th33xitus
e233d8435a fix: new date format, fix a typo, restructure backup.sh 2021-01-27 19:57:19 +01:00
th33xitus
fb52c7e4ab fix: relocate the kiauh.ini file to ~/.config to keep values even after reinstalling kiauh. 2021-01-27 18:51:48 +01:00
th33xitus
bc53de626f fix: version comparing and displaying issues 2021-01-27 18:13:21 +01:00
th33xitus
f10a604f97 fix: small clean-up 2021-01-25 19:09:30 +01:00
th33xitus
1527d7a25d Merge remote-tracking branch 'origin/master' into kiauh-rework 2021-01-25 19:01:55 +01:00
th33xitus
65062bdfda fix: update the macros used for pause, cancel resume by mainsail and fluidd to better ones 2021-01-25 18:51:31 +01:00
th33xitus
e3d242017d fix: update a comment in nginx configs 2021-01-25 18:50:49 +01:00
th33xitus
c14289a8a6 fix: add the new update manager entries to moonraker.conf 2021-01-25 18:50:25 +01:00
th33xitus
d12554d2b7 fix: apply a force reset --hard before updating KIAUH. Fix for #63 2021-01-23 22:52:41 +01:00
th33xitus
036fb08132 fix: remove upload limit of 200M in NGINX configs for mainsail and fluidd 2021-01-23 22:51:44 +01:00
th33xitus
d9e697216f fix: small dwc2 related fixes 2021-01-21 23:47:29 +01:00
th33xitus
d958c1ba2a fix: prepare for possible multi instance capabilities of dwc2-for-klipper-socket 2021-01-21 23:34:25 +01:00
th33xitus
fb1d3a8cd9 fix: dwc installer rework, only for single instance atm. multi instance tbd... 2021-01-21 17:38:21 +01:00
th33xitus
7eb0fe24f8 fix: some typos in moonraker installer 2021-01-21 17:35:21 +01:00
th33xitus
738269adae fix: make use of the delivered .version files to read mainsail and fluidd version 2021-01-21 11:28:31 +01:00
th33xitus
d098eef08e fix: rename files to their correct name 2021-01-21 10:28:04 +01:00
th33xitus
598c106c3a fix: correct mainsail url 2021-01-21 10:23:08 +01:00
th33xitus
8bf20aa059 Merge pull request #60 from jordanruthe/klipperscreen_fix
klipperscreen: update to fix an exit problem
2021-01-20 23:12:34 +01:00
Jordan Ruthe
748a700c36 klipperscreen: update to fix an exit problem 2021-01-20 17:07:30 -05:00
th33xitus
51207e2eb9 fix: remove file, config.yaml gets dynamically created now 2021-01-20 20:25:48 +01:00
th33xitus
ee252d185a fix: update octoprint installer for multi instances 2021-01-20 20:24:24 +01:00
th33xitus
cf1e37fb2a fix: rework octoprint remove function for removing multi instances 2021-01-20 20:23:55 +01:00
th33xitus
3d9722f877 fix: rename array 2021-01-20 20:19:53 +01:00
th33xitus
1ef67fa05c fix: insert blank lines in output 2021-01-20 15:16:41 +01:00
th33xitus
db856de8e5 fix: fix shell command installation 2021-01-20 15:16:22 +01:00
th33xitus
4748c15424 fix: update UI nginx configs 2021-01-20 15:00:12 +01:00
th33xitus
ab9d73b5b5 fix: minimal cfg 2021-01-20 14:55:20 +01:00
th33xitus
6c33282d43 fix: refactor main_menu 2021-01-17 14:50:55 +01:00
th33xitus
e71f1b9dfd fix: refactor install_menu 2021-01-17 14:44:10 +01:00
th33xitus
e24afa42ac fix: made log upload function multi instance capable 2021-01-17 14:29:36 +01:00
th33xitus
2fa975e3c2 fix: missed closing an if statement 2021-01-17 14:29:06 +01:00
th33xitus
feb912da8a fix: make adding the kiauh_macros optional 2021-01-17 12:58:25 +01:00
th33xitus
e5ce642018 fix: reorder the klippy_uds to the bottom 2021-01-17 12:44:42 +01:00
th33xitus
7a4ccfe4df fix: remove the functionality of patching an old/existing moonraker.conf only for making it compatible again. it will be way easier now to simply re-install moonraker and let those configs be created from scratch. 2021-01-17 11:50:20 +01:00
th33xitus
6b2175e439 fix: make klipperscreen installer use the klipper_cfg_loc variable for the printer config directory 2021-01-17 11:28:14 +01:00
th33xitus
dd2a26e2d0 del: remove now unused moonraker.conf template from resources 2021-01-17 11:23:26 +01:00
th33xitus
cd1489f0e5 fix: move the update manager function to the webinterface installations 2021-01-17 11:20:57 +01:00
th33xitus
d705a043c9 fix: add more trusted clients, cors domains, auto create a config folder if missing for whatever reason 2021-01-17 11:19:22 +01:00
th33xitus
c1a21c52b6 fix: create cfg directories if they are missing for whatever reason. 2021-01-17 11:18:09 +01:00
th33xitus
94762727ae fix: rename webui_macros.cfg to kiauh_macros.cfg 2021-01-17 11:17:37 +01:00
th33xitus
9cde005e08 fix: better default park position for printers with less than 230mm on x and y 2021-01-15 20:10:19 +01:00
th33xitus
86e7f37f7b fix: bigger refactoring of the klipper webui installer 2021-01-15 19:52:56 +01:00
th33xitus
4d2eb49e85 fix: big refactoring of the moonraker installer 2021-01-15 17:52:04 +01:00
th33xitus
715ab5f4e2 fix: display instance count in main menu status 2021-01-15 17:50:43 +01:00
th33xitus
146586f894 fix: correct mjpgstreamer port 2021-01-13 18:04:36 +01:00
th33xitus
bd78fa8ef7 add: quick and simple solution to start, stop, restart klipper/moonraker from within the main menu 2021-01-10 13:05:58 +01:00
th33xitus
b12d344b40 fix: update the config folder change function (its more like a "rename only" function now and should be plenty sufficient?) 2021-01-10 12:55:24 +01:00
th33xitus
9ea29bf995 fix: introduce new/better folder structure for multi instance setups 2021-01-09 23:17:45 +01:00
th33xitus
d066999d0b fix: little UI fixes 2021-01-09 18:02:59 +01:00
th33xitus
1ab681e71a fix: enable python3 check again, display moonraker IP at the end of the setup 2021-01-09 16:49:10 +01:00
th33xitus
f782a32e9c fix: bug in assigning correct port to instances 2021-01-09 16:08:29 +01:00
th33xitus
7dc1bc6cc8 fix: add full range of local network to auto-created moonraker.conf files 2021-01-08 20:02:58 +01:00
th33xitus
2a29716bb9 fix: make mcu ids verbose again for easier copy pasting into configs 2021-01-08 20:02:18 +01:00
th33xitus
33a9fef676 fix: check via md5sum of requirements text file if an update of the python env is necessary. if yes, do that update automatically. no user interaction anymore. 2021-01-08 14:57:51 +01:00
th33xitus
88afa55268 feat: support flashing of multiple connected mcus 2021-01-08 13:56:36 +01:00
th33xitus
65ec2e5e1a fix: bugs 2021-01-07 17:03:14 +01:00
th33xitus
08c1807cc7 fix: forgot to stop services first 2021-01-07 16:40:52 +01:00
th33xitus
218700b63d fix: add remove method for init.d moonraker service 2021-01-07 16:27:51 +01:00
th33xitus
bb1c5c4900 fix: add remove method for removing old init.d klipper service 2021-01-07 16:25:31 +01:00
th33xitus
43a724b80e fix: add disable command 2021-01-07 16:22:20 +01:00
th33xitus
08ae57d349 initial commit of a bigger kiauh rework to make multi instance installations possible 2021-01-07 15:26:19 +01:00
th33xitus
61dfab5683 feat: klipper config backup function 2021-01-07 15:24:25 +01:00
th33xitus
8ee277fe2c fix: set the correct port 8080 for mjpgstreamer 2021-01-03 16:18:37 +01:00
th33xitus
a44ae4c2b9 fix: followup to 240746e. kiauh now also tries to identify the branch from the commit SHA if the HEAD was detached at a single commit, if the other method does not give a usefull output. 2020-12-22 21:58:20 +01:00
th33xitus
240746e1b6 fix: a fix for the detached HEAD issue when updating klipper that was introduced in a past commit. the user now gets a warn message that he needs to force update klipper. behind the scenes he will be brought back onto the branch and head gets reattached. 2020-12-22 20:39:12 +01:00
th33xitus
1fd839bc7a fix: add "v" to versions, add kiauh version to bottom left corner in main menu 2020-12-21 21:00:27 +01:00
th33xitus
a6bfb30235 fix: add missing fluidd backup function 2020-12-21 19:08:26 +01:00
th33xitus
7854fe3d95 fix: fix #50 2020-12-20 19:25:13 +01:00
th33xitus
1eda775571 fix: previous klipper updates resulted in a detached head state. fixed update routine and other stuff relaying on the read_branch function/GET_BRANCH variable 2020-12-20 18:41:42 +01:00
th33xitus
06eb86efd7 fix: grep pattern changed to read fluidd version again 2020-12-20 16:23:03 +01:00
th33xitus
0339f3f4f5 fix: update script
there is a new way of grabbing the mainsail and fluidd version without reading them from a version file kiauh created in the past.
2020-12-04 19:38:40 +01:00
domi
3032356f89 fix: bug that caused the script to hang because the file that was searched for was not found 2020-12-04 16:43:51 +01:00
th33xitus
f1289504bf fix: few moonraker update changes
- change in the description
- option 1 only does a git pull again when rebuilding venv/deps is not necessary
2020-12-02 21:39:16 +01:00
th33xitus
d3b7c923bd fix: followup to 1b7b8a1 2020-12-01 18:53:30 +01:00
th33xitus
194b8a54cc Merge pull request #47 from th33xitus/rework-display-version
Rework how versions are displayed and fetched
2020-11-29 14:57:15 +01:00
domi
1b7b8a1cf9 fix: new way of getting mainsail and fluidd version 2020-11-29 14:57:24 +01:00
domi
e6dde2cf23 fix: different way of displaying versions 2020-11-28 23:15:29 +01:00
domi
94926d5985 Update README.md 2020-11-28 18:04:14 +01:00
domi
acf8a817d8 Update changelog.md 2020-11-28 17:46:37 +01:00
domi
10558103a5 fix: fix some UI stuff 2020-11-28 17:42:36 +01:00
th33xitus
c61e101627 fix: fix typo 2020-11-28 16:46:43 +01:00
th33xitus
ddcaee0838 Merge pull request #44 from jordanruthe/master
Add KlipperScreen to kiauh
2020-11-28 16:45:47 +01:00
Jordan Ruthe
622e8251a3 Fixing typos 2020-11-28 10:37:04 -05:00
Jordan
b2556641ac Fix symlinks 2020-11-27 13:50:19 +00:00
Jordan
93f8e5e265 Add KlipperScreen to kiauh 2020-11-27 13:44:06 +00:00
domi
dde5812c6b fix: add check for dbus dependency (fix #43) 2020-11-26 15:23:16 +01:00
domi
42a5a0b6d8 fix: fix typo, should fix #42 2020-11-26 11:00:05 +01:00
domi
d95b8ac763 fix: reset to default foreground color 2020-11-21 21:17:24 +01:00
domi
865177d0fd fix: highlight note in moonraker update menu 2020-11-21 16:45:00 +01:00
domi
8923620414 fix: issue with displaying longer version numbers 2020-11-21 16:39:19 +01:00
th33xitus
ecc349e550 Merge pull request #40 from th33xitus/work-moonraker
fix: rework moonraker update function
2020-11-20 14:06:02 +01:00
domi
bc35a82cc8 fix: rework moonraker update function 2020-11-20 13:14:01 +01:00
domi
4fe964e886 fix: a bit better python3 check for moonraker 2020-11-20 11:18:29 +01:00
th33xitus
8371a014a0 fix: restart nginx with systemctl 2020-11-19 13:47:26 +01:00
th33xitus
90af76402b fix: add messages to ongoing process 2020-11-19 13:43:33 +01:00
th33xitus
25b911e867 fix: followup to d3a12f7d 2020-11-19 12:40:47 +01:00
th33xitus
4e9776cac3 Merge pull request #38 from th33xitus/work-fluidd
Fix KIAUH for update to Fluidd
2020-11-18 18:05:01 +01:00
th33xitus
2e3975374a Update Docs 2020-11-18 18:03:41 +01:00
th33xitus
ed9b8d844b fix: must restart moonraker after changes to moonraker.conf 2020-11-18 17:48:19 +01:00
th33xitus
d3a12f7db7 fix: add a patch due to recent changes to fluidd 2020-11-18 17:37:54 +01:00
th33xitus
3b74bb0b77 fix: add python check to moonraker installer 2020-11-13 21:20:48 +01:00
th33xitus
4f7ac68af6 fix: better moonraker service status check 2020-11-13 21:17:17 +01:00
th33xitus
e22c6be490 fix: cleanup 2020-11-13 20:49:10 +01:00
th33xitus
3fd9f95da3 fix: littel rework for dwc2 installer etc. 2020-11-13 20:48:57 +01:00
th33xitus
065786fea7 fix: forgot to disable nginx test during fluidd install 2020-11-07 10:00:20 +01:00
th33xitus
e370c54739 fix: add dependency check into klipper & moonraker update function 2020-11-06 17:56:30 +01:00
th33xitus
88fd83638c fix: disable error-prone api checks 2020-11-06 17:53:39 +01:00
th33xitus
3d065c0f1b add: example script for autocommits 2020-10-30 16:49:27 +01:00
th33xitus
d98151cfef fix: little typo (but it breaks stuff) 2020-10-30 12:50:51 +01:00
th33xitus
41d2dd80a7 Merge pull request #34 from th33xitus/work-webui
Work webui
2020-10-30 00:13:26 +01:00
th33xitus
7ff1d972b4 Update README.md, changelog.md, features.md 2020-10-30 00:11:20 +01:00
th33xitus
efe482ea58 fix: upload new image 2020-10-30 00:10:40 +01:00
th33xitus
8ba6aee952 fix: add fluidd to ui message 2020-10-29 23:32:24 +01:00
th33xitus
9ecf7068f1 fix: rework of writing to configs 2020-10-29 22:28:24 +01:00
th33xitus
74a6b04762 Merge branch 'work-webui' of https://github.com/th33xitus/kiauh into work-webui 2020-10-29 18:32:01 +01:00
th33xitus
55382b1ecb fix: followup to 9fc7f41 2020-10-29 18:31:07 +01:00
th33xitus
b48319e24d fix: rework klipper webui installers 2020-10-29 18:31:07 +01:00
th33xitus
8b03b27902 Merge remote-tracking branch 'origin/master' into work-webui 2020-10-29 18:30:14 +01:00
th33xitus
be2734e640 fix: little rework and fixes of shell command extension installation 2020-10-29 18:27:22 +01:00
th33xitus
7431124d87 fix: formatting 2020-10-29 17:32:48 +01:00
th33xitus
4ae696857f fix: followup to 9fc7f41 2020-10-28 23:16:24 +01:00
th33xitus
9fc7f4120a Merge branch 'moonraker-rework' into work-webui 2020-10-28 21:29:53 +01:00
th33xitus
dc32470a7c Merge pull request #33 from th33xitus/klipper-systemd
Support installation for klipper as systemd service
2020-10-28 21:24:22 +01:00
th33xitus
d91d01ae2c fix: rework klipper webui installers 2020-10-28 20:39:34 +01:00
th33xitus
62b0f2afd6 fix: convert test echo into status_msg 2020-10-28 13:03:21 +01:00
th33xitus
6ce475e27a fix: revert default selection for log creation question 2020-10-28 12:10:38 +01:00
th33xitus
e6d5a73a79 fix: followup to 5fd19ae to support klipper systemd service 2020-10-28 12:09:15 +01:00
th33xitus
5fd19aef0b add: support to install klipper as systemd service 2020-10-28 12:08:02 +01:00
th33xitus
911d0d37e5 fix: invert default selections during klipper install 2020-10-26 17:59:52 +01:00
th33xitus
cbffa1b31a fix: formatting 2020-10-21 21:40:21 +02:00
th33xitus
32c176bf2f fix: add missing function call and simplify function to set hostname 2020-10-21 21:15:30 +02:00
th33xitus
b4fc691da1 fix: possible fix for #24 2020-10-20 10:32:55 +02:00
th33xitus
6129f15386 Merge pull request #28 from th33xitus/work-ini
fix: #24 - .gitignore for kiauh.ini
2020-10-13 11:59:35 +02:00
th33xitus
759cc8e196 fix: whitespaces 2020-10-13 11:58:05 +02:00
th33xitus
dec1c0e83c fix: addition to b20cda1 2020-10-13 11:55:01 +02:00
th33xitus
b20cda1d15 fix: new function to fix #26 2020-10-13 11:41:57 +02:00
th33xitus
2c522e717f add: .gitignore for kiauh.ini 2020-10-13 11:02:42 +02:00
th33xitus
c06aa0457f bugfix: fix typo 2020-10-10 16:55:47 +02:00
th33xitus
253f4e1bf9 Merge pull request #25 from th33xitus/work-logupload
Add: log upload function
2020-10-10 16:42:19 +02:00
th33xitus
29e3d8b3d2 add: log update function 2020-10-10 16:41:00 +02:00
th33xitus
e0b7a75116 fix: update_all ui 2020-10-10 09:46:45 +02:00
th33xitus
083ca8b7fd fix: fetching correct download url + more status messages 2020-10-10 09:37:06 +02:00
th33xitus
4e2c6aa2cc fix: fluidd update function not existing 2020-10-10 09:35:40 +02:00
th33xitus
a4a85b1045 func: drop support for switching to moonraker klipper branch (it's obsolete) 2020-10-10 01:22:08 +02:00
th33xitus
ccf5b003fe fix: more verbose ok_msg 2020-10-09 19:00:59 +02:00
th33xitus
0027886f4f Update changelog.md 2020-10-06 22:48:07 +02:00
th33xitus
26599d734d Update README.md 2020-10-06 22:37:08 +02:00
th33xitus
d58841a269 Merge pull request #22 from th33xitus/work-fluidd
Installer for Fluidd and several other fixes
2020-10-06 22:31:41 +02:00
th33xitus
ab6fa29bd2 Update README.md 2020-10-06 22:29:03 +02:00
th33xitus
6142c0c18f bugfix: typos 2020-10-06 21:58:25 +02:00
th33xitus
fb14d9c717 bugfix: fix typos 2020-10-06 21:14:56 +02:00
th33xitus
bc658f38bc fix: UI 2020-10-06 21:04:35 +02:00
th33xitus
976d9cb5a9 fix: add gzip to fluidd nginx config 2020-10-06 20:34:45 +02:00
th33xitus
029127bf00 fix: nginx port stuff on all interface installers 2020-10-06 20:19:37 +02:00
th33xitus
9b424c8343 bugfix: add missing removal actions 2020-10-06 18:33:02 +02:00
th33xitus
3658137a1c bugfix: fix introduced bug in fe941f4 2020-10-06 18:18:40 +02:00
th33xitus
4e38e85962 bugfix: add greater delay to test_api to let cpu usage settle on slower devices 2020-10-06 17:55:24 +02:00
th33xitus
fe941f4227 feature: initial commit to add installer for Fluidd + rewrite nginx related stuff 2020-10-06 15:03:54 +02:00
th33xitus
9a4ca4114c fix: more verbose kiauh update message 2020-10-05 14:30:52 +02:00
th33xitus
1c41abe40f bugfix: fix the wrong displaying of color codes in windows-consoles 2020-10-05 14:14:26 +02:00
th33xitus
3257c845ee remove branding removal function for mainsail 2020-09-21 23:48:25 +02:00
th33xitus
3d6b4bec95 Update mainsail_macros.cfg
Bring mainsail_macros.cfg in line with the macro examples from the mainsail instructions: https://github.com/meteyou/mainsail/blob/master/docs/macros.md
2020-09-20 11:02:11 +02:00
th33xitus
c55a010bcd Update README.md 2020-09-18 09:00:32 +02:00
th33xitus
78dcc0a1ad fix: concatenate with && again 2020-09-18 08:58:46 +02:00
th33xitus
2a2bc6b377 Update README.md 2020-09-17 23:29:06 +02:00
th33xitus
1159d74748 Update README.md
update image url
2020-09-17 21:37:03 +02:00
th33xitus
40f34f26ea Merge pull request #18 from th33xitus/work17092020
add: update all function
2020-09-17 21:36:00 +02:00
th33xitus
668331e7ea add: update all function 2020-09-17 21:23:26 +02:00
th33xitus
d2bdf6b089 start/restart/stop services with systemctl 2020-09-17 19:31:33 +02:00
th33xitus
10f75268ab Update README.md 2020-09-17 19:05:04 +02:00
th33xitus
46e6b7d40a Update changelog.md 2020-09-17 19:02:43 +02:00
th33xitus
bd876c744d remove: update check for branch dev-2.0
abandon the dev-2.0 branch
2020-09-17 18:49:58 +02:00
th33xitus
db0a4b4e03 fix: adresses #17 2020-09-16 22:19:14 +02:00
th33xitus
efa67b5843 Update README.md
add link to a gitbook with more detailed instructions for installing klipper and mainsail
2020-09-16 10:16:35 +02:00
th33xitus
d393b0a9e9 Merge pull request #16 from tumbleweedlabs/master, fixes #15
Update mainsail_macros.cfg, fixes #15
2020-09-16 09:52:21 +02:00
tumbleweedlabs
85f0c26215 Update mainsail_macros.cfg
Per meteyou, the last G91 in the resume macro is erroneous and can be eliminated.

https://discordapp.com/channels/431557959978450984/704747457795588100/755540226843345038
2020-09-15 20:39:13 -07:00
tumbleweedlabs
35dcf10c9a Update mainsail_macros.cfg
Pull request to fix issue 15: https://github.com/th33xitus/kiauh/issues/15
2020-09-15 17:30:25 -07:00
th33xitus
64b6d6b26a fix: stop/start dwc service when updating 2020-09-14 22:54:01 +02:00
th33xitus
a2201ab3f7 Merge pull request #13 from th33xitus/work-13092020
Work 13092020
2020-09-13 12:25:14 +02:00
th33xitus
63a2f95cc0 Update README.md 2020-09-13 12:24:02 +02:00
th33xitus
043f09aeeb Delete core_functions.md 2020-09-13 12:23:04 +02:00
th33xitus
1fa1636f75 Update features.md 2020-09-13 12:21:54 +02:00
th33xitus
3770f0fd7a Update README.md 2020-09-13 12:12:33 +02:00
th33xitus
ecd9e7f489 Update README.md 2020-09-13 12:11:52 +02:00
th33xitus
125eea921f add: some docs 2020-09-13 11:57:03 +02:00
th33xitus
2e9be1adc5 fix: typos 2020-09-13 11:47:01 +02:00
th33xitus
3e35be5681 add: user choice for dwc service (init.d or systemd) 2020-09-13 11:25:46 +02:00
th33xitus
d9d3006be4 fix: correct ExecStart parameter 2020-09-13 11:25:13 +02:00
th33xitus
6abab9fb06 fix: remove a sudo command from removal process 2020-09-12 12:31:14 +02:00
th33xitus
ef4b6b092f Update README.md 2020-09-12 09:45:22 +02:00
th33xitus
b2a8c52995 Merge branch 'dev-2.0' 2020-09-12 09:32:24 +02:00
th33xitus
9bfce238d7 add: support for new dwc-for-klipper-socket by @Stephan3
This will replace the previous installer for dwc2-for-klipper without substitution. dwc2-for-klipper won't be maintained anymore.
2020-09-12 09:31:40 +02:00
th33xitus
d0ac497a60 add: dwc2-for-klipper-socket installer scripts 2020-09-12 08:48:33 +02:00
th33xitus
4c44b8714a Update moonraker.conf
Update the default moonraker.conf to be mostly in sync with https://github.com/Arksine/moonraker/blob/master/docs/installation.md#moonraker-configuration-moonrakerconf
2020-09-11 16:17:03 +02:00
th33xitus
da1820a7da Merge branch 'dev-2.0' 2020-09-11 14:34:28 +02:00
th33xitus
b576491320 fix: rework dialogues to display invalid input 2020-09-11 14:12:49 +02:00
th33xitus
0c9e967ebe fix: invert default selection
invert the pre-selected option from yes to no.
2020-09-11 12:55:34 +02:00
th33xitus
4d53a92c83 Merge branch 'master' into dev-2.0 2020-09-11 12:35:42 +02:00
th33xitus
0b474514b1 fix: bug introduced with c98584d 2020-09-11 12:34:47 +02:00
th33xitus
9b7bde7da0 fix: bug introduced in previous commit 2020-09-11 12:20:31 +02:00
th33xitus
fc61f17297 Merge branch 'dev-2.0' of https://github.com/th33xitus/kiauh into dev-2.0 2020-09-11 12:04:41 +02:00
th33xitus
3a4fbdbd61 fix: tweak moonraker/mainsail install process 2020-09-11 12:04:35 +02:00
th33xitus
1ec269969b fix: tweak moonraker/mainsail install process 2020-09-11 11:31:53 +02:00
th33xitus
fa669a8690 fix: do not automatically remove moonraker.conf 2020-09-11 11:31:04 +02:00
th33xitus
599b85c47d fix: issue #12 2020-09-11 10:26:15 +02:00
th33xitus
83e9618a75 fix: cleaner nginx removal process 2020-09-11 10:25:38 +02:00
th33xitus
c98584dde8 bugfix: kiauh status check prevents kiauh from starting if no kiauh .git directory present 2020-09-06 11:16:34 +02:00
th33xitus
53f4b9a9f5 Merge pull request #11 from leifbladt/patch-1
Simplify installation instructions
2020-09-04 11:31:15 +02:00
Leif Bladt
a23fb6b79e Simplify installation instructions
As you're already in the kiauh directory, the `chmod` command can be simplified.
2020-09-04 09:26:29 +02:00
th33xitus
262e489d1a fix: moonraker update function - check for patched klipper sysfile 2020-09-03 09:55:37 +02:00
th33xitus
9e13c2d0ea fix: wrong include path in default mainsail printer.cfg 2020-09-02 23:20:25 +02:00
th33xitus
34162c4b65 fix: bug in moonraker system check for printer.cfg 2020-09-02 23:18:47 +02:00
th33xitus
7384ed8a0e fix: tweak mainsail setup 2020-09-02 23:16:01 +02:00
th33xitus
8899275022 function: re-enable the branding removal function 2020-09-02 22:22:10 +02:00
th33xitus
03c3c3d50f fix: Mainsail update function not working 2020-09-01 18:01:08 +02:00
th33xitus
45c8b5a33f Merge branch 'dev-2.0' 2020-09-01 14:28:40 +02:00
th33xitus
4a60b9c8ec remove: function to remove voron brandings from mainsail, mainsail has its own logo now (thx @lixxbox). function therefore obsolete 2020-09-01 14:27:53 +02:00
th33xitus
d964523558 fix: make some output silent 2020-09-01 14:22:28 +02:00
th33xitus
49a28cff22 fix: better suited status message 2020-09-01 14:19:36 +02:00
th33xitus
8a5e358a73 fix: remove use of symlinks, also update for mainsail v0.2.0 2020-09-01 13:52:36 +02:00
th33xitus
9ec63be673 fix: rework locate_printer_cfg function 2020-09-01 13:51:51 +02:00
th33xitus
bae0c1f0fd fix: typo and bug, also bring code in place for later moonraker version 2020-08-30 13:59:19 +02:00
th33xitus
fc777ccb96 fix: remove two menu entries 2020-08-30 13:37:49 +02:00
th33xitus
96be30343d fix: rework klipper installation routine 2020-08-30 13:35:14 +02:00
th33xitus
24a59cffb7 Merge branch 'dev-2.0' 2020-08-26 10:47:43 +02:00
th33xitus
dba14aadb6 new function: install shell command extension by Arksine 2020-08-26 08:18:59 +02:00
th33xitus
d51062c5ae fix: use new method to locate printer.cfg, rework depending functions 2020-08-26 08:18:18 +02:00
th33xitus
1b4baad654 fix: status message 2020-08-24 11:48:57 +02:00
th33xitus
5a25bd3686 fix: remove old read branch function 2020-08-23 21:52:18 +02:00
th33xitus
f08d6ecdc2 fix: clean up and restructure menus and ui 2020-08-23 20:48:07 +02:00
th33xitus
be56439587 fix: typo in mainsail installer 2020-08-23 16:31:55 +02:00
th33xitus
dd16a2503b fix: extend description 2020-08-22 23:32:35 +02:00
th33xitus
613e0d779f fix: rollback function 2020-08-22 23:21:26 +02:00
th33xitus
612a4225be fix: recreate deleted variable 2020-08-22 22:20:13 +02:00
th33xitus
35ecf3ae65 fix: kiauh update function (finally?) 2020-08-22 21:58:55 +02:00
th33xitus
e32cc9d3cb fix: remove deprecated main menu entry 2020-08-22 20:29:22 +02:00
th33xitus
9a3e41716e fix: delete a debug msg 2020-08-22 20:05:56 +02:00
th33xitus
09e4c7bea5 fix: bring octoprint installation routine into line with dwc2 and mainsail/moonraker routines 2020-08-22 19:30:35 +02:00
th33xitus
5392e2a9ab add: status message for starting installation 2020-08-22 19:18:27 +02:00
th33xitus
41789fd158 fix: restructuring reverse proxy and hostname functions 2020-08-22 19:18:08 +02:00
th33xitus
2532fc7e4c fix: restructure functions.sh 2020-08-22 19:14:11 +02:00
th33xitus
63f65a02b4 fix: a more verbose dependency check message 2020-08-22 18:23:41 +02:00
th33xitus
f3a139f317 fix: remove_ui 2020-08-22 18:19:33 +02:00
th33xitus
68cb5427b6 fix: rewrite dwc2 installation 2020-08-22 17:49:35 +02:00
th33xitus
2be2ce12c6 add: variable triggered function to disable octoprint service 2020-08-22 17:48:52 +02:00
th33xitus
eb6b2465c7 fix: mainsail/moonraker related 2020-08-22 17:47:47 +02:00
th33xitus
cc1c804730 fix: klipper update function 2020-08-22 09:10:09 +02:00
th33xitus
7dda59649b fix: ui not printing branch 2020-08-22 09:09:28 +02:00
th33xitus
60dc9d623e fix: rollback function 2020-08-22 09:09:00 +02:00
th33xitus
92b4bc41ac fix: move test_api to moonraker install routine 2020-08-20 18:58:57 +02:00
th33xitus
db5b906e5b add: separate install/remove function for mainsail and moonraker 2020-08-20 18:30:56 +02:00
th33xitus
d78670e0ab fix: formatting 2020-08-20 17:38:24 +02:00
th33xitus
716b21a629 feature: new experimental moonraker/mainsail install routine 2020-08-20 16:32:01 +02:00
th33xitus
1a037c687b add: new log location to mainsail remove data_arr 2020-08-20 16:31:28 +02:00
th33xitus
0dc5edfd7b add: mainsail_macros.cfg 2020-08-20 16:30:40 +02:00
th33xitus
c82e21419c fix: change color variables 2020-08-20 16:29:15 +02:00
th33xitus
7b220c6546 fix: wrong path for include config 2020-08-16 12:57:53 +02:00
th33xitus
15c6296e2c fix: all scripts chmod +x 2020-08-16 10:49:53 +02:00
th33xitus
e00d2951a9 fix: bug in klipper version check 2020-08-16 10:19:58 +02:00
th33xitus
68f7c98985 Merge pull request #9 from th33xitus/master
fix: kiauh update function
2020-08-15 20:13:25 +02:00
th33xitus
82408fee2e fix: kiauh update function 2020-08-15 19:27:29 +02:00
th33xitus
3076d400a0 fix: kiauh update check 2020-08-15 19:03:44 +02:00
th33xitus
c91718cfc3 update: mainsail installation routine 2020-08-15 17:11:11 +02:00
th33xitus
9c20ad7d99 fix: read branch error 2020-08-15 17:08:55 +02:00
th33xitus
7614d596ab fix: kiauh update function 2020-08-15 10:01:32 +02:00
th33xitus
5bcd9a73b5 fix: missing quotes in remove_brandings 2020-08-12 19:46:54 +02:00
th33xitus
a06b06eb46 show error message if warning is ignored or no printer connection was found. extend the function to search by path as well if search by id is not successfull 2020-08-12 18:02:31 +02:00
th33xitus
66e9069687 feature: remove voron branding from mainsail ui 2020-08-12 17:34:53 +02:00
th33xitus
a782fecd25 fix missing entry in mainsail nginx cfg 2020-08-12 16:23:26 +02:00
th33xitus
5dda935f58 Merge branch 'master' into dev-2.0 2020-08-09 10:20:21 +02:00
th33xitus
8cfe4a81ef Update README.md 2020-08-09 09:47:07 +02:00
th33xitus
2e4da7f220 fix read branch error 2020-08-05 00:09:18 +02:00
th33xitus
be6a63f95b new kiauh update check 2020-08-05 00:03:54 +02:00
th33xitus
9fcbcfcba8 new way of reading current active klipper branch 2020-08-04 23:27:51 +02:00
th33xitus
3ec26ba319 update mainsail config 2020-08-03 09:40:54 +02:00
th33xitus
188239d7ea fix kiauh update check 2020-07-31 17:05:11 +02:00
th33xitus
b850aa4d96 Fix Bug 2020-07-31 10:30:14 +02:00
th33xitus
2e5e06c1ff remove tornado with DWC2 2020-07-31 10:18:03 +02:00
th33xitus
96c094d36b UI Fix 2020-07-31 10:17:53 +02:00
th33xitus
3be01528d9 Merge branch 'master' of https://github.com/th33xitus/kiauh 2020-07-29 21:29:56 +02:00
th33xitus
6b77070baa one screenshot only 2020-07-29 21:27:12 +02:00
th33xitus
4e2743d587 make backup function usable 2020-07-29 21:25:31 +02:00
th33xitus
1d9cbecbb5 add moonraker update function + ui stuff
add moonraker update function + ui stuff
2020-07-29 21:13:51 +02:00
th33xitus
bd90677cfa add moonraker update function + ui stuff 2020-07-29 21:12:07 +02:00
th33xitus
e75df6d46c kiauh status now checks master branch 2020-07-29 15:58:27 +02:00
th33xitus
b6c07fc357 Merge branch 'dev-2.0' 2020-07-29 15:48:47 +02:00
th33xitus
fce1eb5a8c update script to use mainsail beta - dropping mainsail alpha 2020-07-27 11:26:22 +02:00
th33xitus
82d76b18f0 new rollback feature 2020-07-27 10:29:28 +02:00
th33xitus
dc4cc59038 before updating, save the current klipper commit state in ini 2020-07-26 18:15:36 +02:00
th33xitus
6f30208863 rename INI_DIR to INI_FILE 2020-07-26 18:11:22 +02:00
th33xitus
303c96cbb1 remove hint msg -> useless 2020-07-26 18:10:45 +02:00
th33xitus
da57fe7ccc fix bug 2020-07-26 15:16:29 +02:00
th33xitus
e5e135c68b add confirmation messages 2020-07-26 12:36:05 +02:00
th33xitus
22888f5756 simple startup update check for KIAUH 2020-07-25 20:57:32 +02:00
th33xitus
d84a740f0d Merge branch 'dev-2.0' of https://github.com/th33xitus/kiauh into dev-2.0 2020-07-25 13:16:41 +02:00
th33xitus
64a7dec09d Fix UI stuff 2020-07-25 13:16:37 +02:00
th33xitus
a76cab0fe6 restructure install/update for dwc2 2020-07-25 13:16:04 +02:00
th33xitus
e0b9a1ef3b Update README.md 2020-07-24 23:09:06 +02:00
th33xitus
6c4e6b057e Update README.md 2020-07-24 23:08:12 +02:00
th33xitus
37b23129c0 Update README.md 2020-07-24 23:05:24 +02:00
th33xitus
5651ce2d63 add screenshots 2020-07-24 22:07:52 +02:00
th33xitus
0a0a9f2f93 preconfigure octoprint for use with klipper 2020-07-24 21:44:58 +02:00
th33xitus
876b316af4 change nginx configs listen to ipv4 only 2020-07-24 21:44:30 +02:00
th33xitus
f51ba88f64 advanced menu: change hostname function 2020-07-24 18:00:57 +02:00
th33xitus
02c6a46a78 Fix bugs 2020-07-24 13:21:34 +02:00
th33xitus
84fc849caf UI stuff 2020-07-24 13:12:04 +02:00
th33xitus
87ed15a117 Finish backup functions 2020-07-24 13:11:53 +02:00
th33xitus
2bbf006475 some reverse proxy tweaks 2020-07-23 19:39:12 +02:00
th33xitus
2596e8d08a Fixes 2020-07-23 16:13:33 +02:00
th33xitus
80344ec41c Fixes 2020-07-23 11:04:23 +02:00
th33xitus
a2042ec378 Better tornado install check 2020-07-23 10:50:10 +02:00
th33xitus
734aed1f7b Better dependency check 2020-07-23 10:20:14 +02:00
th33xitus
108b5c7b0c Fixes 2020-07-22 17:50:46 +02:00
th33xitus
3091ce55ad Fix bug 2020-07-22 13:27:41 +02:00
th33xitus
e6b6dbfa74 Merge branch 'dev-2.0' of https://github.com/th33xitus/kiauh into dev-2.0 2020-07-22 10:16:57 +02:00
th33xitus
e22105a728 add dwc2 reverse proxy option 2020-07-22 10:16:52 +02:00
th33xitus
092f2793b3 Update README.md 2020-07-20 11:59:02 +02:00
th33xitus
3ab4244709 Update README.md 2020-07-20 11:50:24 +02:00
th33xitus
7b48061281 Update README.md 2020-07-20 11:02:11 +02:00
th33xitus
2b6dbaab76 remove webcam stuff: i can't test it sufficient enough, users need to install it manually for now 2020-07-20 10:45:04 +02:00
th33xitus
4cb2ba51bd fix remote version checks 2020-07-20 10:09:42 +02:00
th33xitus
8ffdf12a4a add octoprint reverse proxy 2020-07-19 22:38:21 +02:00
th33xitus
28d7a4aeda feature: remove option for tornado and nginx 2020-07-18 20:30:49 +02:00
th33xitus
fed882298e do not remove tornado when removing dwc2fk, will get seperate function 2020-07-18 18:49:55 +02:00
th33xitus
e4cfad4162 new function: allow hostname to be set 2020-07-18 18:33:55 +02:00
th33xitus
b4496603ed Update README.md 2020-07-18 15:17:50 +02:00
th33xitus
1cc94d4874 Fix bugs 2020-07-16 17:01:57 +02:00
th33xitus
d19c4d33cb Fix OctoPrint toggle function 2020-07-16 16:33:44 +02:00
th33xitus
fd71b0ee62 Fixes/Tweaks 2020-07-16 15:54:57 +02:00
th33xitus
49d0a65bcd Merge branch 'dev-2.0' of https://github.com/th33xitus/kiauh into dev-2.0 2020-07-16 15:14:27 +02:00
th33xitus
ba53517f09 fix breaking stuff 2020-07-16 15:14:23 +02:00
th33xitus
1be6cd0a3d remove nginx-purge option 2020-07-16 15:09:28 +02:00
th33xitus
8c0e231ade some "debloating" 2020-07-16 15:07:00 +02:00
th33xitus
1551920fe3 Update README.md 2020-07-15 23:33:20 +02:00
th33xitus
a5155c50cc Fixes/Tweaks 2020-07-15 22:52:23 +02:00
th33xitus
57d86b9ef4 Add checks for octoprint service when installing DWC2/Mainsail 2020-07-15 22:02:42 +02:00
th33xitus
182164af9c Small fixes/tweaks 2020-07-15 21:50:40 +02:00
th33xitus
45fe688f87 Add Octoprint Service toggle function and a new confirm message 2020-07-15 21:41:20 +02:00
th33xitus
fe27d054f8 Add Octoprint installer/remover 2020-07-15 18:57:46 +02:00
th33xitus
21aa2f45e5 Rename mainsail service to moonraker service 2020-07-15 11:50:57 +02:00
th33xitus
eea1f5540e Better error message 2020-07-15 11:25:16 +02:00
th33xitus
2771184e48 Add abort method if not on moonraker fork 2020-07-15 11:17:36 +02:00
th33xitus
2b6abac0c9 Fix missing function call
Fixed a missing function call for creating config entries.
Installing mainsail should work now pretty straight forward.
2020-07-13 21:09:40 +02:00
th33xitus
85a9186c78 Add restart functions 2020-07-13 21:08:31 +02:00
th33xitus
7106994bf6 Deactivate backup menu
Still WIP, has no function at all at them moment.
2020-07-13 18:14:10 +02:00
th33xitus
c57acefc16 Delete status.sh
derp...
2020-07-13 18:10:10 +02:00
th33xitus
e2bb6cd849 Fix visual issue
Fix an UI issue when DWC2 or Mainsail are not installed
2020-07-13 18:09:30 +02:00
th33xitus
f1d9b9c21b Fix visual issue
Fix an UI issue when DWC2 or Mainsail are not installed
2020-07-13 18:07:43 +02:00
th33xitus
6cf253eb8c Fix typo 2020-07-13 17:53:08 +02:00
th33xitus
3f7b454710 Update README.md
add screenshot
2020-07-13 17:42:38 +02:00
th33xitus
50eb2621d7 Update README.md
Update instructions for switching to the dev-2.0 branch.
2020-07-13 17:21:10 +02:00
th33xitus
bf70157a64 Update README.md 2020-07-13 17:15:09 +02:00
150 changed files with 16770 additions and 1819 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: dw__0
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://paypal.me/dwillner0

50
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Bug report
description: Create a report to help us improve
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
This issue form is for reporting bugs only!
If you have a feature request, please use [feature_request](/new?template=feature_request.yml)
- type: textarea
id: distro
attributes:
label: Linux Distribution
description: >-
The linux distribution the issue occured on
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened
description: >-
A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: What did you expect to happen
description: >-
A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: repro-steps
attributes:
label: How to reproduce
description: >-
Minimal and precise steps to reproduce this bug.
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional information
description: |
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by
dragging and dropping files in the field below.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Klipper Discord
url: https://discord.klipper3d.org/
about: Quickest way to get in contact

View File

@@ -0,0 +1,40 @@
name: Feature request
description: Suggest an idea for this project
labels: ["feature request"]
body:
- type: markdown
attributes:
value: |
This issue form is for feature requests only!
If you've found a bug, please use [bug_report](/new?template=bug_report.yml)
- type: textarea
id: problem-description
attributes:
label: Is your feature request related to a problem? Please describe
description: >-
A clear and concise description of what the problem is.
validations:
required: true
- type: textarea
id: solution-description
attributes:
label: Describe the solution you'd like
description: >-
A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: possible-alternatives
attributes:
label: Describe alternatives you've considered
description: >-
A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: additional-info
attributes:
label: Additional information
description: |
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by
dragging and dropping files in the field below.

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.idea
.vscode
.idea
.pytest_cache
.kiauh-env
*.code-workspace
*.iml
kiauh.cfg

15
.shellcheckrc Normal file
View File

@@ -0,0 +1,15 @@
source=scripts
enable=avoid-nullary-conditions
enable=deprecate-which
enable=quote-safe-variables
enable=require-variable-braces
enable=require-double-brackets
# SC2162: `read` without `-r` will mangle backslashes.
# https://github.com/koalaman/shellcheck/wiki/SC2162
disable=SC2162
# SC2164: Use `cd ... || exit` in case `cd` fails
# https://github.com/koalaman/shellcheck/wiki/SC2164
disable=SC2164

194
README.md
View File

@@ -1,12 +1,190 @@
# KIAUH <p align="center">
<a>
<img src="https://raw.githubusercontent.com/dw-0/kiauh/master/resources/screenshots/kiauh.png" alt="KIAUH logo" height="181">
<h1 align="center">Klipper Installation And Update Helper</h1>
</a>
</p>
## Klipper Installation And Update Helper <p align="center">
A handy installation script that makes installing Klipper (and more) a breeze!
</p>
This script was actually created for my personal use only but i then decided to make the script accessible for everyone. <p align="center">
It is meant to help guiding you through a complete fresh install of Klipper and optionally the DWC2 web UI + DWC2-for-Klipper. <a><img src="https://img.shields.io/github/license/dw-0/kiauh"></a>
There are also functions for updating your current installations or removing them from your system. <a><img src="https://img.shields.io/github/stars/dw-0/kiauh"></a>
<a><img src="https://img.shields.io/github/forks/dw-0/kiauh"></a>
<a><img src="https://img.shields.io/github/languages/top/dw-0/kiauh?logo=gnubash&logoColor=white"></a>
<a><img src="https://img.shields.io/github/v/tag/dw-0/kiauh"></a>
<br />
<a><img src="https://img.shields.io/github/last-commit/dw-0/kiauh"></a>
<a><img src="https://img.shields.io/github/contributors/dw-0/kiauh"></a>
</p>
## First things first: When you decide to use this script, you use it at your own risk! <hr>
# THIS VERSION IS WORK IN PROGRESS!!! <h2 align="center">
# BUGS MAY BE STILL PRESENT!!! 📄️ Instructions 📄
</h2>
### 📋 Prerequisites
KIAUH is a script that assists you in installing Klipper on a Linux operating system that has
already been flashed to your Raspberry Pi's (or other SBC's) SD card. As a result, you must ensure
that you have a functional Linux system on hand. `Raspberry Pi OS Lite (either 32bit or 64bit)` is a recommended Linux image
if you are using a Raspberry Pi. The [official Raspberry Pi Imager](https://www.raspberrypi.com/software/)
is the simplest way to flash an image like this to an SD card.
* Once you have downloaded, installed and launched the Raspberry Pi Imager,
select `Choose OS -> Raspberry Pi OS (other)`: \
<p align="center">
<img src="https://raw.githubusercontent.com/dw-0/kiauh/master/resources/screenshots/rpi_imager1.png" alt="KIAUH logo" height="350">
</p>
* Then select `Raspberry Pi OS Lite (32bit)` (or 64bit if you want to use that instead):
<p align="center">
<img src="https://raw.githubusercontent.com/dw-0/kiauh/master/resources/screenshots/rpi_imager2.png" alt="KIAUH logo" height="350">
</p>
* Back in the Raspberry Pi Imager's main menu, select the corresponding SD card to which
you want to flash the image.
* Make sure to go into the Advanced Option (the cog icon in the lower left corner of the main menu)
and enable SSH and configure Wi-Fi.
* If you need more help for using the Raspberry Pi Imager, please visit the [official documentation](https://www.raspberrypi.com/documentation/computers/getting-started.html).
These steps **only** apply if you are actually using a Raspberry Pi. In case you want
to use a different SBC (like an Orange Pi or any other Pi derivates), please look up on how to get an appropriate Linux image flashed
to the SD card before proceeding further (usually done with Balena Etcher in those cases). Also make sure that KIAUH will be able to run
and operate on the Linux Distribution you are going to flash. You likely will have the most success with
distributions based on Debian 11 Bullseye. Read the notes further down below in this document.
### 💾 Download and use KIAUH
**📢 Disclaimer: Usage of this script happens at your own risk!**
* **Step 1:** \
To download this script, it is necessary to have git installed. If you don't have git already installed, or if you are unsure, run the following command:
```shell
sudo apt-get update && sudo apt-get install git -y
```
* **Step 2:** \
Once git is installed, use the following command to download KIAUH into your home-directory:
```shell
cd ~ && git clone https://github.com/dw-0/kiauh.git
```
* **Step 3:** \
Finally, start KIAUH by running the next command:
```shell
./kiauh/kiauh.sh
```
* **Step 4:** \
You should now find yourself in the main menu of KIAUH. You will see several actions to choose from depending
on what you want to do. To choose an action, simply type the corresponding number into the "Perform action"
prompt and confirm by hitting ENTER.
<hr>
<h2 align="center">❗ Notes ❗</h2>
### **📋 Please see the [Changelog](docs/changelog.md) for possible important changes!**
- Mainly tested on Raspberry Pi OS Lite (Debian 10 Buster / Debian 11 Bullseye)
- Other Debian based distributions (like Ubuntu 20 to 22) likely work too
- Reported to work on Armbian as well but not tested in detail
- During the use of this script you will be asked for your sudo password. There are several functions involved which need sudo privileges.
<hr>
<h2 align="center">🌐 Sources & Further Information</h2>
<table>
<tr>
<th><h3><a href="https://github.com/Klipper3d/klipper">Klipper</a></h3></th>
<th><h3><a href="https://github.com/Arksine/moonraker">Moonraker</a></h3></th>
<th><h3><a href="https://github.com/mainsail-crew/mainsail">Mainsail</a></h3></th>
</tr>
<tr>
<th><img src="https://raw.githubusercontent.com/Klipper3d/klipper/master/docs/img/klipper-logo.png" alt="Klipper Logo" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/9563098?v=4" alt="Arksine avatar" height="64"></th>
<th><img src="https://raw.githubusercontent.com/mainsail-crew/docs/master/assets/img/logo.png" alt="Mainsail Logo" height="64"></th>
</tr>
<tr>
<th>by <a href="https://github.com/KevinOConnor">KevinOConnor</a></th>
<th>by <a href="https://github.com/Arksine">Arksine</a></th>
<th>by <a href="https://github.com/mainsail-crew">mainsail-crew</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/fluidd-core/fluidd">Fluidd</a></h3></th>
<th><h3><a href="https://github.com/jordanruthe/KlipperScreen">KlipperScreen</a></h3></th>
<th><h3><a href="https://github.com/OctoPrint/OctoPrint">OctoPrint</a></h3></th>
</tr>
<tr>
<th><img src="https://raw.githubusercontent.com/fluidd-core/fluidd/master/docs/assets/images/logo.svg" alt="Fluidd Logo" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/31575189?v=4" alt="jordanruthe avatar" height="64"></th>
<th><img src="https://raw.githubusercontent.com/OctoPrint/OctoPrint/master/docs/images/octoprint-logo.png" alt="OctoPrint Logo" height="64"></th>
</tr>
<tr>
<th>by <a href="https://github.com/fluidd-core">fluidd-core</a></th>
<th>by <a href="https://github.com/jordanruthe">jordanruthe</a></th>
<th>by <a href="https://github.com/OctoPrint">OctoPrint</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/nlef/moonraker-telegram-bot">Moonraker-Telegram-Bot</a></h3></th>
<th><h3><a href="https://github.com/Kragrathea/pgcode">PrettyGCode for Klipper</a></h3></th>
<th><h3><a href="https://github.com/TheSpaghettiDetective/moonraker-obico">Obico for Klipper</a></h3></th>
</tr>
<tr>
<th><img src="https://avatars.githubusercontent.com/u/52351624?v=4" alt="nlef avatar" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/5917231?v=4" alt="Kragrathea avatar" height="64"></th>
<th><img src="https://avatars.githubusercontent.com/u/46323662?s=200&v=4" alt="Obico logo" height="64"></th>
</tr>
<tr>
<th>by <a href="https://github.com/nlef">nlef</a></th>
<th>by <a href="https://github.com/Kragrathea">Kragrathea</a></th>
<th>by <a href="https://github.com/TheSpaghettiDetective">Obico</a></th>
</tr>
<tr>
<th><h3><a href="https://github.com/Clon1998/mobileraker_companion">Mobileraker's Companion</a></h3></th>
<th><h3><a href="https://octoeverywhere.com/?source=kiauh_readme">OctoEverywhere For Klipper</a></h3></th>
<th><h3></h3></th>
</tr>
<tr>
<th><a href="https://github.com/Clon1998/mobileraker_companion"><img src="https://raw.githubusercontent.com/Clon1998/mobileraker/master/assets/icon/mr_appicon.png" alt="OctoEverywhere Logo" height="64"></th>
<th><a href="https://octoeverywhere.com/?source=kiauh_readme"><img src="https://octoeverywhere.com/img/logo.svg" alt="OctoEverywhere Logo" height="64"></a></th>
<th></th>
</tr>
<tr>
<th>by <a href="https://github.com/Clon1998">Patrick Schmidt</a></th>
<th>by <a href="https://github.com/QuinnDamerell">Quinn Damerell</a></th>
<th></th>
</tr>
</table>
<hr>
<h2 align="center">✨ Credits ✨</h2>
* A big thank you to [lixxbox](https://github.com/lixxbox) for that awesome KIAUH-Logo!
* Also, a big thank you to everyone who supported my work with a [Ko-fi](https://ko-fi.com/dw__0) !
* Last but not least: Thank you to all contributors and members of the Klipper Community who like and share this project!
<hr>
<h4 align="center">A special thank you to JetBrains for sponsoring this project with their incredible software!</h4>
<p align="center">
<a href="https://www.jetbrains.com/community/opensource/#support" target="_blank">
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" alt="JetBrains Logo (Main) logo." height="128">
</a>
</p>

264
docs/changelog.md Normal file
View File

@@ -0,0 +1,264 @@
## Changelog
This document covers possible important changes to KIAUH.
### 2023-06-17
KIAUH has now added support for installing Mobileraker's companion!
Mobileraker is a free and Open Source Android and iOS App for Klipper, utilizing the Moonraker API, allowing you
to control your printer. Thank you to [Clon1998](https://github.com/Clon1998) for adding this feature!
### 2023-02-03
The installer for MJPG-Streamer got replaced by crowsnest. It is an improved webcam service, utilizing ustreamer.
Please have a look here for additional info about crowsnest and how to configure it: https://github.com/mainsail-crew/crowsnest \
It's unsure if the previous MJPG-Streamer installer will be updated and make its way back into KIAUH.
A big thanks to [KwadFan](https://github.com/KwadFan) for writing the crowsnest implementation.
### 2022-10-31
Some functions got updated, though not all of them.
The following functions are still currently unavailable:
- Installation of: MJPG-Streamer
- All backup functions and the Log-Upload
### 2022-10-20
KIAUH has now reached major version 5 !
Recently Moonraker introduced some changes which makes it necessary to change the folder structure of printer setups.
If you are interested in the details, check out this PR: https://github.com/Arksine/moonraker/pull/491 \
Although Moonraker has some mechanics available to migrate existing setups to the new file structure with the use of symlinks, fresh and clean installs
should be considered.
The version jump of KIAUH to v5 is a breaking change due to those major changes! That means v4 and v5 are not compatible with each other!
This is also the reason why you will currently be greeted by a yellow notification in the main menu of KIAUH leading to this changelog.
I decided to disable a few functions of the script and focus on releasing the required changes to the core components of this script.
I will work on updating the other parts of the script piece by piece during the next days/weeks.
So I am already sorry in advance if one of your desired components you wanted to install or use temporarily cannot be installed or used right now.
The following functions are currently unavailable:
- Installation of: KlipperScreen, Obico, Octoprint, MJPG-Streamer, Telegram Bot and PrettyGCode
- All backup functions and the Log-Upload
**So what is working?**\
Installation of Klipper, Moonraker, Mainsail and Fluidd. Both, single and multi-instance setups work!\
As already said, the rest will follow in the near future. Updating and removal of already installed components should continue to work.
**What was removed?**\
The option to change Klippers configuration directory got removed. From now on it will not be possible anymore to change
the configuration directory from within KIAUH and the new filestructure is enforced.
**What if I don't have an existing Klipper/Moonraker install right now?**\
Nothing important to think about, install Klipper and Moonraker. KIAUH will install both of them with the new filestructure.
**What if I have an existing Klipper/Moonraker install?**\
First of all: Backups! Please copy all of your config files and the Moonraker database (it is a hidden folder, usually `~/.moonraker_database`) to a safe location.
After that, uninstall Klipper and Moonraker with KIAUH. You can then proceed and re-install both of them with KIAUH again. It is important that you are on KIAUH v5 for that!
Once everything is installed again, you need to manually copy your configuration files from the old `~/klipper_config` folder to the new `~/printer_data/config` folder.
Previous, by Moonraker created symlinks to folder of the old filestructure will not work anymore, you need to move the files to their new location now!
Do the same with the two files inside of `~/.moonraker_database`. Move/copy them into `~/printer_data/database`. If `~/printer_data/database` is already populated with a `data.mdb` and `lock.mdb`
delete them or simply overwrite them. Nothing should be lost as those should be empty database files. Anyway, you made backups, right?
You can now proceed and restart Moonraker. Either from within Mainsail or Fluidd, or use SSH and execute `sudo systemctl restart moonraker`.
If everything went smooth, you should be good to go again. If you see some Moonraker warnings about deprecated options in the `moonraker.conf`, go ahead and resolve them.
I will not cover them in detail here. A good source is the Moonraker documentation: https://moonraker.readthedocs.io/en/latest/configuration/
**What if I have an existing Klipper/Moonraker multi-instance install?**\
Pretty much the same steps that are required for single instance installs apply to multi-instance setups. So please go ahead and read the previous paragraph if you didn't already.
Make backups of everything first. Then remove and install the desired amount of Klipper and Moonraker instances again.
Now you need to move all config and database files to their new locations.\
Example with an instance called `printer_1`:\
The config files go from `~/klipper_config/printer_1` to `~/printer_1_data/config`.
The database files go from `~/.moonraker_database_1` to `~/printer_1_data/database`.
Now restart all Moonraker services. You can restart all of them at once if you launch KIAUH, and in the main menu type `restart moonraker` and hit Enter.
I hope I have covered the most important things. In case you need further support, the official Klipper Discord is a good place to ask for help.
### 2022-08-15
Support for "Obico for Klipper" was added! Huge thanks to [kennethjiang](https://github.com/kennethjiang) for helping me with the implementation!
### 2022-05-29
KIAUH has now reached major version 4 !
* feat: Klipper can be installed under Python3 (still considered as experimental)
* feat: Klipper can be installed from custom repositories / inofficial forks
* feat: Custom instance name for multi instance installations of Klipper
* Any other multi instance will share the same name given to the corresponding Klipper instance
* E.g. klipper-voron2 -> moonraker-voron2 -> moonraker-telegram-bot-voron2
* feat: Option to allow installation of / updating to unstable Mainsail and Fluidd versions
* by default only stable versions get installed/updated
* feat: Multi-Instance OctoPrint installations now each have their own virtual python environment
* allows independent installation of plugins for each instance
* feat: Implementing the use of shellcheck during development
* feat: Implementing a simple logging mechanic
* feat: Log-upload function now also allows uploading other logfiles (kiauh.log, webcamd.log etc.)
* feat: added several new help dialogs which try to explain various functions
* fix: During Klipper installation, checks for group membership of `tty` and `dialout` are made
* refactor: rework of the settings menu for better control the new KIAUH features
* refactor: Support for DWC and DWC-for-Klipper has been removed
* refactor: The backup before update settings were moved to the KIAUH settings menu
* refactor: Switch branch function has been removed (was replaced by the custom Klipper repo feature)
* refactor: The update manager sections for Mainsail, Fluidd and KlipperScreen were removed from the moonraker.conf template
* They will now be individually added during installation of the corresponding interface
* refactor: The rollback function was reworked and now also allows rollbacks of Moonraker
* It now takes numerical inputs and reverts the corresponding repository by the given amount instead
* KIAUH does not save previous states to its config anymore like it did with the previous approach
### 2022-01-29
* Starting from the 28th of January, Moonraker can make use of PackageKit and PolicyKit.\
More details on that can be found [here](
https://github.com/Arksine/moonraker/issues/349) and [here](https://github.com/Arksine/moonraker/pull/346)
* KIAUH will install Moonrakers PolicyKit rules by default when __installing__ Moonraker
* KIAUH will also install Moonrakers PolicyKit rules when __updating__ Moonraker __via KIAUH__ as of now
### 2021-12-30
* Updated the doc for the usage of the [G-Code Shell Command Extension](docs/gcode_shell_command.md)
* It became apparent, that some user groups are missing on some systems. A missing video group \
membership for example caused issues when installing mjpg-streamer while not using the default pi user. \
Other issues could occur when trying to flash an MCU on Debian or Ubuntu distributions where a user might not be part
of the dialout group by default. A check for the tty group is also done. The tty group is needed for setting
up a linux MCU (currently not yet supported by KIAUH).
* There is an issue when trying to install Mainsail or Fluidd on Ubuntu 21.10. Permissions on that distro seem to have seen a rework
in comparison to 20.04 and users will be greeted with an "Error 403 - Permission denied" message after installing one of Klippers webinterfaces.
I still have to figure out a viable solution for that.
### 2021-09-28
* New Feature! Added an installer for the Telegram Bot for Moonraker by [nlef](https://github.com/nlef).
Checkout his project! Remember to report all issues and/or bugs regarding that project in its corresponding repo and not here 😛.\
You can find it here: https://github.com/nlef/moonraker-telegram-bot
### 2021-09-24
* The flashing function got adjusted a bit. It is now possible to also flash controllers which are connected over UART and thus accessible via `/dev/ttyAMA0`. You now have to select a connection methop prior flashing which is either USB or UART.
* Due to several requests over time I have now created a Ko-fi account for those who want to support this project and my work with a small donation. Many thanks in advance to all future donors. You can support me on Ko-fi with this link: https://ko-fi.com/th33xitus
* As usual, if you find any bugs or issues please report them. I tested the little rework i did with the hardware i have available and haven't encountered any malfunctions of flashing them yet.
### 2021-08-10
* KIAUH now supports the installation of the "PrettyGCode for Klipper" GCode-Viewer created by [Kragrathea](https://github.com/Kragrathea)! Installation, updating and removal are possible with KIAUH. For more details to this cool piece of software, please have a look here: https://github.com/Kragrathea/pgcode
### 2021-07-10
* The NGINX configuration files got updated to be in sync with MainsailOS and FluiddPi. Issues with the NGINX service not starting up due to wrong configuration should be resolved now. To get the updated configuration files, please remove Moonraker and Mainsail / Fluidd with KIAUH first and then re-install it. An automated file check for those configuration files might follow in the future which then automates updating those files if there were important changes.
* The default `moonraker.conf` was updated to reflect the recent changes to the update manager section. The update channel is set to `dev`.
### 2021-06-29
* KIAUH will now patch the new `log_path` to existing moonraker.conf files when updating Moonraker and the entry is missing. Before that, it was necessary that the user provided that path manually to make Fluidd display the logfiles in its interface. This issue should be resolved now.
### 2021-06-15
* Moonraker introduced an optional `log_path` which clients can make use of to show log files located in that folder to their users. More info here: https://github.com/Arksine/moonraker/commit/829b3a4ee80579af35dd64a37ccc092a1f67682a \
Client developers agreed upon using `~/klipper_logs` as the new default log path.\
That means, from now on, Klipper and Moonraker services installed with KIAUH will place their logfiles in that mentioned folder.
* Additionally, KIAUH will now detect Klipper and Moonraker systemd services that still use the old default location of `/tmp/<service>.log` and will update them next time the user updates Klipper and/or Moonraker with the KIAUH update function.
* Additional symlinks for the following logfiles will get created along those update procedures to make them accessible through the webinterface once its supported:
- webcamd.log
- mainsail-access.log
- mainsail-error.log
- fluidd-access.log
- fluidd-error.log
* For MainsailOS and FluiddPi users:\
MainsailOS and FluiddPi will switch the shipped Klipper service from SysVinit to systemd probably with their next release. KIAUH can already help migrate older MainsailOS (0.4.0 and below) and FluiddPi (v1.13.0) releases to match their new service-, file- and folder-structure so you don't have to re-flash the SD-Card of your Raspberry Pi.\
In detail here is what is going to happen when you use the new "CustomPiOS Migration Helper" from the Advanced Menu\
`(Main Menu -> 4 -> Enter -> 10 -> Enter)` in a short summary:
* The Klipper SysVinit service will get replaced by a Klipper systemd service
* Klipper and Moonraker will use the new log-directory `~/klipper_logs`
* The webcamd service gets updated
* The webcamd script gets updated and moved from `/root/bin/webcamd` to `/usr/local/bin/webcamd`
* The NGINX `upstreams.conf` gets updated to be able to configure up to 4 webcams
* The `mainsail.txt` / `fluiddpi.txt` gets moved from `/boot` to `~/klipper_config` and renamed to `webcam.txt`
* Symlinks for the webcamd.log and various NGINX logs get created in `~/klipper_config`
* Configuration files for Klipper, Moonraker and webcamd get added to `/etc/logrotate.d`
* If they still exist, two lines will be removed from the mainsail.cfg or client_macros.cfg macro configurations:\
`SAVE_GCODE_STATE NAME=PAUSE_state` and `RESTORE_GCODE_STATE NAME=PAUSE_state`
* **Please note:**\
The "CustomPiOS Migration Helper" is intended to only work on "vanilla" MainsailOS and FluiddPi systems. Do not try to migrate a modified MainsailOS or FluiddPi system (for example if you already used KIAUH to re-install services or to set up a multi-instance installation for Klipper / Moonraker). This won't work.
### 2021-01-31
* **This is a big one... KIAUH v3.0 is out.**\
With this update you can now install multiple instances of Klipper, Moonraker, Duet Web Control or Octoprint on the same Pi. This was quite a big rework of the whole script. So bugs can appear but with the help of some testers, i think there shouldn't be any critical ones anymore. In this regards thanks to @lixxbox and @zellneralex for testing.
* Important changes to how installations are set up now: All components get installed as systemd services. Installation via init.d was dropped completely! This shouldn't affect you at all, since the common linux distributions like RaspberryPi OS or custom distributions like MainsailOS, FluiddPi or OctoPi support both ways of installing services. I just wanted to mention it here.
* Now with KIAUH v3.0 and multi-instance installation capabilities, there are some things to point out. You will now need to tell KIAUH where your printers configurations are located when installing Klipper for the first time. Even though it is not recommended, you can change this location with the help of KIAUH and rewrite Klipper and Moonraker to use the new location.
* When setting up a multi-instance system, the folder structure will only change slightly. The goal was to keep it as compatible as possible with the custom distributions like mainsailOS and FluiddPi. This should help converting a single-instance setup of mainsailOS/FluiddPi to a multi-instance setup in no time, but keeping single-instance backwards compatibility if needed at a later point in time.
* The folder structure is as follows when setting up multi-instances:\
Each printer instance will get its own folder within your configuration location. The decision to this specific structure was made to make it as painless and easy as possible to convert to a multi-instance setup.
Here is an example:
```shell
/home/<username>
└── klipper_config
├── printer_1
│ ├── printer.cfg
│ └── moonraker.conf
├── printer_2
│ ├── printer.cfg
│ └── moonraker.conf
└── printer_n
├── printer.cfg
└── moonraker.conf
```
* Also when setting up multi-instances of each service, the name of each service slightly changes.
Each service gets its corresponding instance added to the service filename.
**This only applies to multi-instances! Single instance installations with KIAUH will keep their original names!**
Corresponding to the filetree example from above that would mean:
```
Klipper services:
--> klipper-1.service
--> klipper-2.service
--> klipper-n.service
Moonraker services:
--> moonraker-1.service
--> moonraker-2.service
--> moonraker-n.service
```
* The same service file rules from above apply to OctoPrint even though only Klipper and Moonraker are shown in this example.
* You can start, stop and restart all Klipper, Moonraker and OctoPrint instances from the KIAUH main menu. For doing this, just type "stop klipper", "start moonraker", "restart octoprint" and so on.
* KIAUH v3.0 relocated its ini-file. It is now a hidden file in the users home-directory calles `.kiauh.ini`. This has the benefit of keeping all values in that file between possible re-installations of KIAUH. Otherwise that file would be lost.
* The option of adding more trusted clients to the moonraker.conf file was dropped. Since you can edit this file right inside of Mainsail or Fluidd, only some basic entries are made which get you running.
* I bet i have missed mentioning other stuff as well because it took me quite some time to re-write many functions. So i just hope you like the new version 😄
### 2020-11-28
* KIAUH now supports the installation, update and removal of [KlipperScreen](https://github.com/jordanruthe/KlipperScreen). This feature was was provided by [jordanruthe](https://github.com/jordanruthe)! Thank you!
### 2020-11-18
* Some changes to Fluidd caused a little rework on how KIAUH will install/update Fluidd from now on. Please see the [fluidd v1.0.0-rc0 release notes](https://github.com/cadriel/fluidd/releases/tag/v1.0.0-rc.0) for further information about what modifications to the moonraker.conf file exactly had to be done. In a nutshell, KIAUH will now always patch the required entries to the moonraker.conf if not already there.
### 2020-10-30:
* The user can now choose to install Klipper as a systemd service.
* The Shell Command extension and `shell_command.py` got renamed to G-Code Shell Command extension and `gcode_shell_command.py`. In case the [pending PR](https://github.com/KevinOConnor/klipper/pull/2173) will be merged in the future, this was an early attempt to dodge possible incompatibilities. The [G-Code Shell Command docs](gcode_shell_command.md) has been updated accordingly.
* The way how KIAUH interacts and writes to the users printer.cfg got changed. Usually KIAUH wrote everything directly into the printer.cfg. The way it will work from now on is, that a new file called `kiauh.cfg` will be created if there is something that needs to be written to the printer.cfg and everything gets written to `kiauh.cfg` instead. The only thing which then gets written to the users printer.cfg is `[include kiauh.cfg]`. This line will be located at the very top of the existing printer.cfg with a little comment as a note. The user can then decide to either keep the `kiauh.cfg` or take its content, places it into the printer.cfg directly and remove the `[include kiauh.cfg]`.
* The `mainsail_macros.cfg` got renamed to `webui_macros.cfg`. Since Mainsail and Fluidd both use the same kind of pause, cancel and resume macros, a more generic name was chosen for the file containing the example macros one can choose to install when installing those webinterfaces.
### 2020-10-10:
* Support for changing the Klipper branch to the moonraker-dev branch from @Arksine has been dropped. Support for Moonraker has been merged into Klipper mainline a long time ago.
* A new function is available from the main menu. You can now upload your log files to http://paste.c-net.org/ to share them for debugging purposes.
### 2020-10-06:
* Fluidd, a new Klipper interface got added to the list of available installers. At the same time some installation routines have changed or have seen some rework. Changes were made to the installation of NGINX configurations. A method was introduced to change the listen port of a webinterface configuration if there is already another webinterface listening on the default port (80).
* At the moment, the Moonraker installer no longer asks you whether you want to install a web interface too. For now you therefore have to install them with their respective installers. Please report any bugs or issues you encounter.
### 2020-09-17:
* The dev-2.0 branch will be abandoned as of today. If you did a checkout to that branch in the past, you have to checkout back to master to receive updates.
### 2020-09-12:
* The old [dwc2-for-klipper](https://github.com/Stephan3/dwc2-for-klipper) won't be supported anymore!\
The is a new, fully rewritten project available: [dwc2-for-klipper-socket](https://github.com/Stephan3/dwc2-for-klipper-socket).\
The installer of this script also got rewritten to make use of that new project. You will not be able to install or remove the old [dwc2-for-klipper](https://github.com/Stephan3/dwc2-for-klipper) with KIAUH anymore if you updated KIAUH to the newest version.

View File

@@ -0,0 +1,74 @@
# G-Code Shell Command Extension
### Creator of this extension is [Arksine](https://github.com/Arksine).
This is a brief explanation of how to use the shell command extension for Klipper, which you can install with KIAUH.
After installing the extension you can execute linux commands or even scripts from within Klipper with custom commands defined in your printer.cfg.
#### How to configure a shell command:
```shell
# Runs a linux command or script from within klipper. Note that sudo commands
# that require password authentication are disallowed. All executable scripts
# should include a shebang.
# [gcode_shell_command my_shell_cmd]
#command:
# The linux shell command/script to be executed. This parameter must be
# provided
#timeout: 2.
# The timeout in seconds until the command is forcably terminated. Default
# is 2 seconds.
#verbose: True
# If enabled, the command's output will be forwarded to the terminal. Its
# recommended to set this to false for commands that my run in quick
# succession. Default is True.
```
Once you have set up a shell command with the given parameters from above in your printer.cfg you can run the command as follows:
`RUN_SHELL_COMMAND CMD=name`
Example:
```
[gcode_shell_command hello_world]
command: echo hello world
timeout: 2.
verbose: True
```
Execute with:
`RUN_SHELL_COMMAND CMD=hello_world`
### Passing parameters:
As of commit [f231fa9](https://github.com/dw-0/kiauh/commit/f231fa9c69191f23277b4e3319f6b675bfa0ee42) it is also possible to pass optional parameters to a `gcode_shell_command`.
The following short example shows storing the extruder temperature into a variable, passing that value with a parameter to a `gcode_shell_command`, which then,
once the gcode_macro runs and the gcode_shell_command gets called, executes the `script.sh`. The script then echoes a message to the console (if `verbose: True`)
and writes the value of the parameter into a textfile called `test.txt` located in the home directory.
Content of the `gcode_shell_command` and the `gcode_macro`:
```
[gcode_shell_command print_to_file]
command: sh /home/pi/klipper_config/script.sh
timeout: 30.
verbose: True
[gcode_macro GET_TEMP]
gcode:
{% set temp = printer.extruder.temperature %}
{ action_respond_info("%s" % (temp)) }
RUN_SHELL_COMMAND CMD=print_to_file PARAMS={temp}
```
Content of `script.sh`:
```shell
#!/bin/sh
echo "temp is: $1"
echo "$1" >> "${HOME}/test.txt"
```
## Warning
This extension may have a high potential for abuse if not used carefully! Also, depending on the command you execute, high system loads may occur and can cause system instabilities.
Use this extension at your own risk and only if you know what you are doing!

20
kiauh.cfg.example Normal file
View File

@@ -0,0 +1,20 @@
[kiauh]
backup_before_update: False
[klipper]
repository_url: https://github.com/Klipper3d/klipper
branch: master
method: https
[moonraker]
repository_url: https://github.com/Arksine/moonraker
branch: master
method: https
[mainsail]
port: 80
unstable_releases: False
[fluidd]
port: 80
unstable_releases: False

View File

@@ -1 +0,0 @@
backup_before_update=false

15
kiauh.py Normal file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 kiauh.main import main
if __name__ == "__main__":
main()

479
kiauh.sh Normal file → Executable file
View File

@@ -1,387 +1,108 @@
#!/bin/bash #!/usr/bin/env bash
clear
#=======================================================================#
# 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 #
#=======================================================================#
set -e set -e
clear
### set some variables function main() {
ERROR_MSG="" local python_command
green="\e[92m" local entrypoint
yellow="\e[93m"
red="\e[91m"
cyan="\e[96m"
default="\e[39m"
### set some messages if command -v python3 &>/dev/null; then
warn_msg(){ python_command="python3"
echo -e "${red}<!!!!> $1${default}" elif command -v python &>/dev/null; then
} python_command="python"
status_msg(){
echo; echo -e "${yellow}###### $1${default}"
}
ok_msg(){
echo -e "${green}>>>>>> $1${default}"
}
title_msg(){
echo -e "${cyan}$1${default}"
}
get_date(){
current_date=`date +"%Y-%m-%d_%H%M%S"`
}
### sourcing all additional scripts
for script in ${HOME}/kiauh/scripts/*; do . $script; done
### set important directories
#klipper
KLIPPER_DIR=${HOME}/klipper
KLIPPY_ENV_DIR=${HOME}/klippy-env
KLIPPER_SERVICE1=/etc/init.d/klipper
KLIPPER_SERVICE2=/etc/default/klipper
#dwc2
DWC2FK_DIR=${HOME}/dwc2-for-klipper
DWC2_DIR=${HOME}/sdcard/dwc2
WEB_DWC2=${HOME}/klipper/klippy/extras/web_dwc2.py
TORNADO_DIR1=${HOME}/klippy-env/lib/python2.7/site-packages/tornado
TORNADO_DIR2=${HOME}/klippy-env/lib/python2.7/site-packages/tornado-5.1.1.dist-info
#mainsail/moonraker
MAINSAIL_DIR=${HOME}/mainsail
MAINSAIL_SERVICE1=/etc/init.d/moonraker
MAINSAIL_SERVICE2=/etc/default/moonraker
#misc
BACKUP_DIR=${HOME}/kiauh-backups
PRINTER_CFG=${HOME}/printer.cfg
### set github repos
KLIPPER_REPO=https://github.com/KevinOConnor/klipper.git
ARKSINE_REPO=https://github.com/Arksine/klipper.git
DMBUTYUGIN_REPO=https://github.com/dmbutyugin/klipper.git
DWC2FK_REPO=https://github.com/Stephan3/dwc2-for-klipper.git
#branches
BRANCH_MOONRAKER=Arksine/work-web_server-20200131
BRANCH_DEV_MOONRAKER=Arksine/dev-moonraker-testing
BRANCH_SCURVE_SMOOTHING=dmbutyugin/scurve-smoothing
BRANCH_SCURVE_SHAPING=dmbutyugin/scurve-shaping
print_error_msg(){
if [[ "$ERROR_MSG" != "" ]]; then
echo -e "${red}"
echo -e "#########################################################"
echo -e "$ERROR_MSG "
echo -e "#########################################################"
echo -e "${default}"
fi
}
main_menu(){
print_header
print_error_msg && ERROR_MSG=""
#check install status
klipper_status
dwc2_status
mainsail_status
print_branch
main_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
0)
clear
print_header
ERROR_MSG=" Sorry this function is not implemented yet!"
print_error_msg && ERROR_MSG=""
main_ui;;
1)
clear
install_menu
break;;
2)
clear
update_menu
break;;
3)
clear
remove_menu
break;;
4)
clear
advanced_menu
break;;
5)
clear
backup_menu
break;;
Q|q)
exit -1;;
*)
clear
print_header
ERROR_MSG=" Unknown command '$action'"
print_error_msg && ERROR_MSG=""
main_ui;;
esac
done
clear; main_menu
}
install_menu(){
print_header
install_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
1)
clear
print_header
install_klipper
print_error_msg && ERROR_MSG=""
install_ui;;
2)
clear
print_header
dwc2_install_routine
print_error_msg && ERROR_MSG=""
install_ui;;
3)
clear
print_header
mainsail_install_routine
print_error_msg && ERROR_MSG=""
install_ui;;
Q|q)
clear; main_menu; break;;
*)
clear
print_header
ERROR_MSG=" Unknown command '$action'"
print_error_msg && ERROR_MSG=""
install_ui;;
esac
done
install_menu
}
update_menu(){
print_header
read_bb4u_stat
#compare versions
ui_print_versions
update_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
0)
clear
print_header
toggle_backups
print_error_msg && ERROR_MSG=""
update_ui;;
1)
clear
print_header
update_klipper && ui_print_versions
print_error_msg && ERROR_MSG=""
update_ui;;
2)
clear
print_header
update_dwc2fk && ui_print_versions
print_error_msg && ERROR_MSG=""
update_ui;;
3)
clear
print_header
update_dwc2 && ui_print_versions
print_error_msg && ERROR_MSG=""
update_ui;;
4)
clear
print_header
update_mainsail && ui_print_versions
print_error_msg && ERROR_MSG=""
update_ui;;
Q|q)
clear; main_menu; break;;
*)
clear
print_header
ERROR_MSG=" Unknown command '$action'"
print_error_msg && ERROR_MSG=""
ui_print_versions
update_ui;;
esac
done
update_menu
}
remove_menu(){
print_header
remove_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
1)
clear
print_header
remove_klipper
print_error_msg && ERROR_MSG=""
remove_ui;;
2)
clear
print_header
remove_dwc2
print_error_msg && ERROR_MSG=""
remove_ui;;
3)
clear
print_header
remove_mainsail
print_error_msg && ERROR_MSG=""
remove_ui;;
Q|q)
clear; main_menu; break;;
*)
clear
print_header
ERROR_MSG=" Unknown command '$action'"
print_error_msg && ERROR_MSG=""
remove_ui;;
esac
done
remove_menu
}
advanced_menu(){
print_header
print_error_msg && ERROR_MSG=""
advanced_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
1)
clear
switch_menu
print_error_msg && ERROR_MSG=""
advanced_ui;;
2)
clear
print_header
build_fw
print_error_msg && ERROR_MSG=""
advanced_ui;;
3)
clear
print_header
flash_routine
print_error_msg && ERROR_MSG=""
advanced_ui;;
4)
clear
print_header
get_usb_id
print_error_msg && ERROR_MSG=""
advanced_ui;;
5)
clear
print_header
get_usb_id && write_printer_id
print_error_msg && ERROR_MSG=""
advanced_ui;;
6)
clear
print_header
create_dwc2fk_cfg
print_error_msg && ERROR_MSG=""
advanced_ui;;
Q|q)
clear; main_menu; break;;
*)
clear
print_header
ERROR_MSG=" Unknown command '$action'"
print_error_msg && ERROR_MSG=""
advanced_ui;;
esac
done
advanced_menu
}
switch_menu(){
print_header
if [ -d $KLIPPER_DIR ]; then
read_branch
print_error_msg && ERROR_MSG=""
switch_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
1)
clear
print_header
switch_to_origin
read_branch
print_error_msg && ERROR_MSG=""
switch_ui;;
2)
clear
print_header
switch_to_scurve_shaping
read_branch
print_error_msg && ERROR_MSG=""
switch_ui;;
3)
clear
print_header
switch_to_scurve_smoothing
read_branch
print_error_msg && ERROR_MSG=""
switch_ui;;
4)
clear
print_header
switch_to_moonraker
read_branch
print_error_msg && ERROR_MSG=""
switch_ui;;
5)
clear
print_header
switch_to_dev_moonraker
read_branch
print_error_msg && ERROR_MSG=""
switch_ui;;
Q|q)
clear; advanced_menu; break;;
esac
done
else else
ERROR_MSG=" No klipper directory found! Download klipper first!" echo "Python is not installed. Please install Python and try again."
exit 1
fi fi
entrypoint=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
${python_command} "${entrypoint}/kiauh.py"
} }
backup_menu(){ main
print_header
print_error_msg && ERROR_MSG=""
backup_ui
while true; do
read -p "Perform action: " action; echo
case "$action" in
1)
clear
print_header
#function goes here
print_error_msg && ERROR_MSG=""
backup_ui;;
Q|q)
clear; main_menu; break;;
*)
clear
print_header
ERROR_MSG=" Unknown command '$action'"
print_error_msg && ERROR_MSG=""
backup_ui;;
esac
done
backup_menu
}
check_euid #### sourcing all additional scripts
main_menu #KIAUH_SRCDIR="$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")"
#for script in "${KIAUH_SRCDIR}/scripts/"*.sh; do . "${script}"; done
#for script in "${KIAUH_SRCDIR}/scripts/ui/"*.sh; do . "${script}"; done
#
##===================================================#
##=================== UPDATE KIAUH ==================#
##===================================================#
#
#function update_kiauh() {
# status_msg "Updating KIAUH ..."
#
# cd "${KIAUH_SRCDIR}"
# git reset --hard && git pull
#
# ok_msg "Update complete! Please restart KIAUH."
# exit 0
#}
#
##===================================================#
##=================== KIAUH STATUS ==================#
##===================================================#
#
#function kiauh_update_avail() {
# [[ ! -d "${KIAUH_SRCDIR}/.git" ]] && return
# local origin head
#
# cd "${KIAUH_SRCDIR}"
#
# ### abort if not on master branch
# ! git branch -a | grep -q "\* master" && return
#
# ### compare commit hash
# git fetch -q
# origin=$(git rev-parse --short=8 origin/master)
# head=$(git rev-parse --short=8 HEAD)
#
# if [[ ${origin} != "${head}" ]]; then
# echo "true"
# fi
#}
#
#function kiauh_update_dialog() {
# [[ ! $(kiauh_update_avail) == "true" ]] && return
# top_border
# echo -e "|${green} New KIAUH update available! ${white}|"
# hr
# echo -e "|${green} View Changelog: https://git.io/JnmlX ${white}|"
# blank_line
# echo -e "|${yellow} It is recommended to keep KIAUH up to date. Updates ${white}|"
# echo -e "|${yellow} usually contain bugfixes, important changes or new ${white}|"
# echo -e "|${yellow} features. Please consider updating! ${white}|"
# bottom_border
#
# local yn
# read -p "${cyan}###### Do you want to update now? (Y/n):${white} " yn
# while true; do
# case "${yn}" in
# Y|y|Yes|yes|"")
# do_action "update_kiauh"
# break;;
# N|n|No|no)
# break;;
# *)
# deny_action "kiauh_update_dialog";;
# esac
# done
#}
#
#check_euid
#init_logfile
#set_globals
#kiauh_update_dialog
#main_menu

19
kiauh/__init__.py Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
KIAUH_CFG = PROJECT_ROOT.joinpath("kiauh.cfg")
APPLICATION_ROOT = Path(__file__).resolve().parent
sys.path.append(str(APPLICATION_ROOT))

View File

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
MODULE_PATH = Path(__file__).resolve().parent
KLIPPER_DIR = Path.home().joinpath("klipper")
KLIPPER_ENV_DIR = Path.home().joinpath("klippy-env")
KLIPPER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("klipper-backups")
KLIPPER_REQUIREMENTS_TXT = KLIPPER_DIR.joinpath("scripts/klippy-requirements.txt")
DEFAULT_KLIPPER_REPO_URL = "https://github.com/Klipper3D/klipper"
EXIT_KLIPPER_SETUP = "Exiting Klipper setup ..."

View File

@@ -0,0 +1 @@
KLIPPER_ARGS="%KLIPPER_DIR%/klippy/klippy.py %CFG% -I %SERIAL% -l %LOG% -a %UDS%"

View File

@@ -0,0 +1,18 @@
[Unit]
Description=Klipper 3D Printer Firmware SV1
Documentation=https://www.klipper3d.org/
After=network-online.target
Wants=udev.target
[Install]
WantedBy=multi-user.target
[Service]
Type=simple
User=%USER%
RemainAfterExit=yes
WorkingDirectory=%KLIPPER_DIR%
EnvironmentFile=%ENV_FILE%
ExecStart=%ENV%/bin/python $KLIPPER_ARGS
Restart=always
RestartSec=10

View File

@@ -0,0 +1,11 @@
[mcu]
serial: /dev/serial/by-id/<your-mcu-id>
[virtual_sdcard]
path: %GCODES_DIR%
on_error_gcode: CANCEL_PRINT
[printer]
kinematics: none
max_velocity: 1000
max_accel: 1000

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 subprocess
from pathlib import Path
from typing import List
from components.klipper import KLIPPER_DIR, KLIPPER_ENV_DIR, MODULE_PATH
from core.instance_manager.base_instance import BaseInstance
from utils.constants import SYSTEMD
from utils.logger import Logger
# noinspection PyMethodMayBeStatic
class Klipper(BaseInstance):
@classmethod
def blacklist(cls) -> List[str]:
return ["None", "mcu"]
def __init__(self, suffix: str = ""):
super().__init__(instance_type=self, suffix=suffix)
self.klipper_dir: Path = KLIPPER_DIR
self.env_dir: Path = KLIPPER_ENV_DIR
self._cfg_file = self.cfg_dir.joinpath("printer.cfg")
self._log = self.log_dir.joinpath("klippy.log")
self._serial = self.comms_dir.joinpath("klippy.serial")
self._uds = self.comms_dir.joinpath("klippy.sock")
@property
def cfg_file(self) -> Path:
return self._cfg_file
@property
def log(self) -> Path:
return self._log
@property
def serial(self) -> Path:
return self._serial
@property
def uds(self) -> Path:
return self._uds
def create(self) -> None:
Logger.print_status("Creating new Klipper Instance ...")
service_template_path = MODULE_PATH.joinpath("assets/klipper.service")
service_file_name = self.get_service_file_name(extension=True)
service_file_target = SYSTEMD.joinpath(service_file_name)
env_template_file_path = MODULE_PATH.joinpath("assets/klipper.env")
env_file_target = self.sysd_dir.joinpath("klipper.env")
try:
self.create_folders()
self.write_service_file(
service_template_path, service_file_target, env_file_target
)
self.write_env_file(env_template_file_path, env_file_target)
except subprocess.CalledProcessError as e:
Logger.print_error(
f"Error creating service file {service_file_target}: {e}"
)
raise
except OSError as e:
Logger.print_error(f"Error creating env file {env_file_target}: {e}")
raise
def delete(self) -> None:
service_file = self.get_service_file_name(extension=True)
service_file_path = self.get_service_file_path()
Logger.print_status(f"Deleting Klipper Instance: {service_file}")
try:
command = ["sudo", "rm", "-f", service_file_path]
subprocess.run(command, check=True)
Logger.print_ok(f"Service file deleted: {service_file_path}")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error deleting service file: {e}")
raise
def write_service_file(
self,
service_template_path: Path,
service_file_target: Path,
env_file_target: Path,
) -> None:
service_content = self._prep_service_file(
service_template_path, env_file_target
)
command = ["sudo", "tee", service_file_target]
subprocess.run(
command,
input=service_content.encode(),
stdout=subprocess.DEVNULL,
check=True,
)
Logger.print_ok(f"Service file created: {service_file_target}")
def write_env_file(
self, env_template_file_path: Path, env_file_target: Path
) -> None:
env_file_content = self._prep_env_file(env_template_file_path)
with open(env_file_target, "w") as env_file:
env_file.write(env_file_content)
Logger.print_ok(f"Env file created: {env_file_target}")
def _prep_service_file(
self, service_template_path: Path, env_file_path: Path
) -> str:
try:
with open(service_template_path, "r") as template_file:
template_content = template_file.read()
except FileNotFoundError:
Logger.print_error(
f"Unable to open {service_template_path} - File not found"
)
raise
service_content = template_content.replace("%USER%", self.user)
service_content = service_content.replace(
"%KLIPPER_DIR%", str(self.klipper_dir)
)
service_content = service_content.replace("%ENV%", str(self.env_dir))
service_content = service_content.replace("%ENV_FILE%", str(env_file_path))
return service_content
def _prep_env_file(self, env_template_file_path: Path) -> str:
try:
with open(env_template_file_path, "r") as env_file:
env_template_file_content = env_file.read()
except FileNotFoundError:
Logger.print_error(
f"Unable to open {env_template_file_path} - File not found"
)
raise
env_file_content = env_template_file_content.replace(
"%KLIPPER_DIR%", str(self.klipper_dir)
)
env_file_content = env_file_content.replace(
"%CFG%", f"{self.cfg_dir}/printer.cfg"
)
env_file_content = env_file_content.replace("%SERIAL%", str(self.serial))
env_file_content = env_file_content.replace("%LOG%", str(self.log))
env_file_content = env_file_content.replace("%UDS%", str(self.uds))
return env_file_content

View File

@@ -0,0 +1,136 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from typing import List
from core.instance_manager.base_instance import BaseInstance
from core.menus.base_menu import print_back_footer
from utils.constants import COLOR_GREEN, RESET_FORMAT, COLOR_YELLOW, COLOR_CYAN
def print_instance_overview(
instances: List[BaseInstance], show_index=False, show_select_all=False
):
headline = f"{COLOR_GREEN}The following Klipper instances were found:{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
|{headline:^64}|
|-------------------------------------------------------|
"""
)[1:]
if show_select_all:
select_all = f"{COLOR_YELLOW}a) Select all{RESET_FORMAT}"
dialog += f"| {select_all:<63}|\n"
dialog += "| |\n"
for i, s in enumerate(instances):
line = f"{COLOR_CYAN}{f'{i})' if show_index else ''} {s.get_service_file_name()}{RESET_FORMAT}"
dialog += f"| {line:<63}|\n"
print(dialog, end="")
print_back_footer()
def print_select_instance_count_dialog():
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}Setting up too many instances may crash your system.{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| Please select the number of Klipper instances to set |
| up. The number of Klipper instances will determine |
| the amount of printers you can run from this host. |
| |
| {line1:<63}|
| {line2:<63}|
"""
)[1:]
print(dialog, end="")
print_back_footer()
def print_select_custom_name_dialog():
line1 = f"{COLOR_YELLOW}INFO:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}Only alphanumeric characters are allowed!{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| You can now assign a custom name to each instance. |
| If skipped, each instance will get an index assigned |
| in ascending order, starting at index '1'. |
| |
| {line1:<63}|
| {line2:<63}|
"""
)[1:]
print(dialog, end="")
print_back_footer()
def print_missing_usergroup_dialog(missing_groups) -> None:
line1 = f"{COLOR_YELLOW}WARNING: Your current user is not in group:{RESET_FORMAT}"
line2 = f"{COLOR_CYAN}● tty{RESET_FORMAT}"
line3 = f"{COLOR_CYAN}● dialout{RESET_FORMAT}"
line4 = f"{COLOR_YELLOW}INFO:{RESET_FORMAT}"
line5 = f"{COLOR_YELLOW}Relog required for group assignments to take effect!{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| {line1:<63}|
"""
)[1:]
if "tty" in missing_groups:
dialog += f"| {line2:<63}|\n"
if "dialout" in missing_groups:
dialog += f"| {line3:<63}|\n"
dialog += textwrap.dedent(
f"""
| |
| It is possible that you won't be able to successfully |
| connect and/or flash the controller board without |
| your user being a member of that group. |
| If you want to add the current user to the group(s) |
| listed above, answer with 'Y'. Else skip with 'n'. |
| |
| {line4:<63}|
| {line5:<63}|
\\=======================================================/
"""
)[1:]
print(dialog, end="")
def print_update_warn_dialog() -> None:
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}Do NOT continue if there are ongoing prints running!{RESET_FORMAT}"
line3 = f"{COLOR_YELLOW}All Klipper instances will be restarted during the {RESET_FORMAT}"
line4 = f"{COLOR_YELLOW}update process and ongoing prints WILL FAIL.{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| {line1:<63}|
| {line2:<63}|
| {line3:<63}|
| {line4:<63}|
\\=======================================================/
"""
)[1:]
print(dialog, end="")

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import List, Union
from components.klipper import KLIPPER_DIR, KLIPPER_ENV_DIR
from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import print_instance_overview
from core.instance_manager.instance_manager import InstanceManager
from utils.filesystem_utils import remove_file
from utils.input_utils import get_selection_input
from utils.logger import Logger
def run_klipper_removal(
remove_service: bool,
remove_dir: bool,
remove_env: bool,
delete_logs: bool,
) -> None:
im = InstanceManager(Klipper)
if remove_service:
Logger.print_status("Removing Klipper instances ...")
if im.instances:
instances_to_remove = select_instances_to_remove(im.instances)
remove_instances(im, instances_to_remove)
else:
Logger.print_info("No Klipper Services installed! Skipped ...")
if (remove_dir or remove_env) and im.instances:
Logger.print_warn("There are still other Klipper services installed!")
Logger.print_warn("Therefor the following parts cannot be removed:")
Logger.print_warn(
"""
● Klipper local repository
● Klipper Python environment
""",
False,
)
else:
if remove_dir:
Logger.print_status("Removing Klipper local repository ...")
remove_klipper_dir()
if remove_env:
Logger.print_status("Removing Klipper Python environment ...")
remove_klipper_env()
# delete klipper logs of all instances
if delete_logs:
Logger.print_status("Removing all Klipper logs ...")
delete_klipper_logs(im.instances)
def select_instances_to_remove(
instances: List[Klipper],
) -> Union[List[Klipper], None]:
print_instance_overview(instances, True, True)
options = [str(i) for i in range(len(instances))]
options.extend(["a", "A", "b", "B"])
selection = get_selection_input("Select Klipper instance to remove", options)
instances_to_remove = []
if selection == "b".lower():
return None
elif selection == "a".lower():
instances_to_remove.extend(instances)
else:
instance = instances[int(selection)]
instances_to_remove.append(instance)
return instances_to_remove
def remove_instances(
instance_manager: InstanceManager,
instance_list: List[Klipper],
) -> None:
for instance in instance_list:
Logger.print_status(f"Removing instance {instance.get_service_file_name()} ...")
instance_manager.current_instance = instance
instance_manager.stop_instance()
instance_manager.disable_instance()
instance_manager.delete_instance()
instance_manager.reload_daemon()
def remove_klipper_dir() -> None:
if not KLIPPER_DIR.exists():
Logger.print_info(f"'{KLIPPER_DIR}' does not exist. Skipped ...")
return
try:
shutil.rmtree(KLIPPER_DIR)
except OSError as e:
Logger.print_error(f"Unable to delete '{KLIPPER_DIR}':\n{e}")
def remove_klipper_env() -> None:
if not KLIPPER_ENV_DIR.exists():
Logger.print_info(f"'{KLIPPER_ENV_DIR}' does not exist. Skipped ...")
return
try:
shutil.rmtree(KLIPPER_ENV_DIR)
except OSError as e:
Logger.print_error(f"Unable to delete '{KLIPPER_ENV_DIR}':\n{e}")
def delete_klipper_logs(instances: List[Klipper]) -> None:
all_logfiles = []
for instance in instances:
all_logfiles = list(instance.log_dir.glob("klippy.log*"))
if not all_logfiles:
Logger.print_info("No Klipper logs found. Skipped ...")
return
for log in all_logfiles:
Logger.print_status(f"Remove '{log}'")
remove_file(log)

View File

@@ -0,0 +1,191 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 components.webui_client.client_utils import (
get_existing_client_config,
get_existing_clients,
)
from kiauh import KIAUH_CFG
from components.klipper import (
EXIT_KLIPPER_SETUP,
DEFAULT_KLIPPER_REPO_URL,
KLIPPER_DIR,
KLIPPER_ENV_DIR,
KLIPPER_REQUIREMENTS_TXT,
)
from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import print_update_warn_dialog
from components.klipper.klipper_utils import (
handle_disruptive_system_packages,
check_user_groups,
handle_to_multi_instance_conversion,
create_example_printer_cfg,
add_to_existing,
get_install_count,
init_name_scheme,
check_is_single_to_multi_conversion,
update_name_scheme,
handle_instance_naming,
backup_klipper_dir,
)
from components.moonraker.moonraker import Moonraker
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.instance_manager import InstanceManager
from core.repo_manager.repo_manager import RepoManager
from utils.input_utils import get_confirm
from utils.logger import Logger
from utils.system_utils import (
parse_packages_from_file,
create_python_venv,
install_python_requirements,
update_system_package_lists,
install_system_packages,
)
def install_klipper() -> None:
kl_im = InstanceManager(Klipper)
# ask to add new instances, if there are existing ones
if kl_im.instances and not add_to_existing():
Logger.print_status(EXIT_KLIPPER_SETUP)
return
install_count = get_install_count()
if install_count is None:
Logger.print_status(EXIT_KLIPPER_SETUP)
return
# create a dict of the size of the existing instances + install count
name_dict = {c: "" for c in range(len(kl_im.instances) + install_count)}
name_scheme = init_name_scheme(kl_im.instances, install_count)
mr_im = InstanceManager(Moonraker)
name_scheme = update_name_scheme(
name_scheme, name_dict, kl_im.instances, mr_im.instances
)
handle_instance_naming(name_dict, name_scheme)
create_example_cfg = get_confirm("Create example printer.cfg?")
try:
if not kl_im.instances:
setup_klipper_prerequesites()
count = 0
for name in name_dict:
if name_dict[name] in [n.suffix for n in kl_im.instances]:
continue
if check_is_single_to_multi_conversion(kl_im.instances):
handle_to_multi_instance_conversion(name_dict[name])
continue
count += 1
create_klipper_instance(name_dict[name], create_example_cfg)
if count == install_count:
break
kl_im.reload_daemon()
except Exception as e:
Logger.print_error(e)
Logger.print_error("Klipper installation failed!")
return
# step 4: check/handle conflicting packages/services
handle_disruptive_system_packages()
# step 5: check for required group membership
check_user_groups()
def setup_klipper_prerequesites() -> None:
cm = ConfigManager(cfg_file=KIAUH_CFG)
repo = str(cm.get_value("klipper", "repository_url") or DEFAULT_KLIPPER_REPO_URL)
branch = str(cm.get_value("klipper", "branch") or "master")
repo_manager = RepoManager(
repo=repo,
branch=branch,
target_dir=KLIPPER_DIR,
)
repo_manager.clone_repo()
# install klipper dependencies and create python virtualenv
try:
install_klipper_packages(KLIPPER_DIR)
create_python_venv(KLIPPER_ENV_DIR)
install_python_requirements(KLIPPER_ENV_DIR, KLIPPER_REQUIREMENTS_TXT)
except Exception:
Logger.print_error("Error during installation of Klipper requirements!")
raise
def install_klipper_packages(klipper_dir: Path) -> None:
script = klipper_dir.joinpath("scripts/install-debian.sh")
packages = parse_packages_from_file(script)
packages = [pkg.replace("python-dev", "python3-dev") for pkg in packages]
packages.append("python3-venv")
# Add dfu-util for octopi-images
packages.append("dfu-util")
# Add dbus requirement for DietPi distro
if Path("/boot/dietpi/.version").exists():
packages.append("dbus")
update_system_package_lists(silent=False)
install_system_packages(packages)
def update_klipper() -> None:
print_update_warn_dialog()
if not get_confirm("Update Klipper now?"):
return
cm = ConfigManager(cfg_file=KIAUH_CFG)
if cm.get_value("kiauh", "backup_before_update"):
backup_klipper_dir()
instance_manager = InstanceManager(Klipper)
instance_manager.stop_all_instance()
repo = str(cm.get_value("klipper", "repository_url") or DEFAULT_KLIPPER_REPO_URL)
branch = str(cm.get_value("klipper", "branch") or "master")
repo_manager = RepoManager(
repo=repo,
branch=branch,
target_dir=KLIPPER_DIR,
)
repo_manager.pull_repo()
# install possible new system packages
install_klipper_packages(KLIPPER_DIR)
# install possible new python dependencies
install_python_requirements(KLIPPER_ENV_DIR, KLIPPER_REQUIREMENTS_TXT)
instance_manager.start_all_instance()
def create_klipper_instance(name: str, create_example_cfg: bool) -> None:
kl_im = InstanceManager(Klipper)
new_instance = Klipper(suffix=name)
kl_im.current_instance = new_instance
kl_im.create_instance()
kl_im.enable_instance()
if create_example_cfg:
# if a client-config is installed, include it in the new example cfg
clients = get_existing_clients()
create_example_printer_cfg(new_instance, clients)
kl_im.start_instance()

View File

@@ -0,0 +1,324 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 grp
import os
import re
import shutil
import subprocess
import textwrap
from pathlib import Path
from typing import List, Union, Literal, Dict, Optional
from components.klipper import (
MODULE_PATH,
KLIPPER_DIR,
KLIPPER_ENV_DIR,
KLIPPER_BACKUP_DIR,
)
from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import (
print_missing_usergroup_dialog,
print_instance_overview,
print_select_instance_count_dialog,
print_select_custom_name_dialog,
)
from components.moonraker.moonraker import Moonraker
from components.moonraker.moonraker_utils import moonraker_to_multi_conversion
from components.webui_client import ClientData
from components.webui_client.client_config.client_config_setup import (
create_client_config_symlink,
)
from core.backup_manager.backup_manager import BackupManager
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.base_instance import BaseInstance
from core.instance_manager.instance_manager import InstanceManager
from core.instance_manager.name_scheme import NameScheme
from core.repo_manager.repo_manager import RepoManager
from utils import PRINTER_CFG_BACKUP_DIR
from utils.common import get_install_status_common
from utils.constants import CURRENT_USER
from utils.input_utils import get_confirm, get_string_input, get_number_input
from utils.logger import Logger
from utils.system_utils import mask_system_service
def get_klipper_status() -> Dict[
Literal["status", "status_code", "instances", "repo", "local", "remote"],
Union[str, int],
]:
status = get_install_status_common(Klipper, KLIPPER_DIR, KLIPPER_ENV_DIR)
return {
"status": status.get("status"),
"status_code": status.get("status_code"),
"instances": status.get("instances"),
"repo": RepoManager.get_repo_name(KLIPPER_DIR),
"local": RepoManager.get_local_commit(KLIPPER_DIR),
"remote": RepoManager.get_remote_commit(KLIPPER_DIR),
}
def check_is_multi_install(
existing_instances: List[Klipper], install_count: int
) -> bool:
return not existing_instances and install_count > 1
def check_is_single_to_multi_conversion(existing_instances: List[Klipper]) -> bool:
return len(existing_instances) == 1 and existing_instances[0].suffix == ""
def init_name_scheme(
existing_instances: List[Klipper], install_count: int
) -> NameScheme:
if check_is_multi_install(
existing_instances, install_count
) or check_is_single_to_multi_conversion(existing_instances):
print_select_custom_name_dialog()
if get_confirm("Assign custom names?", False, allow_go_back=True):
return NameScheme.CUSTOM
else:
return NameScheme.INDEX
else:
return NameScheme.SINGLE
def update_name_scheme(
name_scheme: NameScheme,
name_dict: Dict[int, str],
klipper_instances: List[Klipper],
moonraker_instances: List[Moonraker],
) -> NameScheme:
# if there are more moonraker instances installed than klipper, we
# load their names into the name_dict, as we will detect and enforce that naming scheme
if len(moonraker_instances) > len(klipper_instances):
update_name_dict(name_dict, moonraker_instances)
return detect_name_scheme(moonraker_instances)
elif len(klipper_instances) > 1:
update_name_dict(name_dict, klipper_instances)
return detect_name_scheme(klipper_instances)
else:
return name_scheme
def update_name_dict(name_dict: Dict[int, str], instances: List[BaseInstance]) -> None:
for k, v in enumerate(instances):
name_dict[k] = v.suffix
def handle_instance_naming(name_dict: Dict[int, str], name_scheme: NameScheme) -> None:
if name_scheme == NameScheme.SINGLE:
return
for k in name_dict:
if name_dict[k] == "" and name_scheme == NameScheme.INDEX:
name_dict[k] = str(k + 1)
elif name_dict[k] == "" and name_scheme == NameScheme.CUSTOM:
assign_custom_name(k, name_dict)
def add_to_existing() -> bool:
kl_instances = InstanceManager(Klipper).instances
print_instance_overview(kl_instances)
return get_confirm("Add new instances?", allow_go_back=True)
def get_install_count() -> Union[int, None]:
"""
Print a dialog for selecting the amount of Klipper instances
to set up with an option to navigate back. Returns None if the
user selected to go back, otherwise an integer greater or equal than 1 |
:return: Integer >= 1 or None
"""
kl_instances = InstanceManager(Klipper).instances
print_select_instance_count_dialog()
question = f"Number of{' additional' if len(kl_instances) > 0 else ''} Klipper instances to set up"
return get_number_input(question, 1, default=1, allow_go_back=True)
def assign_custom_name(key: int, name_dict: Dict[int, str]) -> None:
existing_names = []
existing_names.extend(Klipper.blacklist())
existing_names.extend(name_dict[n] for n in name_dict)
question = f"Enter name for instance {key + 1}"
name_dict[key] = get_string_input(question, exclude=existing_names)
def handle_to_multi_instance_conversion(new_name: str) -> None:
Logger.print_status("Converting single instance to multi instances ...")
klipper_to_multi_conversion(new_name)
moonraker_to_multi_conversion(new_name)
def klipper_to_multi_conversion(new_name: str) -> None:
Logger.print_status("Convert Klipper single to multi instance ...")
im = InstanceManager(Klipper)
im.current_instance = im.instances[0]
# temporarily store the data dir path
old_data_dir = im.instances[0].data_dir
old_data_dir_name = im.instances[0].data_dir_name
# backup the old data_dir
bm = BackupManager()
name = f"config-{old_data_dir_name}"
bm.backup_directory(
name,
source=im.current_instance.cfg_dir,
target=PRINTER_CFG_BACKUP_DIR,
)
# remove the old single instance
im.stop_instance()
im.disable_instance()
im.delete_instance()
# create a new klipper instance with the new name
new_instance = Klipper(suffix=new_name)
im.current_instance = new_instance
if not new_instance.data_dir.is_dir():
# rename the old data dir and use it for the new instance
Logger.print_status(f"Rename '{old_data_dir}' to '{new_instance.data_dir}' ...")
old_data_dir.rename(new_instance.data_dir)
else:
Logger.print_info(f"Existing '{new_instance.data_dir}' found ...")
# patch the virtual_sdcard sections path value to match the new printer_data foldername
cm = ConfigManager(new_instance.cfg_file)
if cm.config.has_section("virtual_sdcard"):
cm.set_value("virtual_sdcard", "path", str(new_instance.gcodes_dir))
cm.write_config()
# finalize creating the new instance
im.create_instance()
im.enable_instance()
im.start_instance()
def check_user_groups():
current_groups = [grp.getgrgid(gid).gr_name for gid in os.getgroups()]
missing_groups = []
if "tty" not in current_groups:
missing_groups.append("tty")
if "dialout" not in current_groups:
missing_groups.append("dialout")
if not missing_groups:
return
print_missing_usergroup_dialog(missing_groups)
if not get_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"):
log = "Skipped adding user to required groups. You might encounter issues."
Logger.warn(log)
return
try:
for group in missing_groups:
Logger.print_status(f"Adding user '{CURRENT_USER}' to group {group} ...")
command = ["sudo", "usermod", "-a", "-G", group, CURRENT_USER]
subprocess.run(command, check=True)
Logger.print_ok(f"Group {group} assigned to user '{CURRENT_USER}'.")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Unable to add user to usergroups: {e}")
raise
log = "Remember to relog/restart this machine for the group(s) to be applied!"
Logger.print_warn(log)
def handle_disruptive_system_packages() -> None:
services = []
command = ["systemctl", "is-enabled", "brltty"]
brltty_status = subprocess.run(command, capture_output=True, text=True)
command = ["systemctl", "is-enabled", "brltty-udev"]
brltty_udev_status = subprocess.run(command, capture_output=True, text=True)
command = ["systemctl", "is-enabled", "ModemManager"]
modem_manager_status = subprocess.run(command, capture_output=True, text=True)
if "enabled" in brltty_status.stdout:
services.append("brltty")
if "enabled" in brltty_udev_status.stdout:
services.append("brltty-udev")
if "enabled" in modem_manager_status.stdout:
services.append("ModemManager")
for service in services if services else []:
try:
log = f"{service} service detected! Masking {service} service ..."
Logger.print_status(log)
mask_system_service(service)
Logger.print_ok(f"{service} service masked!")
except subprocess.CalledProcessError:
warn_msg = textwrap.dedent(
f"""
KIAUH was unable to mask the {service} system service.
Please fix the problem manually. Otherwise, this may have
undesirable effects on the operation of Klipper.
"""
)[1:]
Logger.print_warn(warn_msg)
def detect_name_scheme(instance_list: List[BaseInstance]) -> NameScheme:
pattern = re.compile("^\d+$")
for instance in instance_list:
if not pattern.match(instance.suffix):
return NameScheme.CUSTOM
return NameScheme.INDEX
def get_highest_index(instance_list: List[Klipper]) -> int:
indices = [int(instance.suffix.split("-")[-1]) for instance in instance_list]
return max(indices)
def create_example_printer_cfg(
instance: Klipper, clients: Optional[List[ClientData]] = None
) -> None:
Logger.print_status(f"Creating example printer.cfg in '{instance.cfg_dir}'")
if instance.cfg_file.is_file():
Logger.print_info(f"'{instance.cfg_file}' already exists.")
return
source = MODULE_PATH.joinpath("assets/printer.cfg")
target = instance.cfg_file
try:
shutil.copy(source, target)
except OSError as e:
Logger.print_error(f"Unable to create example printer.cfg:\n{e}")
return
cm = ConfigManager(target)
cm.set_value("virtual_sdcard", "path", str(instance.gcodes_dir))
# include existing client configs in the example config
if clients is not None and len(clients) > 0:
for c in clients:
client_config = c.get("client_config")
section = client_config.get("printer_cfg_section")
cm.config.add_section(section=section)
create_client_config_symlink(client_config, [instance])
cm.write_config()
Logger.print_ok(f"Example printer.cfg created in '{instance.cfg_dir}'")
def backup_klipper_dir() -> None:
bm = BackupManager()
bm.backup_directory("klipper", source=KLIPPER_DIR, target=KLIPPER_BACKUP_DIR)
bm.backup_directory("klippy-env", source=KLIPPER_ENV_DIR, target=KLIPPER_BACKUP_DIR)

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.klipper import klipper_remove
from core.menus import BACK_HELP_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN
# noinspection PyUnusedLocal
class KlipperRemoveMenu(BaseMenu):
def __init__(self):
super().__init__(
header=False,
options={
"0": self.toggle_all,
"1": self.toggle_remove_klipper_service,
"2": self.toggle_remove_klipper_dir,
"3": self.toggle_remove_klipper_env,
"4": self.toggle_delete_klipper_logs,
"c": self.run_removal_process,
},
footer_type=BACK_HELP_FOOTER,
)
self.remove_klipper_service = False
self.remove_klipper_dir = False
self.remove_klipper_env = False
self.delete_klipper_logs = False
def print_menu(self) -> None:
header = " [ Remove Klipper ] "
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
checked = f"[{COLOR_CYAN}x{RESET_FORMAT}]"
unchecked = "[ ]"
o1 = checked if self.remove_klipper_service else unchecked
o2 = checked if self.remove_klipper_dir else unchecked
o3 = checked if self.remove_klipper_env else unchecked
o4 = checked if self.delete_klipper_logs else unchecked
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| Enter a number and hit enter to select / deselect |
| the specific option for removal. |
|-------------------------------------------------------|
| 0) Select everything |
|-------------------------------------------------------|
| 1) {o1} Remove Service |
| 2) {o2} Remove Local Repository |
| 3) {o3} Remove Python Environment |
| 4) {o4} Delete all Log-Files |
|-------------------------------------------------------|
| C) Continue |
"""
)[1:]
print(menu, end="")
def toggle_all(self, **kwargs) -> None:
self.remove_klipper_service = True
self.remove_klipper_dir = True
self.remove_klipper_env = True
self.delete_klipper_logs = True
def toggle_remove_klipper_service(self, **kwargs) -> None:
self.remove_klipper_service = not self.remove_klipper_service
def toggle_remove_klipper_dir(self, **kwargs) -> None:
self.remove_klipper_dir = not self.remove_klipper_dir
def toggle_remove_klipper_env(self, **kwargs) -> None:
self.remove_klipper_env = not self.remove_klipper_env
def toggle_delete_klipper_logs(self, **kwargs) -> None:
self.delete_klipper_logs = not self.delete_klipper_logs
def run_removal_process(self, **kwargs) -> None:
if (
not self.remove_klipper_service
and not self.remove_klipper_dir
and not self.remove_klipper_env
and not self.delete_klipper_logs
):
error = f"{COLOR_RED}Nothing selected! Select options to remove first.{RESET_FORMAT}"
print(error)
return
klipper_remove.run_klipper_removal(
self.remove_klipper_service,
self.remove_klipper_dir,
self.remove_klipper_env,
self.delete_klipper_logs,
)
self.remove_klipper_service = False
self.remove_klipper_dir = False
self.remove_klipper_env = False
self.delete_klipper_logs = False

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import Dict, Union, Literal
FileKey = Literal["filepath", "display_name"]
LogFile = Dict[FileKey, Union[str, Path]]

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 urllib.request
from pathlib import Path
from typing import List
from components.klipper.klipper import Klipper
from components.log_uploads import LogFile
from core.instance_manager.instance_manager import InstanceManager
from utils.logger import Logger
def get_logfile_list() -> List[LogFile]:
cm = InstanceManager(Klipper)
log_dirs: List[Path] = [instance.log_dir for instance in cm.instances]
logfiles: List[LogFile] = []
for _dir in log_dirs:
for f in _dir.iterdir():
logfiles.append({"filepath": f, "display_name": get_display_name(f)})
return logfiles
def get_display_name(filepath: Path) -> str:
printer = " ".join(filepath.parts[-3].split("_")[:-1])
name = filepath.name
return f"{printer}: {name}"
def upload_logfile(logfile: LogFile) -> None:
file = logfile.get("filepath")
name = logfile.get("display_name")
Logger.print_status(f"Uploading the following logfile from {name} ...")
with open(file, "rb") as f:
headers = {"x-random": ""}
req = urllib.request.Request("http://paste.c-net.org/", headers=headers, data=f)
try:
response = urllib.request.urlopen(req)
link = response.read().decode("utf-8")
Logger.print_ok("Upload successful! Access it via the following link:")
Logger.print_ok(f">>>> {link}", False)
except Exception as e:
Logger.print_error(f"Uploading logfile failed!")
Logger.print_error(str(e))

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.log_uploads.log_upload_utils import get_logfile_list
from components.log_uploads.log_upload_utils import upload_logfile
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import RESET_FORMAT, COLOR_YELLOW
# noinspection PyMethodMayBeStatic
class LogUploadMenu(BaseMenu):
def __init__(self):
self.logfile_list = get_logfile_list()
options = {index: self.upload for index in range(len(self.logfile_list))}
super().__init__(
header=True,
options=options,
footer_type=BACK_FOOTER,
)
def print_menu(self):
header = " [ Log Upload ] "
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| You can select the following logfiles for uploading: |
| |
"""
)[1:]
logfile_list = get_logfile_list()
for logfile in enumerate(logfile_list):
line = f"{logfile[0]}) {logfile[1].get('display_name')}"
menu += f"| {line:<54}|\n"
print(menu, end="")
def upload(self, **kwargs):
upload_logfile(self.logfile_list[kwargs.get("opt_index")])

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
MODULE_PATH = Path(__file__).resolve().parent
MOONRAKER_DIR = Path.home().joinpath("moonraker")
MOONRAKER_ENV_DIR = Path.home().joinpath("moonraker-env")
MOONRAKER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("moonraker-backups")
MOONRAKER_DB_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("moonraker-db-backups")
MOONRAKER_REQUIREMENTS_TXT = MOONRAKER_DIR.joinpath(
"scripts/moonraker-requirements.txt"
)
DEFAULT_MOONRAKER_REPO_URL = "https://github.com/Arksine/moonraker"
DEFAULT_MOONRAKER_PORT = 7125
# introduced due to
# https://github.com/Arksine/moonraker/issues/349
# https://github.com/Arksine/moonraker/pull/346
POLKIT_LEGACY_FILE = Path("/etc/polkit-1/localauthority/50-local.d/10-moonraker.pkla")
POLKIT_FILE = Path("/etc/polkit-1/rules.d/moonraker.rules")
POLKIT_USR_FILE = Path("/usr/share/polkit-1/rules.d/moonraker.rules")
POLKIT_SCRIPT = Path.home().joinpath("moonraker/scripts/set-policykit-rules.sh")
EXIT_MOONRAKER_SETUP = "Exiting Moonraker setup ..."

View File

@@ -0,0 +1,29 @@
[server]
host: 0.0.0.0
port: %PORT%
klippy_uds_address: %UDS%
[authorization]
trusted_clients:
10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
FE80::/10
::1/128
cors_domains:
*.lan
*.local
*://localhost
*://localhost:*
*://my.mainsail.xyz
*://app.fluidd.xyz
[octoprint_compat]
[history]
[update_manager]
channel: dev
refresh_interval: 168

View File

@@ -0,0 +1 @@
MOONRAKER_ARGS="%MOONRAKER_DIR%/moonraker/moonraker.py -d %PRINTER_DATA%"

View File

@@ -0,0 +1,19 @@
[Unit]
Description=API Server for Klipper SV1
Documentation=https://moonraker.readthedocs.io/
Requires=network-online.target
After=network-online.target
[Install]
WantedBy=multi-user.target
[Service]
Type=simple
User=%USER%
SupplementaryGroups=moonraker-admin
RemainAfterExit=yes
WorkingDirectory=%MOONRAKER_DIR%
EnvironmentFile=%ENV_FILE%
ExecStart=%ENV%/bin/python $MOONRAKER_ARGS
Restart=always
RestartSec=10

View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.moonraker import moonraker_remove
from core.menus import BACK_HELP_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN
# noinspection PyUnusedLocal
class MoonrakerRemoveMenu(BaseMenu):
def __init__(self):
super().__init__(
header=False,
options={
"0": self.toggle_all,
"1": self.toggle_remove_moonraker_service,
"2": self.toggle_remove_moonraker_dir,
"3": self.toggle_remove_moonraker_env,
"4": self.toggle_remove_moonraker_polkit,
"5": self.toggle_delete_moonraker_logs,
"c": self.run_removal_process,
},
footer_type=BACK_HELP_FOOTER,
)
self.remove_moonraker_service = False
self.remove_moonraker_dir = False
self.remove_moonraker_env = False
self.remove_moonraker_polkit = False
self.delete_moonraker_logs = False
def print_menu(self) -> None:
header = " [ Remove Moonraker ] "
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
checked = f"[{COLOR_CYAN}x{RESET_FORMAT}]"
unchecked = "[ ]"
o1 = checked if self.remove_moonraker_service else unchecked
o2 = checked if self.remove_moonraker_dir else unchecked
o3 = checked if self.remove_moonraker_env else unchecked
o4 = checked if self.remove_moonraker_polkit else unchecked
o5 = checked if self.delete_moonraker_logs else unchecked
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| Enter a number and hit enter to select / deselect |
| the specific option for removal. |
|-------------------------------------------------------|
| 0) Select everything |
|-------------------------------------------------------|
| 1) {o1} Remove Service |
| 2) {o2} Remove Local Repository |
| 3) {o3} Remove Python Environment |
| 4) {o4} Remove Policy Kit Rules |
| 5) {o5} Delete all Log-Files |
|-------------------------------------------------------|
| C) Continue |
"""
)[1:]
print(menu, end="")
def toggle_all(self, **kwargs) -> None:
self.remove_moonraker_service = True
self.remove_moonraker_dir = True
self.remove_moonraker_env = True
self.remove_moonraker_polkit = True
self.delete_moonraker_logs = True
def toggle_remove_moonraker_service(self, **kwargs) -> None:
self.remove_moonraker_service = not self.remove_moonraker_service
def toggle_remove_moonraker_dir(self, **kwargs) -> None:
self.remove_moonraker_dir = not self.remove_moonraker_dir
def toggle_remove_moonraker_env(self, **kwargs) -> None:
self.remove_moonraker_env = not self.remove_moonraker_env
def toggle_remove_moonraker_polkit(self, **kwargs) -> None:
self.remove_moonraker_polkit = not self.remove_moonraker_polkit
def toggle_delete_moonraker_logs(self, **kwargs) -> None:
self.delete_moonraker_logs = not self.delete_moonraker_logs
def run_removal_process(self, **kwargs) -> None:
if (
not self.remove_moonraker_service
and not self.remove_moonraker_dir
and not self.remove_moonraker_env
and not self.remove_moonraker_polkit
and not self.delete_moonraker_logs
):
error = f"{COLOR_RED}Nothing selected! Select options to remove first.{RESET_FORMAT}"
print(error)
return
moonraker_remove.run_moonraker_removal(
self.remove_moonraker_service,
self.remove_moonraker_dir,
self.remove_moonraker_env,
self.remove_moonraker_polkit,
self.delete_moonraker_logs,
)
self.remove_moonraker_service = False
self.remove_moonraker_dir = False
self.remove_moonraker_env = False
self.remove_moonraker_polkit = False
self.delete_moonraker_logs = False

View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 subprocess
from pathlib import Path
from typing import List, Union
from components.moonraker import MOONRAKER_DIR, MOONRAKER_ENV_DIR, MODULE_PATH
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.base_instance import BaseInstance
from utils.constants import SYSTEMD
from utils.logger import Logger
# noinspection PyMethodMayBeStatic
class Moonraker(BaseInstance):
@classmethod
def blacklist(cls) -> List[str]:
return ["None", "mcu"]
def __init__(self, suffix: str = ""):
super().__init__(instance_type=self, suffix=suffix)
self.moonraker_dir: Path = MOONRAKER_DIR
self.env_dir: Path = MOONRAKER_ENV_DIR
self.cfg_file = self.cfg_dir.joinpath("moonraker.conf")
self.port = self._get_port()
self.backup_dir = self.data_dir.joinpath("backup")
self.certs_dir = self.data_dir.joinpath("certs")
self._db_dir = self.data_dir.joinpath("database")
self._comms_dir = self.data_dir.joinpath("comms")
self.log = self.log_dir.joinpath("moonraker.log")
@property
def db_dir(self) -> Path:
return self._db_dir
@property
def comms_dir(self) -> Path:
return self._comms_dir
def create(self, create_example_cfg: bool = False) -> None:
Logger.print_status("Creating new Moonraker Instance ...")
service_template_path = MODULE_PATH.joinpath("assets/moonraker.service")
env_template_file_path = MODULE_PATH.joinpath("assets/moonraker.env")
service_file_name = self.get_service_file_name(extension=True)
service_file_target = SYSTEMD.joinpath(service_file_name)
env_file_target = self.sysd_dir.joinpath("moonraker.env")
try:
self.create_folders([self.backup_dir, self.certs_dir, self._db_dir])
self.write_service_file(
service_template_path, service_file_target, env_file_target
)
self.write_env_file(env_template_file_path, env_file_target)
except subprocess.CalledProcessError as e:
Logger.print_error(
f"Error creating service file {service_file_target}: {e}"
)
raise
except OSError as e:
Logger.print_error(f"Error writing file: {e}")
raise
def delete(self) -> None:
service_file = self.get_service_file_name(extension=True)
service_file_path = self.get_service_file_path()
Logger.print_status(f"Deleting Moonraker Instance: {service_file}")
try:
command = ["sudo", "rm", "-f", service_file_path]
subprocess.run(command, check=True)
Logger.print_ok(f"Service file deleted: {service_file_path}")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error deleting service file: {e}")
raise
def write_service_file(
self,
service_template_path: Path,
service_file_target: Path,
env_file_target: Path,
) -> None:
service_content = self._prep_service_file(
service_template_path, env_file_target
)
command = ["sudo", "tee", service_file_target]
subprocess.run(
command,
input=service_content.encode(),
stdout=subprocess.DEVNULL,
check=True,
)
Logger.print_ok(f"Service file created: {service_file_target}")
def write_env_file(
self, env_template_file_path: Path, env_file_target: Path
) -> None:
env_file_content = self._prep_env_file(env_template_file_path)
with open(env_file_target, "w") as env_file:
env_file.write(env_file_content)
Logger.print_ok(f"Env file created: {env_file_target}")
def _prep_service_file(
self, service_template_path: Path, env_file_path: Path
) -> str:
try:
with open(service_template_path, "r") as template_file:
template_content = template_file.read()
except FileNotFoundError:
Logger.print_error(
f"Unable to open {service_template_path} - File not found"
)
raise
service_content = template_content.replace("%USER%", self.user)
service_content = service_content.replace(
"%MOONRAKER_DIR%", str(self.moonraker_dir)
)
service_content = service_content.replace("%ENV%", str(self.env_dir))
service_content = service_content.replace("%ENV_FILE%", str(env_file_path))
return service_content
def _prep_env_file(self, env_template_file_path: Path) -> str:
try:
with open(env_template_file_path, "r") as env_file:
env_template_file_content = env_file.read()
except FileNotFoundError:
Logger.print_error(
f"Unable to open {env_template_file_path} - File not found"
)
raise
env_file_content = env_template_file_content.replace(
"%MOONRAKER_DIR%", str(self.moonraker_dir)
)
env_file_content = env_file_content.replace(
"%PRINTER_DATA%", str(self.data_dir)
)
return env_file_content
def _get_port(self) -> Union[int, None]:
if not self.cfg_file.is_file():
return None
cm = ConfigManager(cfg_file=self.cfg_file)
port = cm.get_value("server", "port")
return int(port) if port is not None else port

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from typing import List
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from core.menus.base_menu import print_back_footer
from utils.constants import COLOR_GREEN, RESET_FORMAT, COLOR_YELLOW, COLOR_CYAN
def print_moonraker_overview(
klipper_instances: List[Klipper],
moonraker_instances: List[Moonraker],
show_index=False,
show_select_all=False,
):
headline = f"{COLOR_GREEN}The following instances were found:{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
|{headline:^64}|
|-------------------------------------------------------|
"""
)[1:]
if show_select_all:
select_all = f"{COLOR_YELLOW}a) Select all{RESET_FORMAT}"
dialog += f"| {select_all:<63}|\n"
dialog += "| |\n"
instance_map = {
k.get_service_file_name(): (
k.get_service_file_name().replace("klipper", "moonraker")
if k.suffix in [m.suffix for m in moonraker_instances]
else ""
)
for k in klipper_instances
}
for i, k in enumerate(instance_map):
mr_name = instance_map.get(k)
m = f"<-> {mr_name}" if mr_name != "" else ""
line = f"{COLOR_CYAN}{f'{i})' if show_index else ''} {k} {m} {RESET_FORMAT}"
dialog += f"| {line:<63}|\n"
warn_l1 = f"{COLOR_YELLOW}PLEASE NOTE: {RESET_FORMAT}"
warn_l2 = f"{COLOR_YELLOW}If you select an instance with an existing Moonraker{RESET_FORMAT}"
warn_l3 = f"{COLOR_YELLOW}instance, that Moonraker instance will be re-created!{RESET_FORMAT}"
warning = textwrap.dedent(
f"""
| |
|-------------------------------------------------------|
| {warn_l1:<63}|
| {warn_l2:<63}|
| {warn_l3:<63}|
"""
)[1:]
dialog += warning
print(dialog, end="")
print_back_footer()

View File

@@ -0,0 +1,155 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
import subprocess
from typing import List, Union
from components.klipper.klipper_dialogs import print_instance_overview
from components.moonraker import MOONRAKER_DIR, MOONRAKER_ENV_DIR
from components.moonraker.moonraker import Moonraker
from core.instance_manager.instance_manager import InstanceManager
from utils.filesystem_utils import remove_file
from utils.input_utils import get_selection_input
from utils.logger import Logger
def run_moonraker_removal(
remove_service: bool,
remove_dir: bool,
remove_env: bool,
remove_polkit: bool,
delete_logs: bool,
) -> None:
im = InstanceManager(Moonraker)
if remove_service:
Logger.print_status("Removing Moonraker instances ...")
if im.instances:
instances_to_remove = select_instances_to_remove(im.instances)
remove_instances(im, instances_to_remove)
else:
Logger.print_info("No Moonraker Services installed! Skipped ...")
if (remove_polkit or remove_dir or remove_env) and im.instances:
Logger.print_warn("There are still other Moonraker services installed!")
Logger.print_warn("Therefor the following parts cannot be removed:")
Logger.print_warn(
"""
● Moonraker PolicyKit rules
● Moonraker local repository
● Moonraker Python environment
""",
False,
)
else:
if remove_polkit:
Logger.print_status("Removing all Moonraker policykit rules ...")
remove_polkit_rules()
if remove_dir:
Logger.print_status("Removing Moonraker local repository ...")
remove_moonraker_dir()
if remove_env:
Logger.print_status("Removing Moonraker Python environment ...")
remove_moonraker_env()
# delete moonraker logs of all instances
if delete_logs:
Logger.print_status("Removing all Moonraker logs ...")
delete_moonraker_logs(im.instances)
def select_instances_to_remove(
instances: List[Moonraker],
) -> Union[List[Moonraker], None]:
print_instance_overview(instances, True, True)
options = [str(i) for i in range(len(instances))]
options.extend(["a", "A", "b", "B"])
selection = get_selection_input("Select Moonraker instance to remove", options)
instances_to_remove = []
if selection == "b".lower():
return None
elif selection == "a".lower():
instances_to_remove.extend(instances)
else:
instance = instances[int(selection)]
instances_to_remove.append(instance)
return instances_to_remove
def remove_instances(
instance_manager: InstanceManager,
instance_list: List[Moonraker],
) -> None:
for instance in instance_list:
Logger.print_status(f"Removing instance {instance.get_service_file_name()} ...")
instance_manager.current_instance = instance
instance_manager.stop_instance()
instance_manager.disable_instance()
instance_manager.delete_instance()
instance_manager.reload_daemon()
def remove_moonraker_dir() -> None:
if not MOONRAKER_DIR.exists():
Logger.print_info(f"'{MOONRAKER_DIR}' does not exist. Skipped ...")
return
try:
shutil.rmtree(MOONRAKER_DIR)
except OSError as e:
Logger.print_error(f"Unable to delete '{MOONRAKER_DIR}':\n{e}")
def remove_moonraker_env() -> None:
if not MOONRAKER_ENV_DIR.exists():
Logger.print_info(f"'{MOONRAKER_ENV_DIR}' does not exist. Skipped ...")
return
try:
shutil.rmtree(MOONRAKER_ENV_DIR)
except OSError as e:
Logger.print_error(f"Unable to delete '{MOONRAKER_ENV_DIR}':\n{e}")
def remove_polkit_rules() -> None:
if not MOONRAKER_DIR.exists():
log = "Cannot remove policykit rules. Moonraker directory not found."
Logger.print_warn(log)
return
try:
command = [f"{MOONRAKER_DIR}/scripts/set-policykit-rules.sh", "--clear"]
subprocess.run(
command, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, check=True
)
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error while removing policykit rules: {e}")
Logger.print_ok("Policykit rules successfully removed!")
def delete_moonraker_logs(instances: List[Moonraker]) -> None:
all_logfiles = []
for instance in instances:
all_logfiles = list(instance.log_dir.glob("moonraker.log*"))
if not all_logfiles:
Logger.print_info("No Moonraker logs found. Skipped ...")
return
for log in all_logfiles:
Logger.print_status(f"Remove '{log}'")
remove_file(log)

View File

@@ -0,0 +1,218 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 subprocess
import sys
from pathlib import Path
from typing import List
from components.webui_client import MAINSAIL_DIR
from components.webui_client.client_utils import enable_mainsail_remotemode, get_existing_clients
from kiauh import KIAUH_CFG
from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import print_instance_overview
from components.moonraker import (
EXIT_MOONRAKER_SETUP,
DEFAULT_MOONRAKER_REPO_URL,
MOONRAKER_DIR,
MOONRAKER_ENV_DIR,
MOONRAKER_REQUIREMENTS_TXT,
POLKIT_LEGACY_FILE,
POLKIT_FILE,
POLKIT_USR_FILE,
POLKIT_SCRIPT,
)
from components.moonraker.moonraker import Moonraker
from components.moonraker.moonraker_dialogs import print_moonraker_overview
from components.moonraker.moonraker_utils import (
create_example_moonraker_conf,
backup_moonraker_dir,
)
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.instance_manager import InstanceManager
from core.repo_manager.repo_manager import RepoManager
from utils.filesystem_utils import check_file_exist
from utils.input_utils import (
get_confirm,
get_selection_input,
)
from utils.logger import Logger
from utils.system_utils import (
parse_packages_from_file,
create_python_venv,
install_python_requirements,
update_system_package_lists,
install_system_packages,
)
def install_moonraker() -> None:
if not check_moonraker_install_requirements():
return
kl_im = InstanceManager(Klipper)
klipper_instances = kl_im.instances
mr_im = InstanceManager(Moonraker)
moonraker_instances = mr_im.instances
selected_klipper_instance = 0
if len(klipper_instances) > 1:
print_moonraker_overview(
klipper_instances,
moonraker_instances,
show_index=True,
show_select_all=True,
)
options = [str(i) for i in range(len(klipper_instances))]
options.extend(["a", "A", "b", "B"])
question = "Select Klipper instance to setup Moonraker for"
selected_klipper_instance = get_selection_input(question, options).lower()
instance_names = []
if selected_klipper_instance == "b":
Logger.print_status(EXIT_MOONRAKER_SETUP)
return
elif selected_klipper_instance == "a":
for instance in klipper_instances:
instance_names.append(instance.suffix)
else:
index = int(selected_klipper_instance)
instance_names.append(klipper_instances[index].suffix)
create_example_cfg = get_confirm("Create example moonraker.conf?")
setup_moonraker_prerequesites()
install_moonraker_polkit()
used_ports_map = {
instance.suffix: instance.port for instance in moonraker_instances
}
for name in instance_names:
current_instance = Moonraker(suffix=name)
mr_im.current_instance = current_instance
mr_im.create_instance()
mr_im.enable_instance()
if create_example_cfg:
# if a webclient and/or it's config is installed, patch its update section to the config
clients = get_existing_clients()
create_example_moonraker_conf(current_instance, used_ports_map, clients)
mr_im.start_instance()
mr_im.reload_daemon()
# if mainsail is installed, and we installed
# multiple moonraker instances, we enable mainsails remote mode
if MAINSAIL_DIR.exists() and len(mr_im.instances) > 1:
enable_mainsail_remotemode()
def check_moonraker_install_requirements() -> bool:
if not (sys.version_info.major >= 3 and sys.version_info.minor >= 7):
Logger.print_error("Versioncheck failed!")
Logger.print_error("Python 3.7 or newer required to run Moonraker.")
return False
kl_instance_count = len(InstanceManager(Klipper).instances)
if kl_instance_count < 1:
Logger.print_warn("Klipper not installed!")
Logger.print_warn("Moonraker cannot be installed! Install Klipper first.")
return False
return True
def setup_moonraker_prerequesites() -> None:
cm = ConfigManager(cfg_file=KIAUH_CFG)
repo = str(
cm.get_value("moonraker", "repository_url") or DEFAULT_MOONRAKER_REPO_URL
)
branch = str(cm.get_value("moonraker", "branch") or "master")
repo_manager = RepoManager(
repo=repo,
branch=branch,
target_dir=MOONRAKER_DIR,
)
repo_manager.clone_repo()
# install moonraker dependencies and create python virtualenv
install_moonraker_packages(MOONRAKER_DIR)
create_python_venv(MOONRAKER_ENV_DIR)
install_python_requirements(MOONRAKER_ENV_DIR, MOONRAKER_REQUIREMENTS_TXT)
def install_moonraker_packages(moonraker_dir: Path) -> None:
script = moonraker_dir.joinpath("scripts/install-moonraker.sh")
packages = parse_packages_from_file(script)
update_system_package_lists(silent=False)
install_system_packages(packages)
def install_moonraker_polkit() -> None:
Logger.print_status("Installing Moonraker policykit rules ...")
legacy_file_exists = check_file_exist(POLKIT_LEGACY_FILE, True)
polkit_file_exists = check_file_exist(POLKIT_FILE, True)
usr_file_exists = check_file_exist(POLKIT_USR_FILE, True)
if legacy_file_exists or (polkit_file_exists and usr_file_exists):
Logger.print_info("Moonraker policykit rules are already installed.")
return
try:
command = [POLKIT_SCRIPT, "--disable-systemctl"]
result = subprocess.run(
command, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True
)
if result.returncode != 0 or result.stderr:
Logger.print_error(f"{result.stderr}", False)
Logger.print_error("Installing Moonraker policykit rules failed!")
return
Logger.print_ok("Moonraker policykit rules successfully installed!")
except subprocess.CalledProcessError as e:
log = f"Error while installing Moonraker policykit rules: {e.stderr.decode()}"
Logger.print_error(log)
def update_moonraker() -> None:
if not get_confirm("Update Moonraker now?"):
return
cm = ConfigManager(cfg_file=KIAUH_CFG)
if cm.get_value("kiauh", "backup_before_update"):
backup_moonraker_dir()
instance_manager = InstanceManager(Moonraker)
instance_manager.stop_all_instance()
repo = str(
cm.get_value("moonraker", "repository_url") or DEFAULT_MOONRAKER_REPO_URL
)
branch = str(cm.get_value("moonraker", "branch") or "master")
repo_manager = RepoManager(
repo=repo,
branch=branch,
target_dir=MOONRAKER_DIR,
)
repo_manager.pull_repo()
# install possible new system packages
install_moonraker_packages(MOONRAKER_DIR)
# install possible new python dependencies
install_python_requirements(MOONRAKER_ENV_DIR, MOONRAKER_REQUIREMENTS_TXT)
instance_manager.start_all_instance()

View File

@@ -0,0 +1,201 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import Dict, Literal, List, Union, Optional
from components.moonraker import (
DEFAULT_MOONRAKER_PORT,
MODULE_PATH,
MOONRAKER_DIR,
MOONRAKER_ENV_DIR,
MOONRAKER_BACKUP_DIR,
MOONRAKER_DB_BACKUP_DIR,
)
from components.moonraker.moonraker import Moonraker
from components.webui_client import MAINSAIL_DIR, ClientData
from components.webui_client.client_utils import enable_mainsail_remotemode
from core.backup_manager.backup_manager import BackupManager
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.instance_manager import InstanceManager
from core.repo_manager.repo_manager import RepoManager
from utils.common import get_install_status_common
from utils.logger import Logger
from utils.system_utils import (
get_ipv4_addr,
)
def get_moonraker_status() -> Dict[
Literal["status", "status_code", "instances", "repo", "local", "remote"],
Union[str, int],
]:
status = get_install_status_common(Moonraker, MOONRAKER_DIR, MOONRAKER_ENV_DIR)
return {
"status": status.get("status"),
"status_code": status.get("status_code"),
"instances": status.get("instances"),
"repo": RepoManager.get_repo_name(MOONRAKER_DIR),
"local": RepoManager.get_local_commit(MOONRAKER_DIR),
"remote": RepoManager.get_remote_commit(MOONRAKER_DIR),
}
def create_example_moonraker_conf(
instance: Moonraker,
ports_map: Dict[str, int],
clients: Optional[List[ClientData]] = None,
) -> None:
Logger.print_status(f"Creating example moonraker.conf in '{instance.cfg_dir}'")
if instance.cfg_file.is_file():
Logger.print_info(f"'{instance.cfg_file}' already exists.")
return
source = MODULE_PATH.joinpath("assets/moonraker.conf")
target = instance.cfg_file
try:
shutil.copy(source, target)
except OSError as e:
Logger.print_error(f"Unable to create example moonraker.conf:\n{e}")
return
ports = [
ports_map.get(instance)
for instance in ports_map
if ports_map.get(instance) is not None
]
if ports_map.get(instance.suffix) is None:
# this could be improved to not increment the max value of the ports list and assign it as the port
# as it can lead to situation where the port for e.g. instance moonraker-2 becomes 7128 if the port
# of moonraker-1 is 7125 and moonraker-3 is 7127 and there are moonraker.conf files for moonraker-1
# and moonraker-3 already. though, there does not seem to be a very reliable way of always assigning
# the correct port to each instance and the user will likely be required to correct the value manually.
port = max(ports) + 1 if ports else DEFAULT_MOONRAKER_PORT
else:
port = ports_map.get(instance.suffix)
ports_map[instance.suffix] = port
ip = get_ipv4_addr().split(".")[:2]
ip.extend(["0", "0/16"])
uds = instance.comms_dir.joinpath("klippy.sock")
cm = ConfigManager(target)
trusted_clients = f"\n{'.'.join(ip)}"
trusted_clients += cm.get_value("authorization", "trusted_clients")
cm.set_value("server", "port", str(port))
cm.set_value("server", "klippy_uds_address", str(uds))
cm.set_value("authorization", "trusted_clients", trusted_clients)
# add existing client and client configs in the update section
if clients is not None and len(clients) > 0:
for c in clients:
# client part
c_section = f"update_manager {c.get('name')}"
c_options = [
("type", "web"),
("channel", "stable"),
("repo", c.get("mr_conf_repo")),
("path", c.get("mr_conf_path")),
]
cm.config.add_section(section=c_section)
for option in c_options:
cm.config.set(c_section, option[0], option[1])
# client config part
c_config = c.get("client_config")
if c_config.get("dir").exists():
c_config_section = f"update_manager {c_config.get('name')}"
c_config_options = [
("type", "git_repo"),
("primary_branch", "master"),
("path", c_config.get("mr_conf_path")),
("origin", c_config.get("mr_conf_origin")),
("managed_services", "klipper"),
]
cm.config.add_section(section=c_config_section)
for option in c_config_options:
cm.config.set(c_config_section, option[0], option[1])
cm.write_config()
Logger.print_ok(f"Example moonraker.conf created in '{instance.cfg_dir}'")
def moonraker_to_multi_conversion(new_name: str) -> None:
"""
Converts the first instance in the List of Moonraker instances to an instance
with a new name. This method will be called when converting from a single Klipper
instance install to a multi instance install when Moonraker is also already
installed with a single instance.
:param new_name: new name the previous single instance is renamed to
:return: None
"""
im = InstanceManager(Moonraker)
instances: List[Moonraker] = im.instances
if not instances:
return
# in case there are multiple Moonraker instances, we don't want to do anything
if len(instances) > 1:
Logger.print_info("More than a single Moonraker instance found. Skipped ...")
return
Logger.print_status("Convert Moonraker single to multi instance ...")
# remove the old single instance
im.current_instance = im.instances[0]
im.stop_instance()
im.disable_instance()
im.delete_instance()
# create a new moonraker instance with the new name
new_instance = Moonraker(suffix=new_name)
im.current_instance = new_instance
# patch the server sections klippy_uds_address value to match the new printer_data foldername
cm = ConfigManager(new_instance.cfg_file)
if cm.config.has_section("server"):
cm.set_value(
"server",
"klippy_uds_address",
str(new_instance.comms_dir.joinpath("klippy.sock")),
)
cm.write_config()
# create, enable and start the new moonraker instance
im.create_instance()
im.enable_instance()
im.start_instance()
# if mainsail is installed, we enable mainsails remote mode
if MAINSAIL_DIR.exists() and len(im.instances) > 1:
enable_mainsail_remotemode()
def backup_moonraker_dir():
bm = BackupManager()
bm.backup_directory("moonraker", source=MOONRAKER_DIR, target=MOONRAKER_BACKUP_DIR)
bm.backup_directory(
"moonraker-env", source=MOONRAKER_ENV_DIR, target=MOONRAKER_BACKUP_DIR
)
def backup_moonraker_db_dir() -> None:
im = InstanceManager(Moonraker)
instances: List[Moonraker] = im.instances
bm = BackupManager()
for instance in instances:
name = f"database-{instance.data_dir_name}"
bm.backup_directory(
name, source=instance.db_dir, target=MOONRAKER_DB_BACKUP_DIR
)

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import Literal, TypedDict, Set
from core.backup_manager import BACKUP_ROOT_DIR
MODULE_PATH = Path(__file__).resolve().parent
###########
# MAINSAIL
###########
MAINSAIL_DIR = Path.home().joinpath("mainsail")
MAINSAIL_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("mainsail-backups")
MAINSAIL_CONFIG_DIR = Path.home().joinpath("mainsail-config")
MAINSAIL_CONFIG_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("mainsail-config-backups")
MAINSAIL_CONFIG_REPO_URL = "https://github.com/mainsail-crew/mainsail-config.git"
MAINSAIL_CONFIG_JSON = MAINSAIL_DIR.joinpath("config.json")
MAINSAIL_URL = (
"https://github.com/mainsail-crew/mainsail/releases/latest/download/mainsail.zip"
)
MAINSAIL_PRE_RLS_URL = (
"https://github.com/mainsail-crew/mainsail/releases/download/%TAG%/mainsail.zip"
)
MAINSAIL_TAGS_URL = "https://api.github.com/repos/mainsail-crew/mainsail/tags"
#########
# FLUIDD
#########
FLUIDD_DIR = Path.home().joinpath("fluidd")
FLUIDD_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("fluidd-backups")
FLUIDD_CONFIG_DIR = Path.home().joinpath("fluidd-config")
FLUIDD_CONFIG_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("fluidd-config-backups")
FLUIDD_CONFIG_REPO_URL = "https://github.com/fluidd-core/fluidd-config.git"
FLUIDD_URL = "https://github.com/fluidd-core/fluidd/releases/latest/download/fluidd.zip"
FLUIDD_PRE_RLS_URL = (
"https://github.com/fluidd-core/fluidd/releases/download/%TAG%/fluidd.zip"
)
FLUIDD_TAGS_URL = "https://api.github.com/repos/fluidd-core/fluidd/tags"
ClientName = Literal["mainsail", "fluidd"]
ClientConfigName = Literal["mainsail-config", "fluidd-config"]
class ClientData(TypedDict):
name: ClientName
display_name: str
dir: Path
backup_dir: Path
url: str
pre_release_url: str
tags_url: str
remote_mode: bool # required only for Mainsail
mr_conf_repo: str
mr_conf_path: str
client_config: "ClientConfigData"
class ClientConfigData(TypedDict):
name: ClientConfigName
display_name: str
cfg_filename: str
dir: Path
backup_dir: Path
url: str
printer_cfg_section: str
mr_conf_path: str
mr_conf_origin: str

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
import subprocess
from typing import List
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from components.webui_client import ClientConfigData
from core.instance_manager.instance_manager import InstanceManager
from utils.filesystem_utils import remove_file, remove_config_section
from utils.logger import Logger
def run_client_config_removal(
client_config: ClientConfigData,
kl_instances: List[Klipper],
mr_instances: List[Moonraker],
) -> None:
remove_client_config_dir(client_config)
remove_client_config_symlink(client_config)
remove_config_section(f"update_manager {client_config.get('name')}", mr_instances)
remove_config_section(client_config.get("printer_cfg_section"), kl_instances)
def remove_client_config_dir(client_config: ClientConfigData) -> None:
Logger.print_status(f"Removing {client_config.get('name')} ...")
client_config_dir = client_config.get("dir")
if not client_config_dir.exists():
Logger.print_info(f"'{client_config_dir}' does not exist. Skipping ...")
return
try:
shutil.rmtree(client_config_dir)
except OSError as e:
Logger.print_error(f"Unable to delete '{client_config_dir}':\n{e}")
def remove_client_config_symlink(client_config: ClientConfigData) -> None:
im = InstanceManager(Klipper)
instances: List[Klipper] = im.instances
for instance in instances:
Logger.print_status(f"Removing symlink from '{instance.cfg_dir}' ...")
symlink = instance.cfg_dir.joinpath(client_config.get("cfg_filename"))
if not symlink.is_symlink():
Logger.print_info(f"'{symlink}' does not exist. Skipping ...")
continue
try:
remove_file(symlink)
except subprocess.CalledProcessError:
Logger.print_error("Failed to remove symlink!")

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
import subprocess
from pathlib import Path
from typing import List
from kiauh import KIAUH_CFG
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from components.webui_client import ClientConfigData, ClientName, ClientData
from components.webui_client.client_dialogs import print_client_already_installed_dialog
from components.webui_client.client_utils import (
load_client_data,
backup_client_config_data, config_for_other_client_exist,
)
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.instance_manager import InstanceManager
from core.repo_manager.repo_manager import RepoManager
from utils.filesystem_utils import (
create_symlink,
add_config_section,
)
from utils.input_utils import get_confirm
from utils.logger import Logger
def install_client_config(client_name: ClientName) -> None:
client: ClientData = load_client_data(client_name)
client_config: ClientConfigData = client.get("client_config")
d_name = client_config.get("display_name")
if config_for_other_client_exist(client_name):
Logger.print_info("Another Client-Config is already installed! Skipped ...")
return
if client_config.get("dir").exists():
print_client_already_installed_dialog(d_name)
if get_confirm(f"Re-install {d_name}?", allow_go_back=True):
shutil.rmtree(client_config.get("dir"))
else:
return
mr_im = InstanceManager(Moonraker)
mr_instances: List[Moonraker] = mr_im.instances
kl_im = InstanceManager(Klipper)
kl_instances = kl_im.instances
try:
download_client_config(client_config)
create_client_config_symlink(client_config, kl_instances)
add_config_section(
section=f"update_manager {client_config.get('name')}",
instances=mr_instances,
options=[
("type", "git_repo"),
("primary_branch", "master"),
("path", client_config.get("mr_conf_path")),
("origin", client_config.get("mr_conf_origin")),
("managed_services", "klipper"),
],
)
add_config_section(client_config.get("printer_cfg_section"), kl_instances)
kl_im.restart_all_instance()
except Exception as e:
Logger.print_error(f"{d_name} installation failed!\n{e}")
return
Logger.print_ok(f"{d_name} installation complete!", start="\n")
def download_client_config(client_config: ClientConfigData) -> None:
try:
Logger.print_status(f"Downloading {client_config.get('display_name')} ...")
rm = RepoManager(
client_config.get("url"), target_dir=str(client_config.get("dir"))
)
rm.clone_repo()
except Exception:
Logger.print_error(f"Downloading {client_config.get('display_name')} failed!")
raise
def update_client_config(client: ClientData) -> None:
client_config: ClientConfigData = client.get("client_config")
Logger.print_status(f"Updating {client_config.get('display_name')} ...")
cm = ConfigManager(cfg_file=KIAUH_CFG)
if cm.get_value("kiauh", "backup_before_update"):
backup_client_config_data(client)
repo_manager = RepoManager(
repo=client_config.get("url"),
branch="master",
target_dir=str(client_config.get("dir")),
)
repo_manager.pull_repo()
Logger.print_ok(f"Successfully updated {client_config.get('display_name')}.")
Logger.print_warn("Remember to restart Klipper to reload the configurations!")
def create_client_config_symlink(
client_config: ClientConfigData, klipper_instances: List[Klipper] = None
) -> None:
if klipper_instances is None:
kl_im = InstanceManager(Klipper)
klipper_instances = kl_im.instances
Logger.print_status(f"Create symlink for {client_config.get('cfg_filename')} ...")
source = Path(client_config.get("dir"), client_config.get("cfg_filename"))
for instance in klipper_instances:
target = instance.cfg_dir
Logger.print_status(f"Linking {source} to {target}")
try:
create_symlink(source, target)
except subprocess.CalledProcessError:
Logger.print_error("Creating symlink failed!")

View File

@@ -0,0 +1,110 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from typing import List
from components.webui_client import ClientData
from core.menus.base_menu import print_back_footer
from utils.constants import RESET_FORMAT, COLOR_YELLOW, COLOR_CYAN
def print_moonraker_not_found_dialog():
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}No local Moonraker installation was found!{RESET_FORMAT}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| {line1:<63}|
| {line2:<63}|
|-------------------------------------------------------|
| It is possible to install Mainsail without a local |
| Moonraker installation. If you continue, you need to |
| make sure, that Moonraker is installed on another |
| machine in your network. Otherwise Mainsail will NOT |
| work correctly. |
"""
)[1:]
print(dialog, end="")
print_back_footer()
def print_client_already_installed_dialog(name: str):
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}{name} seems to be already installed!{RESET_FORMAT}"
line3 = f"If you continue, your current {name}"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| {line1:<63}|
| {line2:<63}|
|-------------------------------------------------------|
| {line3:<54}|
| installation will be overwritten. |
"""
)[1:]
print(dialog, end="")
print_back_footer()
def print_client_port_select_dialog(name: str, port: str, ports_in_use: List[str]):
port = f"{COLOR_CYAN}{port}{RESET_FORMAT}"
line1 = f"Please select the port, {name} should be served on."
line2 = f"In case you need {name} to be served on a specific"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| {line1:<54}|
| If you are unsure what to select, hit Enter to apply |
| the suggested value of: {port:38} |
| |
| {line2:<54}|
| port, you can set it now. Make sure the port is not |
| used by any other application on your system! |
"""
)[1:]
if len(ports_in_use) > 0:
dialog += "|-------------------------------------------------------|\n"
dialog += "| The following ports were found to be in use already: |\n"
for port in ports_in_use:
port = f"{COLOR_CYAN}{port}{RESET_FORMAT}"
dialog += f"| {port:60} |\n"
dialog += "\\=======================================================/\n"
print(dialog, end="")
def print_install_client_config_dialog(client: ClientData):
name = client.get("display_name")
url = client.get("client_config").get("url").replace(".git", "")
line1 = f"have {name} fully functional and working."
line2 = f"The recommended macros for {name} can be seen here:"
dialog = textwrap.dedent(
f"""
/=======================================================\\
| It is recommended to use special macros in order to |
| {line1:<54}|
| |
| {line2:<54}|
| {url:<54}|
| |
| If you already use these macros skip this step. |
| Otherwise you should consider to answer with 'Y' to |
| download the recommended macros. |
\\=======================================================/
"""
)[1:]
print(dialog, end="")

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import List
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from components.webui_client import ClientData
from components.webui_client.client_config.client_config_remove import (
run_client_config_removal,
)
from components.webui_client.client_utils import backup_mainsail_config_json
from core.instance_manager.instance_manager import InstanceManager
from utils.filesystem_utils import (
remove_nginx_config,
remove_nginx_logs,
remove_config_section,
)
from utils.logger import Logger
def run_client_removal(
client: ClientData,
rm_client: bool,
rm_client_config: bool,
backup_ms_config_json: bool,
) -> None:
mr_im = InstanceManager(Moonraker)
mr_instances: List[Moonraker] = mr_im.instances
kl_im = InstanceManager(Klipper)
kl_instances: List[Klipper] = kl_im.instances
if backup_ms_config_json and client.get("name") == "mainsail":
backup_mainsail_config_json()
if rm_client:
client_name = client.get("name")
remove_client_dir(client)
remove_nginx_config(client_name)
remove_nginx_logs(client_name)
section = f"update_manager {client_name}"
remove_config_section(section, mr_instances)
if rm_client_config:
run_client_config_removal(
client.get("client_config"),
kl_instances,
mr_instances,
)
def remove_client_dir(client: ClientData) -> None:
Logger.print_status(f"Removing {client.get('display_name')} ...")
client_dir = client.get("dir")
if not client.get("dir").exists():
Logger.print_info(f"'{client_dir}' does not exist. Skipping ...")
return
try:
shutil.rmtree(client_dir)
except OSError as e:
Logger.print_error(f"Unable to delete '{client_dir}':\n{e}")

View File

@@ -0,0 +1,207 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import List
from components.klipper.klipper import Klipper
from components.webui_client import (
ClientName,
ClientData,
)
from components.moonraker.moonraker import Moonraker
from components.webui_client.client_config.client_config_setup import (
install_client_config,
)
from components.webui_client.client_dialogs import (
print_moonraker_not_found_dialog,
print_client_port_select_dialog,
print_install_client_config_dialog,
)
from components.webui_client.client_utils import (
backup_mainsail_config_json,
restore_mainsail_config_json,
enable_mainsail_remotemode,
symlink_webui_nginx_log,
load_client_data, config_for_other_client_exist,
)
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.instance_manager import InstanceManager
from kiauh import KIAUH_CFG
from utils import NGINX_SITES_AVAILABLE, NGINX_SITES_ENABLED
from utils.common import check_install_dependencies
from utils.filesystem_utils import (
unzip,
copy_upstream_nginx_cfg,
copy_common_vars_nginx_cfg,
create_nginx_cfg,
create_symlink,
remove_file,
add_config_section,
read_ports_from_nginx_configs,
is_valid_port,
get_next_free_port,
)
from utils.input_utils import get_confirm, get_number_input
from utils.logger import Logger
from utils.system_utils import (
download_file,
set_nginx_permissions,
get_ipv4_addr,
control_systemd_service,
)
def install_client(client_name: ClientName) -> None:
client: ClientData = load_client_data(client_name)
d_name = client.get("display_name")
if client is None:
Logger.print_error("Missing parameter client_name!")
return
if client.get("dir").exists():
Logger.print_info(
f"{client.get('display_name')} seems to be already installed! Skipped ..."
)
return
mr_im = InstanceManager(Moonraker)
mr_instances: List[Moonraker] = mr_im.instances
enable_remotemode = False
if not mr_instances:
print_moonraker_not_found_dialog()
if not get_confirm(
f"Continue {d_name} installation?",
allow_go_back=True,
):
return
# if moonraker is not installed or multiple instances
# are installed we enable mainsails remote mode
if client.get("remote_mode") and not mr_instances or len(mr_instances) > 1:
enable_remotemode = True
kl_im = InstanceManager(Klipper)
kl_instances = kl_im.instances
install_client_cfg = False
client_config = client.get("client_config")
if (
kl_instances
and not client_config.get("dir").exists()
and not config_for_other_client_exist(client_to_ignore=client.get("name"))
):
print_install_client_config_dialog(client)
question = f"Download the recommended {client_config.get('display_name')}?"
install_client_cfg = get_confirm(question, allow_go_back=False)
cm = ConfigManager(cfg_file=KIAUH_CFG)
default_port = cm.get_value(client.get("name"), "port")
client_port = default_port if default_port and default_port.isdigit() else "80"
ports_in_use = read_ports_from_nginx_configs()
# check if configured port is a valid number and not in use already
valid_port = is_valid_port(client_port, ports_in_use)
while not valid_port:
next_port = get_next_free_port(ports_in_use)
print_client_port_select_dialog(d_name, next_port, ports_in_use)
client_port = str(
get_number_input(
f"Configure {d_name} for port",
min_count=int(next_port),
default=next_port,
)
)
valid_port = is_valid_port(client_port, ports_in_use)
check_install_dependencies(["nginx"])
try:
download_client(client)
if enable_remotemode and client.get("name") == "mainsail":
enable_mainsail_remotemode()
if mr_instances:
add_config_section(
section=f"update_manager {client.get('name')}",
instances=mr_instances,
options=[
("type", "web"),
("channel", "stable"),
("repo", client.get("mr_conf_repo")),
("path", client.get("mr_conf_path")),
],
)
mr_im.restart_all_instance()
if install_client_cfg and kl_instances:
install_client_config(client.get("name"))
copy_upstream_nginx_cfg()
copy_common_vars_nginx_cfg()
create_client_nginx_cfg(client, client_port)
if kl_instances:
symlink_webui_nginx_log(kl_instances)
control_systemd_service("nginx", "restart")
except Exception as e:
Logger.print_error(f"{d_name} installation failed!\n{e}")
return
log = f"Open {d_name} now on: http://{get_ipv4_addr()}:{client_port}"
Logger.print_ok(f"{d_name} installation complete!", start="\n")
Logger.print_ok(log, prefix=False, end="\n\n")
def download_client(client: ClientData) -> None:
zipfile = f"{client.get('name').lower()}.zip"
target = Path().home().joinpath(zipfile)
try:
Logger.print_status(f"Downloading {zipfile} ...")
download_file(client.get("url"), target, True)
Logger.print_ok("Download complete!")
Logger.print_status(f"Extracting {zipfile} ...")
unzip(target, client.get("dir"))
target.unlink(missing_ok=True)
Logger.print_ok("OK!")
except Exception:
Logger.print_error(f"Downloading {zipfile} failed!")
raise
def update_client(client: ClientData) -> None:
Logger.print_status(f"Updating {client.get('display_name')} ...")
if client.get("name") == "mainsail":
backup_mainsail_config_json(is_temp=True)
download_client(client)
if client.get("name") == "mainsail":
restore_mainsail_config_json()
def create_client_nginx_cfg(client: ClientData, port: int) -> None:
d_name = client.get("display_name")
root_dir = client.get("dir")
source = NGINX_SITES_AVAILABLE.joinpath(client.get("name"))
target = NGINX_SITES_ENABLED.joinpath(client.get("name"))
try:
Logger.print_status(f"Creating NGINX config for {d_name} ...")
remove_file(Path("/etc/nginx/sites-enabled/default"), True)
create_nginx_cfg(client.get("name"), port, root_dir)
create_symlink(source, target, True)
set_nginx_permissions()
Logger.print_ok(f"NGINX config for {d_name} successfully created.")
except Exception:
Logger.print_error(f"Creating NGINX config for {d_name} failed!")
raise

View File

@@ -0,0 +1,275 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
import shutil
from json import JSONDecodeError
from pathlib import Path
from typing import List, Optional, Dict, Literal, Union, get_args
import urllib.request
from components.klipper.klipper import Klipper
from components.webui_client import (
MAINSAIL_CONFIG_JSON,
MAINSAIL_DIR,
MAINSAIL_BACKUP_DIR,
FLUIDD_PRE_RLS_URL,
FLUIDD_BACKUP_DIR,
FLUIDD_URL,
FLUIDD_DIR,
ClientData,
FLUIDD_CONFIG_REPO_URL,
FLUIDD_CONFIG_DIR,
ClientConfigData,
MAINSAIL_PRE_RLS_URL,
MAINSAIL_URL,
MAINSAIL_CONFIG_REPO_URL,
MAINSAIL_CONFIG_DIR,
ClientName,
MAINSAIL_TAGS_URL,
FLUIDD_TAGS_URL,
FLUIDD_CONFIG_BACKUP_DIR,
MAINSAIL_CONFIG_BACKUP_DIR,
)
from core.backup_manager.backup_manager import BackupManager
from core.repo_manager.repo_manager import RepoManager
from utils import NGINX_SITES_AVAILABLE, NGINX_CONFD
from utils.common import get_install_status_webui
from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW
from utils.logger import Logger
def load_client_data(client_name: ClientName) -> Optional[ClientData]:
client_data = None
if client_name == "mainsail":
client_config_data = ClientConfigData(
name="mainsail-config",
display_name="Mainsail-Config",
cfg_filename="mainsail.cfg",
dir=MAINSAIL_CONFIG_DIR,
backup_dir=MAINSAIL_CONFIG_BACKUP_DIR,
url=MAINSAIL_CONFIG_REPO_URL,
printer_cfg_section="include mainsail.cfg",
mr_conf_path="~/mainsail-config",
mr_conf_origin=MAINSAIL_CONFIG_REPO_URL,
)
client_data = ClientData(
name=client_name,
display_name=client_name.capitalize(),
dir=MAINSAIL_DIR,
backup_dir=MAINSAIL_BACKUP_DIR,
url=MAINSAIL_URL,
pre_release_url=MAINSAIL_PRE_RLS_URL,
tags_url=MAINSAIL_TAGS_URL,
remote_mode=True,
mr_conf_repo="mainsail-crew/mainsail",
mr_conf_path="~/mainsail",
client_config=client_config_data,
)
elif client_name == "fluidd":
client_config_data = ClientConfigData(
name="fluidd-config",
display_name="Fluidd-Config",
cfg_filename="fluidd.cfg",
dir=FLUIDD_CONFIG_DIR,
backup_dir=FLUIDD_CONFIG_BACKUP_DIR,
url=FLUIDD_CONFIG_REPO_URL,
printer_cfg_section="include fluidd.cfg",
mr_conf_path="~/fluidd-config",
mr_conf_origin=FLUIDD_CONFIG_REPO_URL,
)
client_data = ClientData(
name=client_name,
display_name=client_name.capitalize(),
dir=FLUIDD_DIR,
backup_dir=FLUIDD_BACKUP_DIR,
url=FLUIDD_URL,
pre_release_url=FLUIDD_PRE_RLS_URL,
tags_url=FLUIDD_TAGS_URL,
remote_mode=False,
mr_conf_repo="fluidd-core/fluidd",
mr_conf_path="~/fluidd",
client_config=client_config_data,
)
return client_data
def get_client_status(client: ClientData) -> str:
return get_install_status_webui(
client.get("dir"),
NGINX_SITES_AVAILABLE.joinpath(client.get("name")),
NGINX_CONFD.joinpath("upstreams.conf"),
NGINX_CONFD.joinpath("common_vars.conf"),
)
def get_client_config_status(client: ClientData) -> Dict[
Literal["repo", "local", "remote"],
Union[str, int],
]:
client_config = client.get("client_config")
client_config = client_config.get("dir")
return {
"repo": RepoManager.get_repo_name(client_config),
"local": RepoManager.get_local_commit(client_config),
"remote": RepoManager.get_remote_commit(client_config),
}
def get_current_client_config(clients: List[ClientData]) -> str:
installed = []
for client in clients:
client_config = client.get("client_config")
if client_config.get("dir").exists():
installed.append(client)
if len(installed) > 1:
return f"{COLOR_YELLOW}Conflict!{RESET_FORMAT}"
elif len(installed) == 1:
cfg = installed[0].get("client_config")
return f"{COLOR_CYAN}{cfg.get('display_name')}{RESET_FORMAT}"
return f"{COLOR_CYAN}-{RESET_FORMAT}"
def backup_mainsail_config_json(is_temp=False) -> None:
Logger.print_status(f"Backup '{MAINSAIL_CONFIG_JSON}' ...")
bm = BackupManager()
if is_temp:
fn = Path.home().joinpath("config.json.kiauh.bak")
bm.backup_file(MAINSAIL_CONFIG_JSON, custom_filename=fn)
else:
bm.backup_file(MAINSAIL_CONFIG_JSON)
def restore_mainsail_config_json() -> None:
try:
Logger.print_status(f"Restore '{MAINSAIL_CONFIG_JSON}' ...")
source = Path.home().joinpath("config.json.kiauh.bak")
shutil.copy(source, MAINSAIL_CONFIG_JSON)
except OSError:
Logger.print_info("Unable to restore config.json. Skipped ...")
def enable_mainsail_remotemode() -> None:
Logger.print_status("Enable Mainsails remote mode ...")
with open(MAINSAIL_CONFIG_JSON, "r") as f:
config_data = json.load(f)
if config_data["instancesDB"] == "browser":
Logger.print_info("Remote mode already configured. Skipped ...")
return
Logger.print_status("Setting instance storage location to 'browser' ...")
config_data["instancesDB"] = "browser"
with open(MAINSAIL_CONFIG_JSON, "w") as f:
json.dump(config_data, f, indent=4)
Logger.print_ok("Mainsails remote mode enabled!")
def symlink_webui_nginx_log(klipper_instances: List[Klipper]) -> None:
Logger.print_status("Link NGINX logs into log directory ...")
access_log = Path("/var/log/nginx/mainsail-access.log")
error_log = Path("/var/log/nginx/mainsail-error.log")
for instance in klipper_instances:
desti_access = instance.log_dir.joinpath("mainsail-access.log")
if not desti_access.exists():
desti_access.symlink_to(access_log)
desti_error = instance.log_dir.joinpath("mainsail-error.log")
if not desti_error.exists():
desti_error.symlink_to(error_log)
def get_local_client_version(client: ClientData) -> str:
relinfo_file = client.get("dir").joinpath("release_info.json")
if not relinfo_file.is_file():
return "-"
with open(relinfo_file, "r") as f:
return json.load(f)["version"]
def get_remote_client_version(client: ClientData) -> str:
try:
with urllib.request.urlopen(client.get("tags_url")) as response:
data = json.loads(response.read())
return data[0]["name"]
except (JSONDecodeError, TypeError):
return "ERROR"
def backup_client_data(client: ClientData) -> None:
name = client.get("name")
src = client.get("dir")
dest = client.get("backup_dir")
with open(src.joinpath(".version"), "r") as v:
version = v.readlines()[0]
bm = BackupManager()
bm.backup_directory(f"{name}-{version}", src, dest)
if name == "mainsail":
bm.backup_file(MAINSAIL_CONFIG_JSON, dest)
bm.backup_file(NGINX_SITES_AVAILABLE.joinpath(name), dest)
def backup_client_config_data(client: ClientData) -> None:
client_config = client.get("client_config")
name = client_config.get("name")
source = client_config.get("dir")
target = client_config.get("backup_dir")
bm = BackupManager()
bm.backup_directory(name, source, target)
def get_existing_clients() -> List[ClientData]:
clients = list(get_args(ClientName))
installed_clients: List[ClientData] = []
for c in clients:
c_data: ClientData = load_client_data(c)
if c_data.get("dir").exists():
installed_clients.append(c_data)
return installed_clients
def get_existing_client_config() -> List[ClientData]:
clients = list(get_args(ClientName))
installed_client_configs: List[ClientData] = []
for c in clients:
c_data: ClientData = load_client_data(c)
c_config_data: ClientConfigData = c_data.get("client_config")
if c_config_data.get("dir").exists():
installed_client_configs.append(c_data)
return installed_client_configs
def config_for_other_client_exist(client_to_ignore: ClientName) -> bool:
"""
Check if any other client configs are present on the system.
It is usually not harmful, but chances are they can conflict each other.
Multiple client configs are, at least, redundant to have them installed
:param client_to_ignore: The client name to ignore for the check
:return: True, if other client configs were found, else False
"""
clients = set([c["name"] for c in get_existing_client_config()])
clients = clients - {client_to_ignore}
return True if len(clients) > 0 else False

View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from typing import Callable, Dict
from components.webui_client import client_remove, ClientData
from core.menus import BACK_HELP_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import RESET_FORMAT, COLOR_RED, COLOR_CYAN
# noinspection PyUnusedLocal
class ClientRemoveMenu(BaseMenu):
def __init__(self, client: ClientData):
self.client = client
self.rm_client = False
self.rm_client_config = False
self.backup_mainsail_config_json = False
super().__init__(
header=False,
options=self.get_options(),
footer_type=BACK_HELP_FOOTER,
)
def get_options(self) -> Dict[str, Callable]:
options = {
"0": self.toggle_all,
"1": self.toggle_rm_client,
"2": self.toggle_rm_client_config,
"c": self.run_removal_process,
}
if self.client.get("name") == "mainsail":
options["3"] = self.toggle_backup_mainsail_config_json
return options
def print_menu(self) -> None:
client_name = self.client.get("display_name")
client_config = self.client.get("client_config")
client_config_name = client_config.get("display_name")
header = f" [ Remove {client_name} ] "
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
checked = f"[{COLOR_CYAN}x{RESET_FORMAT}]"
unchecked = "[ ]"
o1 = checked if self.rm_client else unchecked
o2 = checked if self.rm_client_config else unchecked
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| Enter a number and hit enter to select / deselect |
| the specific option for removal. |
|-------------------------------------------------------|
| 0) Select everything |
|-------------------------------------------------------|
| 1) {o1} Remove {client_name:16} |
| 2) {o2} Remove {client_config_name:24} |
"""
)[1:]
if self.client.get("name") == "mainsail":
o3 = checked if self.backup_mainsail_config_json else unchecked
menu += textwrap.dedent(
f"""
| 3) {o3} Backup config.json |
"""
)[1:]
menu += textwrap.dedent(
"""
|-------------------------------------------------------|
| C) Continue |
"""
)[1:]
print(menu, end="")
def toggle_all(self, **kwargs) -> None:
self.rm_client = True
self.rm_client_config = True
self.backup_mainsail_config_json = True
def toggle_rm_client(self, **kwargs) -> None:
self.rm_client = not self.rm_client
def toggle_rm_client_config(self, **kwargs) -> None:
self.rm_client_config = not self.rm_client_config
def toggle_backup_mainsail_config_json(self, **kwargs) -> None:
self.backup_mainsail_config_json = not self.backup_mainsail_config_json
def run_removal_process(self, **kwargs) -> None:
if (
not self.rm_client
and not self.rm_client_config
and not self.backup_mainsail_config_json
):
error = f"{COLOR_RED}Nothing selected ...{RESET_FORMAT}"
print(error)
return
client_remove.run_client_removal(
client=self.client,
rm_client=self.rm_client,
rm_client_config=self.rm_client_config,
backup_ms_config_json=self.backup_mainsail_config_json,
)
self.rm_client = False
self.rm_client_config = False
self.backup_mainsail_config_json = False

0
kiauh/core/__init__.py Normal file
View File

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
BACKUP_ROOT_DIR = Path.home().joinpath("kiauh-backups")

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import List
from core.backup_manager import BACKUP_ROOT_DIR
from utils.common import get_current_date
from utils.logger import Logger
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class BackupManager:
def __init__(self, backup_root_dir: Path = BACKUP_ROOT_DIR):
self._backup_root_dir = backup_root_dir
self._ignore_folders = None
@property
def backup_root_dir(self) -> Path:
return self._backup_root_dir
@backup_root_dir.setter
def backup_root_dir(self, value: Path):
self._backup_root_dir = value
@property
def ignore_folders(self) -> List[str]:
return self._ignore_folders
@ignore_folders.setter
def ignore_folders(self, value: List[str]):
self._ignore_folders = value
def backup_file(self, file: Path = None, target: Path = None, custom_filename=None):
if not file:
raise ValueError("Parameter 'file' cannot be None!")
target = self.backup_root_dir if target is None else target
Logger.print_status(f"Creating backup of {file} ...")
if Path(file).is_file():
date = get_current_date().get("date")
time = get_current_date().get("time")
filename = f"{file.stem}-{date}-{time}{file.suffix}"
filename = custom_filename if custom_filename is not None else filename
try:
Path(target).mkdir(exist_ok=True)
shutil.copyfile(file, target.joinpath(filename))
Logger.print_ok("Backup successful!")
except OSError as e:
Logger.print_error(f"Unable to backup '{file}':\n{e}")
else:
Logger.print_info(f"File '{file}' not found ...")
def backup_directory(self, name: str, source: Path, target: Path = None) -> None:
if source is None or not Path(source).exists():
raise OSError("Parameter 'source' is None or Path does not exist!")
target = self.backup_root_dir if target is None else target
try:
log = f"Creating backup of {name} in {target} ..."
Logger.print_status(log)
date = get_current_date().get("date")
time = get_current_date().get("time")
shutil.copytree(
source,
target.joinpath(f"{name.lower()}-{date}-{time}"),
ignore=self.ignore_folders_func,
)
Logger.print_ok("Backup successful!")
except OSError as e:
Logger.print_error(f"Unable to backup directory '{source}':\n{e}")
return
def ignore_folders_func(self, dirpath, filenames):
return (
[f for f in filenames if f in self._ignore_folders]
if self._ignore_folders is not None
else []
)

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 abc import abstractmethod, ABC
from typing import Dict
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class BaseExtension(ABC):
def __init__(self, metadata: Dict[str, str]):
self.metadata = metadata
@abstractmethod
def install_extension(self, **kwargs) -> None:
raise NotImplementedError(
"Subclasses must implement the install_extension method"
)
@abstractmethod
def remove_extension(self, **kwargs) -> None:
raise NotImplementedError(
"Subclasses must implement the remove_extension method"
)

View File

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 configparser
from pathlib import Path
from typing import Union
from utils.logger import Logger
# noinspection PyMethodMayBeStatic
class ConfigManager:
def __init__(self, cfg_file: Path):
self.config_file = cfg_file
self.config = CustomConfigParser()
if cfg_file.is_file():
self.read_config()
def read_config(self) -> None:
if not self.config_file:
Logger.print_error("Unable to read config file. File not found.")
return
self.config.read_file(open(self.config_file, "r"))
def write_config(self) -> None:
with open(self.config_file, "w") as cfg:
self.config.write(cfg)
def get_value(self, section: str, key: str, silent=True) -> Union[str, bool, None]:
if not self.config.has_section(section):
if not silent:
log = f"Section not defined. Unable to read section: [{section}]."
Logger.print_error(log)
return None
if not self.config.has_option(section, key):
if not silent:
log = f"Option not defined in section [{section}]. Unable to read option: '{key}'."
Logger.print_error(log)
return None
value = self.config.get(section, key)
if value == "True" or value == "true":
return True
elif value == "False" or value == "false":
return False
else:
return value
def set_value(self, section: str, key: str, value: str):
self.config.set(section, key, value)
class CustomConfigParser(configparser.ConfigParser):
"""
A custom ConfigParser class overwriting the write() method of configparser.Configparser.
Key and value will be delimited by a ": ".
Note the whitespace AFTER the colon, which is the whole reason for that overwrite.
"""
def write(self, fp, space_around_delimiters=False):
if self._defaults:
fp.write("[%s]\n" % configparser.DEFAULTSECT)
for key, value in self._defaults.items():
fp.write("%s: %s\n" % (key, str(value).replace("\n", "\n\t")))
fp.write("\n")
for section in self._sections:
fp.write("[%s]\n" % section)
for key, value in self._sections[section].items():
if key == "__name__":
continue
if (value is not None) or (self._optcre == self.OPTCRE):
key = ": ".join((key, str(value).replace("\n", "\n\t")))
fp.write("%s\n" % key)
fp.write("\n")

View File

View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 abc import abstractmethod, ABC
from pathlib import Path
from typing import List, Type, TypeVar
from utils.constants import SYSTEMD, CURRENT_USER
B = TypeVar(name="B", bound="BaseInstance", covariant=True)
class BaseInstance(ABC):
@classmethod
def blacklist(cls) -> List[str]:
return []
def __init__(
self,
suffix: str,
instance_type: B = B,
):
self._instance_type = instance_type
self._suffix = suffix
self._user = CURRENT_USER
self._data_dir_name = self.get_data_dir_name_from_suffix()
self._data_dir = Path.home().joinpath(f"{self._data_dir_name}_data")
self._cfg_dir = self.data_dir.joinpath("config")
self._log_dir = self.data_dir.joinpath("logs")
self._comms_dir = self.data_dir.joinpath("comms")
self._sysd_dir = self.data_dir.joinpath("systemd")
self._gcodes_dir = self.data_dir.joinpath("gcodes")
@property
def instance_type(self) -> Type["BaseInstance"]:
return self._instance_type
@instance_type.setter
def instance_type(self, value: Type["BaseInstance"]) -> None:
self._instance_type = value
@property
def suffix(self) -> str:
return self._suffix
@suffix.setter
def suffix(self, value: str) -> None:
self._suffix = value
@property
def user(self) -> str:
return self._user
@user.setter
def user(self, value: str) -> None:
self._user = value
@property
def data_dir_name(self) -> str:
return self._data_dir_name
@data_dir_name.setter
def data_dir_name(self, value: str) -> None:
self._data_dir_name = value
@property
def data_dir(self) -> Path:
return self._data_dir
@data_dir.setter
def data_dir(self, value: str) -> None:
self._data_dir = value
@property
def cfg_dir(self) -> Path:
return self._cfg_dir
@cfg_dir.setter
def cfg_dir(self, value: str) -> None:
self._cfg_dir = value
@property
def log_dir(self) -> Path:
return self._log_dir
@log_dir.setter
def log_dir(self, value: str) -> None:
self._log_dir = value
@property
def comms_dir(self) -> Path:
return self._comms_dir
@comms_dir.setter
def comms_dir(self, value: str) -> None:
self._comms_dir = value
@property
def sysd_dir(self) -> Path:
return self._sysd_dir
@sysd_dir.setter
def sysd_dir(self, value: str) -> None:
self._sysd_dir = value
@property
def gcodes_dir(self) -> Path:
return self._gcodes_dir
@gcodes_dir.setter
def gcodes_dir(self, value: str) -> None:
self._gcodes_dir = value
@abstractmethod
def create(self) -> None:
raise NotImplementedError("Subclasses must implement the create method")
@abstractmethod
def delete(self) -> None:
raise NotImplementedError("Subclasses must implement the delete method")
def create_folders(self, add_dirs: List[Path] = None) -> None:
dirs = [
self.data_dir,
self.cfg_dir,
self.log_dir,
self.comms_dir,
self.sysd_dir,
]
if add_dirs:
dirs.extend(add_dirs)
for _dir in dirs:
_dir.mkdir(exist_ok=True)
def get_service_file_name(self, extension: bool = False) -> str:
name = f"{self.__class__.__name__.lower()}"
if self.suffix != "":
name += f"-{self.suffix}"
return name if not extension else f"{name}.service"
def get_service_file_path(self) -> Path:
return SYSTEMD.joinpath(self.get_service_file_name(extension=True))
def get_data_dir_name_from_suffix(self) -> str:
if self._suffix == "":
return "printer"
elif self._suffix.isdigit():
return f"printer_{self._suffix}"
else:
return self._suffix

View File

@@ -0,0 +1,213 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 re
import subprocess
from pathlib import Path
from typing import List, Optional, Union, TypeVar
from core.instance_manager.base_instance import BaseInstance
from utils.constants import SYSTEMD
from utils.logger import Logger
I = TypeVar(name="I", bound=BaseInstance, covariant=True)
# noinspection PyMethodMayBeStatic
class InstanceManager:
def __init__(self, instance_type: I) -> None:
self._instance_type = instance_type
self._current_instance: Optional[I] = None
self._instance_suffix: Optional[str] = None
self._instance_service: Optional[str] = None
self._instance_service_full: Optional[str] = None
self._instance_service_path: Optional[str] = None
self._instances: List[I] = []
@property
def instance_type(self) -> I:
return self._instance_type
@instance_type.setter
def instance_type(self, value: I):
self._instance_type = value
@property
def current_instance(self) -> I:
return self._current_instance
@current_instance.setter
def current_instance(self, value: I) -> None:
self._current_instance = value
self.instance_suffix = value.suffix
self.instance_service = value.get_service_file_name()
self.instance_service_path = value.get_service_file_path()
@property
def instance_suffix(self) -> str:
return self._instance_suffix
@instance_suffix.setter
def instance_suffix(self, value: str):
self._instance_suffix = value
@property
def instance_service(self) -> str:
return self._instance_service
@instance_service.setter
def instance_service(self, value: str):
self._instance_service = value
@property
def instance_service_full(self) -> str:
return f"{self._instance_service}.service"
@property
def instance_service_path(self) -> str:
return self._instance_service_path
@instance_service_path.setter
def instance_service_path(self, value: str):
self._instance_service_path = value
@property
def instances(self) -> List[I]:
return self.find_instances()
@instances.setter
def instances(self, value: List[I]):
self._instances = value
def create_instance(self) -> None:
if self.current_instance is not None:
try:
self.current_instance.create()
except (OSError, subprocess.CalledProcessError) as e:
Logger.print_error(f"Creating instance failed: {e}")
raise
else:
raise ValueError("current_instance cannot be None")
def delete_instance(self) -> None:
if self.current_instance is not None:
try:
self.current_instance.delete()
except (OSError, subprocess.CalledProcessError) as e:
Logger.print_error(f"Removing instance failed: {e}")
raise
else:
raise ValueError("current_instance cannot be None")
def enable_instance(self) -> None:
Logger.print_status(f"Enabling {self.instance_service_full} ...")
try:
command = ["sudo", "systemctl", "enable", self.instance_service_full]
if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_service_full} enabled.")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error enabling service {self.instance_service_full}:")
Logger.print_error(f"{e}")
def disable_instance(self) -> None:
Logger.print_status(f"Disabling {self.instance_service_full} ...")
try:
command = ["sudo", "systemctl", "disable", self.instance_service_full]
if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_service_full} disabled.")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error disabling {self.instance_service_full}:")
Logger.print_error(f"{e}")
def start_instance(self) -> None:
Logger.print_status(f"Starting {self.instance_service_full} ...")
try:
command = ["sudo", "systemctl", "start", self.instance_service_full]
if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_service_full} started.")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error starting {self.instance_service_full}:")
Logger.print_error(f"{e}")
def restart_instance(self) -> None:
Logger.print_status(f"Restarting {self.instance_service_full} ...")
try:
command = ["sudo", "systemctl", "restart", self.instance_service_full]
if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_service_full} restarted.")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error restarting {self.instance_service_full}:")
Logger.print_error(f"{e}")
def start_all_instance(self) -> None:
for instance in self.instances:
self.current_instance = instance
self.start_instance()
def restart_all_instance(self) -> None:
for instance in self.instances:
self.current_instance = instance
self.restart_instance()
def stop_instance(self) -> None:
Logger.print_status(f"Stopping {self.instance_service_full} ...")
try:
command = ["sudo", "systemctl", "stop", self.instance_service_full]
if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_service_full} stopped.")
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error stopping {self.instance_service_full}:")
Logger.print_error(f"{e}")
raise
def stop_all_instance(self) -> None:
for instance in self.instances:
self.current_instance = instance
self.stop_instance()
def reload_daemon(self) -> None:
Logger.print_status("Reloading systemd manager configuration ...")
try:
command = ["sudo", "systemctl", "daemon-reload"]
if subprocess.run(command, check=True):
Logger.print_ok("Systemd manager configuration reloaded")
except subprocess.CalledProcessError as e:
Logger.print_error("Error reloading systemd manager configuration:")
Logger.print_error(f"{e}")
raise
def find_instances(self) -> List[I]:
name = self.instance_type.__name__.lower()
pattern = re.compile(f"^{name}(-[0-9a-zA-Z]+)?.service$")
excluded = self.instance_type.blacklist()
service_list = [
Path(SYSTEMD, service)
for service in SYSTEMD.iterdir()
if pattern.search(service.name)
and not any(s in service.name for s in excluded)
]
instance_list = [
self.instance_type(suffix=self._get_instance_suffix(service))
for service in service_list
]
return sorted(instance_list, key=lambda x: self._sort_instance_list(x.suffix))
def _get_instance_suffix(self, file_path: Path) -> str:
return file_path.stem.split("-")[-1] if "-" in file_path.stem else ""
def _sort_instance_list(self, s: Union[int, str, None]):
if s is None:
return
return int(s) if s.isdigit() else s

View File

@@ -0,0 +1,8 @@
from enum import unique, Enum
@unique
class NameScheme(Enum):
SINGLE = "SINGLE"
INDEX = "INDEX"
CUSTOM = "CUSTOM"

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 #
# ======================================================================= #
QUIT_FOOTER = "quit"
BACK_FOOTER = "back"
BACK_HELP_FOOTER = "back_help"

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import COLOR_YELLOW, RESET_FORMAT
class AdvancedMenu(BaseMenu):
def __init__(self):
super().__init__(header=True, options={}, footer_type=BACK_FOOTER)
def print_menu(self):
header = " [ Advanced Menu ] "
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| Klipper & API: | Mainsail: |
| 0) [Rollback] | 5) [Theme installer] |
| | |
| Firmware: | System: |
| 1) [Build only] | 6) [Change hostname] |
| 2) [Flash only] | |
| 3) [Build + Flash] | Extras: |
| 4) [Get MCU ID] | 7) [G-Code Shell Command] |
"""
)[1:]
print(menu, end="")

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.klipper.klipper_utils import backup_klipper_dir
from components.moonraker.moonraker_utils import (
backup_moonraker_dir,
backup_moonraker_db_dir,
)
from components.webui_client.client_utils import (
backup_client_data,
load_client_data,
backup_client_config_data,
)
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.common import backup_printer_config_dir
from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class BackupMenu(BaseMenu):
def __init__(self):
super().__init__(
header=True,
options={
"1": self.backup_klipper,
"2": self.backup_moonraker,
"3": self.backup_printer_config,
"4": self.backup_moonraker_db,
"5": self.backup_mainsail,
"6": self.backup_fluidd,
"7": self.backup_mainsail_config,
"8": self.backup_fluidd_config,
"9": self.backup_klipperscreen,
},
footer_type=BACK_FOOTER,
)
def print_menu(self):
header = " [ Backup Menu ] "
line1 = f"{COLOR_YELLOW}INFO: Backups are located in '~/kiauh-backups'{RESET_FORMAT}"
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| {line1:^62} |
|-------------------------------------------------------|
| Klipper & Moonraker API: | Client-Config: |
| 1) [Klipper] | 7) [Mainsail-Config] |
| 2) [Moonraker] | 8) [Fluidd-Config] |
| 3) [Config Folder] | |
| 4) [Moonraker Database] | Touchscreen GUI: |
| | 9) [KlipperScreen] |
| Webinterface: | |
| 5) [Mainsail] | |
| 6) [Fluidd] | |
"""
)[1:]
print(menu, end="")
def backup_klipper(self, **kwargs):
backup_klipper_dir()
def backup_moonraker(self, **kwargs):
backup_moonraker_dir()
def backup_printer_config(self, **kwargs):
backup_printer_config_dir()
def backup_moonraker_db(self, **kwargs):
backup_moonraker_db_dir()
def backup_mainsail(self, **kwargs):
backup_client_data(load_client_data("mainsail"))
def backup_fluidd(self, **kwargs):
backup_client_data(load_client_data("fluidd"))
def backup_mainsail_config(self, **kwargs):
backup_client_config_data(load_client_data("mainsail"))
def backup_fluidd_config(self, **kwargs):
backup_client_config_data(load_client_data("fluidd"))
def backup_klipperscreen(self, **kwargs):
pass

View File

@@ -0,0 +1,189 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 subprocess
import sys
import textwrap
from abc import abstractmethod, ABC
from typing import Dict, Any, Literal, Union, Callable
from core.menus import QUIT_FOOTER, BACK_FOOTER, BACK_HELP_FOOTER
from utils.constants import (
COLOR_GREEN,
COLOR_YELLOW,
COLOR_RED,
COLOR_CYAN,
RESET_FORMAT,
)
from utils.logger import Logger
def clear():
subprocess.call("clear", shell=True)
def print_header():
line1 = " [ KIAUH ] "
line2 = "Klipper Installation And Update Helper"
line3 = ""
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)
header = textwrap.dedent(
f"""
/=======================================================\\
| {color}{line1:~^{count}}{RESET_FORMAT} |
| {color}{line2:^{count}}{RESET_FORMAT} |
| {color}{line3:~^{count}}{RESET_FORMAT} |
\=======================================================/
"""
)[1:]
print(header, end="")
def print_quit_footer():
text = "Q) Quit"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
footer = textwrap.dedent(
f"""
|-------------------------------------------------------|
| {color}{text:^{count}}{RESET_FORMAT} |
\=======================================================/
"""
)[1:]
print(footer, end="")
def print_back_footer():
text = "B) « Back"
color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT)
footer = textwrap.dedent(
f"""
|-------------------------------------------------------|
| {color}{text:^{count}}{RESET_FORMAT} |
\=======================================================/
"""
)[1:]
print(footer, end="")
def print_back_help_footer():
text1 = "B) « Back"
text2 = "H) Help [?]"
color1 = COLOR_GREEN
color2 = COLOR_YELLOW
count = 34 - len(color1) - len(RESET_FORMAT)
footer = textwrap.dedent(
f"""
|-------------------------------------------------------|
| {color1}{text1:^{count}}{RESET_FORMAT} | {color2}{text2:^{count}}{RESET_FORMAT} |
\=======================================================/
"""
)[1:]
print(footer, end="")
class BaseMenu(ABC):
NAVI_OPTIONS = {"quit": ["q"], "back": ["b"], "back_help": ["b", "h"]}
def __init__(
self,
options: Dict[str, Union[Callable, Any]],
options_offset: int = 0,
header: bool = True,
footer_type: Literal[
"QUIT_FOOTER", "BACK_FOOTER", "BACK_HELP_FOOTER"
] = QUIT_FOOTER,
):
self.previous_menu = None
self.options = options
self.options_offset = options_offset
self.header = header
self.footer_type = footer_type
@abstractmethod
def print_menu(self) -> None:
raise NotImplementedError("Subclasses must implement the print_menu method")
def print_footer(self) -> None:
footer_type_map = {
QUIT_FOOTER: print_quit_footer,
BACK_FOOTER: print_back_footer,
BACK_HELP_FOOTER: print_back_help_footer,
}
footer_function = footer_type_map.get(self.footer_type, print_quit_footer)
footer_function()
def display(self) -> None:
# clear()
if self.header:
print_header()
self.print_menu()
self.print_footer()
def handle_user_input(self) -> str:
while True:
choice = input(f"{COLOR_CYAN}###### Perform action: {RESET_FORMAT}").lower()
option = self.options.get(choice, None)
has_navi_option = self.footer_type in self.NAVI_OPTIONS
user_navigated = choice in self.NAVI_OPTIONS[self.footer_type]
if has_navi_option and user_navigated:
return choice
if option is not None:
return choice
else:
Logger.print_error("Invalid input!", False)
def start(self) -> None:
while True:
self.display()
choice = self.handle_user_input()
if choice == "q":
Logger.print_ok("###### Happy printing!", False)
sys.exit(0)
elif choice == "b":
return
elif choice == "h":
print("help!")
else:
self.execute_option(choice)
def execute_option(self, choice: str) -> None:
option = self.options.get(choice, None)
if isinstance(option, type) and issubclass(option, BaseMenu):
self.navigate_to_menu(option, True)
elif isinstance(option, BaseMenu):
self.navigate_to_menu(option, False)
elif callable(option):
option(opt_index=choice)
elif option is None:
raise NotImplementedError(f"No implementation for option {choice}")
else:
raise TypeError(
f"Type {type(option)} of option {choice} not of type BaseMenu or Method"
)
def navigate_to_menu(self, menu, instantiate: bool) -> None:
"""
Method for handling the actual menu switch. Can either take in a menu type or an already
instantiated menu class. Use instantiated menu classes only if the menu requires specific input parameters
:param menu: A menu type or menu instance
:param instantiate: Specify if the menu requires instantiation
:return: None
"""
menu = menu() if instantiate else menu
menu.previous_menu = self
menu.start()

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 importlib
import inspect
import json
import textwrap
from pathlib import Path
from typing import List, Dict
from core.base_extension import BaseExtension
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import RESET_FORMAT, COLOR_CYAN, COLOR_YELLOW
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class ExtensionsMenu(BaseMenu):
def __init__(self):
self.extensions = self.discover_extensions()
super().__init__(
header=True,
options=self.get_options(),
footer_type=BACK_FOOTER,
)
def discover_extensions(self) -> List[BaseExtension]:
extensions = []
extensions_dir = Path(__file__).resolve().parents[2].joinpath("extensions")
for extension in extensions_dir.iterdir():
metadata_json = Path(extension).joinpath("metadata.json")
if not metadata_json.exists():
continue
try:
with open(metadata_json, "r") as m:
metadata = json.load(m).get("metadata")
module_name = (
f"kiauh.extensions.{extension.name}.{metadata.get('module')}"
)
name, extension = inspect.getmembers(
importlib.import_module(module_name),
predicate=lambda o: inspect.isclass(o)
and issubclass(o, BaseExtension)
and o != BaseExtension,
)[0]
extensions.append(extension(metadata))
except (IOError, json.JSONDecodeError, ImportError) as e:
print(f"Failed loading extension {extension}: {e}")
return sorted(extensions, key=lambda ex: ex.metadata.get("index"))
def get_options(self) -> Dict[str, BaseMenu]:
options = {}
for extension in self.extensions:
index = extension.metadata.get("index")
options[f"{index}"] = ExtensionSubmenu(extension)
return options
def print_menu(self):
header = " [ Extensions Menu ] "
color = COLOR_CYAN
line1 = f"{COLOR_YELLOW}Available Extensions:{RESET_FORMAT}"
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| {line1:<62} |
| |
"""
)[1:]
print(menu, end="")
for extension in self.extensions:
index = extension.metadata.get("index")
name = extension.metadata.get("display_name")
row = f"{index}) {name}"
print(f"| {row:<53} |")
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class ExtensionSubmenu(BaseMenu):
def __init__(self, extension: BaseExtension):
self.extension = extension
self.extension_name = extension.metadata.get("display_name")
self.extension_desc = extension.metadata.get("description")
super().__init__(
header=False,
options={
"1": extension.install_extension,
"2": extension.remove_extension,
},
footer_type=BACK_FOOTER,
)
def print_menu(self) -> None:
header = f" [ {self.extension_name} ] "
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
wrapper = textwrap.TextWrapper(55, initial_indent="| ", subsequent_indent="| ")
lines = wrapper.wrap(self.extension_desc)
formatted_lines = [f"{line:<55} |" for line in lines]
description_text = "\n".join(formatted_lines)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
"""
)[1:]
menu += f"{description_text}\n"
menu += textwrap.dedent(
"""
|-------------------------------------------------------|
| 1) Install |
| 2) Remove |
"""
)[1:]
print(menu, end="")

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.klipper import klipper_setup
from components.moonraker import moonraker_setup
from components.webui_client import client_setup
from components.webui_client.client_config import client_config_setup
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import COLOR_GREEN, RESET_FORMAT
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class InstallMenu(BaseMenu):
def __init__(self):
super().__init__(
header=True,
options={
"1": self.install_klipper,
"2": self.install_moonraker,
"3": self.install_mainsail,
"4": self.install_fluidd,
"5": self.install_mainsail_config,
"6": self.install_fluidd_config,
"7": None,
"8": None,
"9": None,
},
footer_type=BACK_FOOTER,
)
def print_menu(self):
header = " [ Installation Menu ] "
color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| Firmware & API: | Touchscreen GUI: |
| 1) [Klipper] | 7) [KlipperScreen] |
| 2) [Moonraker] | |
| | Android / iOS: |
| Webinterface: | 8) [Mobileraker] |
| 3) [Mainsail] | |
| 4) [Fluidd] | Webcam Streamer: |
| | 9) [Crowsnest] |
| Client-Config: | |
| 5) [Mainsail-Config] | |
| 6) [Fluidd-Config] | |
| | |
"""
)[1:]
print(menu, end="")
def install_klipper(self, **kwargs):
klipper_setup.install_klipper()
def install_moonraker(self, **kwargs):
moonraker_setup.install_moonraker()
def install_mainsail(self, **kwargs):
client_setup.install_client(client_name="mainsail")
def install_mainsail_config(self, **kwargs):
client_config_setup.install_client_config(client_name="mainsail")
def install_fluidd(self, **kwargs):
client_setup.install_client(client_name="fluidd")
def install_fluidd_config(self, **kwargs):
client_config_setup.install_client_config(client_name="fluidd")

View File

@@ -0,0 +1,139 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.klipper.klipper_utils import get_klipper_status
from components.log_uploads.menus.log_upload_menu import LogUploadMenu
from components.moonraker.moonraker_utils import get_moonraker_status
from components.webui_client.client_utils import (
get_client_status,
load_client_data,
get_current_client_config,
)
from core.menus import QUIT_FOOTER
from core.menus.advanced_menu import AdvancedMenu
from core.menus.backup_menu import BackupMenu
from core.menus.base_menu import BaseMenu
from core.menus.extensions_menu import ExtensionsMenu
from core.menus.install_menu import InstallMenu
from core.menus.remove_menu import RemoveMenu
from core.menus.settings_menu import SettingsMenu
from core.menus.update_menu import UpdateMenu
from utils.constants import (
COLOR_MAGENTA,
COLOR_CYAN,
RESET_FORMAT,
COLOR_RED,
COLOR_GREEN,
COLOR_YELLOW,
)
# noinspection PyMethodMayBeStatic
class MainMenu(BaseMenu):
def __init__(self):
super().__init__(
header=True,
options={
"0": LogUploadMenu,
"1": InstallMenu,
"2": UpdateMenu,
"3": RemoveMenu,
"4": AdvancedMenu,
"5": BackupMenu,
"e": ExtensionsMenu,
"s": SettingsMenu,
},
footer_type=QUIT_FOOTER,
)
self.kl_status = ""
self.kl_repo = ""
self.mr_status = ""
self.mr_repo = ""
self.ms_status = ""
self.fl_status = ""
self.ks_status = ""
self.mb_status = ""
self.cn_status = ""
self.cc_status = ""
self.init_status()
def init_status(self) -> None:
status_vars = ["kl", "mr", "ms", "fl", "ks", "mb", "cn"]
for var in status_vars:
setattr(self, f"{var}_status", f"{COLOR_RED}Not installed!{RESET_FORMAT}")
def fetch_status(self) -> None:
# klipper
klipper_status = get_klipper_status()
kl_status = klipper_status.get("status")
kl_code = klipper_status.get("status_code")
kl_instances = f" {klipper_status.get('instances')}" if kl_code == 1 else ""
self.kl_status = self.format_status_by_code(kl_code, kl_status, kl_instances)
self.kl_repo = f"{COLOR_CYAN}{klipper_status.get('repo')}{RESET_FORMAT}"
# moonraker
moonraker_status = get_moonraker_status()
mr_status = moonraker_status.get("status")
mr_code = moonraker_status.get("status_code")
mr_instances = f" {moonraker_status.get('instances')}" if mr_code == 1 else ""
self.mr_status = self.format_status_by_code(mr_code, mr_status, mr_instances)
self.mr_repo = f"{COLOR_CYAN}{moonraker_status.get('repo')}{RESET_FORMAT}"
# mainsail
mainsail_client_data = load_client_data("mainsail")
self.ms_status = get_client_status(mainsail_client_data)
# fluidd
fluidd_client_data = load_client_data("fluidd")
self.fl_status = get_client_status(fluidd_client_data)
# client-config
self.cc_status = get_current_client_config(
[mainsail_client_data, fluidd_client_data]
)
def format_status_by_code(self, code: int, status: str, count: str) -> str:
if code == 1:
return f"{COLOR_GREEN}{status}{count}{RESET_FORMAT}"
elif code == 2:
return f"{COLOR_RED}{status}{count}{RESET_FORMAT}"
return f"{COLOR_YELLOW}{status}{count}{RESET_FORMAT}"
def print_menu(self):
self.fetch_status()
header = " [ Main Menu ] "
footer1 = "KIAUH v6.0.0"
footer2 = f"Changelog: {COLOR_MAGENTA}https://git.io/JnmlX{RESET_FORMAT}"
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| 0) [Log-Upload] | Klipper: {self.kl_status:<32} |
| | Repo: {self.kl_repo:<32} |
| 1) [Install] |------------------------------------|
| 2) [Update] | Moonraker: {self.mr_status:<32} |
| 3) [Remove] | Repo: {self.mr_repo:<32} |
| 4) [Advanced] |------------------------------------|
| 5) [Backup] | Mainsail: {self.ms_status:<26} |
| | Fluidd: {self.fl_status:<26} |
| S) [Settings] | Client-Config: {self.cc_status:<26} |
| | |
| Community: | KlipperScreen: {self.ks_status:<26} |
| E) [Extensions] | Mobileraker: {self.mb_status:<26} |
| | Crowsnest: {self.cn_status:<26} |
|-------------------------------------------------------|
| {COLOR_CYAN}{footer1:^16}{RESET_FORMAT} | {footer2:^43} |
"""
)[1:]
print(menu, end="")

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu
from components.moonraker.menus.moonraker_remove_menu import MoonrakerRemoveMenu
from components.webui_client.client_utils import load_client_data
from components.webui_client.menus.client_remove_menu import ClientRemoveMenu
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import COLOR_RED, RESET_FORMAT
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class RemoveMenu(BaseMenu):
def __init__(self):
super().__init__(
header=True,
options={
"1": KlipperRemoveMenu,
"2": MoonrakerRemoveMenu,
"3": ClientRemoveMenu(client=load_client_data("mainsail")),
"4": ClientRemoveMenu(client=load_client_data("fluidd")),
"5": None,
"6": None,
"7": None,
"8": None,
"9": None,
"10": None,
"11": None,
"12": None,
"13": None,
},
footer_type=BACK_FOOTER,
)
def print_menu(self):
header = " [ Remove Menu ] "
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| INFO: Configurations and/or any backups will be kept! |
|-------------------------------------------------------|
| Firmware & API: | Webcam Streamer: |
| 1) [Klipper] | 6) [Crowsnest] |
| 2) [Moonraker] | 7) [MJPG-Streamer] |
| | |
| Klipper Webinterface: | Other: |
| 3) [Mainsail] | 8) [PrettyGCode] |
| 4) [Fluidd] | 9) [Telegram Bot] |
| | 10) [Obico for Klipper] |
| Touchscreen GUI: | 11) [OctoEverywhere] |
| 5) [KlipperScreen] | 12) [Mobileraker] |
| | 13) [NGINX] |
| | |
"""
)[1:]
print(menu, end="")

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 core.menus.base_menu import BaseMenu
# noinspection PyMethodMayBeStatic
class SettingsMenu(BaseMenu):
def __init__(self):
super().__init__(header=True, options={})
def print_menu(self):
print("self")
def execute_option_p(self):
# Implement the functionality for Option P
print("Executing Option P")
def execute_option_q(self):
# Implement the functionality for Option Q
print("Executing Option Q")
def execute_option_r(self):
# Implement the functionality for Option R
print("Executing Option R")

View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 textwrap
from components.klipper.klipper_setup import update_klipper
from components.klipper.klipper_utils import (
get_klipper_status,
)
from components.moonraker.moonraker_setup import update_moonraker
from components.moonraker.moonraker_utils import get_moonraker_status
from components.webui_client.client_config.client_config_setup import (
update_client_config,
)
from components.webui_client.client_setup import update_client
from components.webui_client.client_utils import (
get_local_client_version,
get_remote_client_version,
load_client_data,
get_client_config_status,
)
from core.menus import BACK_FOOTER
from core.menus.base_menu import BaseMenu
from utils.constants import (
COLOR_GREEN,
RESET_FORMAT,
COLOR_YELLOW,
COLOR_WHITE,
COLOR_RED,
)
# noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic
class UpdateMenu(BaseMenu):
def __init__(self):
super().__init__(
header=True,
options={
"0": self.update_all,
"1": self.update_klipper,
"2": self.update_moonraker,
"3": self.update_mainsail,
"4": self.update_fluidd,
"5": self.update_mainsail_config,
"6": self.update_fluidd_config,
"7": self.update_klipperscreen,
"8": self.update_mobileraker,
"9": self.update_crowsnest,
"10": self.upgrade_system_packages,
},
footer_type=BACK_FOOTER,
)
self.kl_local = f"{COLOR_WHITE}{RESET_FORMAT}"
self.kl_remote = f"{COLOR_WHITE}{RESET_FORMAT}"
self.mr_local = f"{COLOR_WHITE}{RESET_FORMAT}"
self.mr_remote = f"{COLOR_WHITE}{RESET_FORMAT}"
self.ms_local = f"{COLOR_WHITE}{RESET_FORMAT}"
self.ms_remote = f"{COLOR_WHITE}{RESET_FORMAT}"
self.fl_local = f"{COLOR_WHITE}{RESET_FORMAT}"
self.fl_remote = f"{COLOR_WHITE}{RESET_FORMAT}"
self.mc_local = f"{COLOR_WHITE}{RESET_FORMAT}"
self.mc_remote = f"{COLOR_WHITE}{RESET_FORMAT}"
self.fc_local = f"{COLOR_WHITE}{RESET_FORMAT}"
self.fc_remote = f"{COLOR_WHITE}{RESET_FORMAT}"
def print_menu(self):
self.fetch_update_status()
header = " [ Update Menu ] "
color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent(
f"""
/=======================================================\\
| {color}{header:~^{count}}{RESET_FORMAT} |
|-------------------------------------------------------|
| 0) Update all | | |
| | Current: | Latest: |
| Klipper & API: |---------------|---------------|
| 1) Klipper | {self.kl_local:<22} | {self.kl_remote:<22} |
| 2) Moonraker | {self.mr_local:<22} | {self.mr_remote:<22} |
| | | |
| Webinterface: |---------------|---------------|
| 3) Mainsail | {self.ms_local:<22} | {self.ms_remote:<22} |
| 4) Fluidd | {self.fl_local:<22} | {self.fl_remote:<22} |
| | | |
| Client-Config: |---------------|---------------|
| 5) Mainsail-Config | {self.mc_local:<22} | {self.mc_remote:<22} |
| 6) Fluidd-Config | {self.fc_local:<22} | {self.fc_remote:<22} |
| | | |
| Other: |---------------|---------------|
| 7) KlipperScreen | | |
| 8) Mobileraker | | |
| 9) Crowsnest | | |
| |-------------------------------|
| 10) System | |
"""
)[1:]
print(menu, end="")
def update_all(self, **kwargs):
print("update_all")
def update_klipper(self, **kwargs):
update_klipper()
def update_moonraker(self, **kwargs):
update_moonraker()
def update_mainsail(self, **kwargs):
update_client(load_client_data("mainsail"))
def update_mainsail_config(self, **kwargs):
update_client_config(load_client_data("mainsail"))
def update_fluidd(self, **kwargs):
update_client(load_client_data("fluidd"))
def update_fluidd_config(self, **kwargs):
update_client_config(load_client_data("fluidd"))
def update_klipperscreen(self, **kwargs): ...
def update_mobileraker(self, **kwargs): ...
def update_crowsnest(self, **kwargs): ...
def upgrade_system_packages(self, **kwargs): ...
def fetch_update_status(self):
# klipper
kl_status = get_klipper_status()
self.kl_local = kl_status.get("local")
self.kl_remote = kl_status.get("remote")
if self.kl_local == self.kl_remote:
self.kl_local = f"{COLOR_GREEN}{self.kl_local}{RESET_FORMAT}"
else:
self.kl_local = f"{COLOR_YELLOW}{self.kl_local}{RESET_FORMAT}"
self.kl_remote = f"{COLOR_GREEN}{self.kl_remote}{RESET_FORMAT}"
# moonraker
mr_status = get_moonraker_status()
self.mr_local = mr_status.get("local")
self.mr_remote = mr_status.get("remote")
if self.mr_local == self.mr_remote:
self.mr_local = f"{COLOR_GREEN}{self.mr_local}{RESET_FORMAT}"
else:
self.mr_local = f"{COLOR_YELLOW}{self.mr_local}{RESET_FORMAT}"
self.mr_remote = f"{COLOR_GREEN}{self.mr_remote}{RESET_FORMAT}"
# mainsail
mainsail_client_data = load_client_data("mainsail")
self.ms_local = get_local_client_version(mainsail_client_data)
self.ms_remote = get_remote_client_version(mainsail_client_data)
if self.ms_local == self.ms_remote:
self.ms_local = f"{COLOR_GREEN}{self.ms_local}{RESET_FORMAT}"
else:
self.ms_local = f"{COLOR_YELLOW}{self.ms_local}{RESET_FORMAT}"
self.ms_remote = f"{COLOR_GREEN if self.ms_remote != 'ERROR' else COLOR_RED}{self.ms_remote}{RESET_FORMAT}"
# fluidd
fluidd_client_data = load_client_data("fluidd")
self.fl_local = get_local_client_version(fluidd_client_data)
self.fl_remote = get_remote_client_version(fluidd_client_data)
if self.fl_local == self.fl_remote:
self.fl_local = f"{COLOR_GREEN}{self.fl_local}{RESET_FORMAT}"
else:
self.fl_local = f"{COLOR_YELLOW}{self.fl_local}{RESET_FORMAT}"
self.fl_remote = f"{COLOR_GREEN if self.fl_remote != 'ERROR' else COLOR_RED}{self.fl_remote}{RESET_FORMAT}"
# mainsail-config
mc_status = get_client_config_status(load_client_data("mainsail"))
self.mc_local = mc_status.get("local")
self.mc_remote = mc_status.get("remote")
if self.mc_local == self.mc_remote:
self.mc_local = f"{COLOR_GREEN}{self.mc_local}{RESET_FORMAT}"
else:
self.mc_local = f"{COLOR_YELLOW}{self.mc_local}{RESET_FORMAT}"
self.mc_remote = f"{COLOR_GREEN}{self.mc_remote}{RESET_FORMAT}"
# fluidd-config
fc_status = get_client_config_status(load_client_data("fluidd"))
self.fc_local = fc_status.get("local")
self.fc_remote = fc_status.get("remote")
if self.fc_local == self.mc_remote:
self.fc_local = f"{COLOR_GREEN}{self.fc_local}{RESET_FORMAT}"
else:
self.fc_local = f"{COLOR_YELLOW}{self.fc_local}{RESET_FORMAT}"
self.fc_remote = f"{COLOR_GREEN}{self.fc_remote}{RESET_FORMAT}"

View File

View File

@@ -0,0 +1,170 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
import subprocess
from pathlib import Path
from utils.input_utils import get_confirm
from utils.logger import Logger
# noinspection PyMethodMayBeStatic
class RepoManager:
def __init__(
self,
repo: str,
target_dir: str,
branch: str = None,
):
self._repo = repo
self._branch = branch if branch is not None else "master"
self._method = self._get_method()
self._target_dir = target_dir
@property
def repo(self) -> str:
return self._repo
@repo.setter
def repo(self, value) -> None:
self._repo = value
@property
def branch(self) -> str:
return self._branch
@branch.setter
def branch(self, value) -> None:
self._branch = value
@property
def method(self) -> str:
return self._method
@method.setter
def method(self, value) -> None:
self._method = value
@property
def target_dir(self) -> str:
return self._target_dir
@target_dir.setter
def target_dir(self, value) -> None:
self._target_dir = value
@staticmethod
def get_repo_name(repo: Path) -> str:
"""
Helper method to extract the organisation and name of a repository |
:param repo: repository to extract the values from
:return: String in form of "<orga>/<name>"
"""
if not repo.exists() and not repo.joinpath(".git").exists():
return "-"
try:
cmd = ["git", "-C", repo, "config", "--get", "remote.origin.url"]
result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
return "/".join(result.decode().strip().split("/")[-2:])
except subprocess.CalledProcessError:
return "-"
@staticmethod
def get_local_commit(repo: Path) -> str:
if not repo.exists() and not repo.joinpath(".git").exists():
return "-"
try:
cmd = f"cd {repo} && git describe HEAD --always --tags | cut -d '-' -f 1,2"
return subprocess.check_output(cmd, shell=True, text=True).strip()
except subprocess.CalledProcessError:
return "-"
@staticmethod
def get_remote_commit(repo: Path) -> str:
if not repo.exists() and not repo.joinpath(".git").exists():
return "-"
try:
# get locally checked out branch
branch_cmd = f"cd {repo} && git branch | grep -E '\*'"
branch = subprocess.check_output(branch_cmd, shell=True, text=True)
branch = branch.split("*")[-1].strip()
cmd = f"cd {repo} && git describe 'origin/{branch}' --always --tags | cut -d '-' -f 1,2"
return subprocess.check_output(cmd, shell=True, text=True).strip()
except subprocess.CalledProcessError:
return "-"
def clone_repo(self):
log = f"Cloning repository from '{self.repo}' with method '{self.method}'"
Logger.print_status(log)
try:
if Path(self.target_dir).exists():
question = f"'{self.target_dir}' already exists. Overwrite?"
if not get_confirm(question, default_choice=False):
Logger.print_info("Skipping re-clone of repository.")
return
shutil.rmtree(self.target_dir)
self._clone()
self._checkout()
except subprocess.CalledProcessError:
log = "An unexpected error occured during cloning of the repository."
Logger.print_error(log)
return
except OSError as e:
Logger.print_error(f"Error removing existing repository: {e.strerror}")
return
def pull_repo(self) -> None:
Logger.print_status(f"Updating repository '{self.repo}' ...")
try:
self._pull()
except subprocess.CalledProcessError:
log = "An unexpected error occured during updating the repository."
Logger.print_error(log)
return
def _clone(self):
try:
command = ["git", "clone", self.repo, self.target_dir]
subprocess.run(command, check=True)
Logger.print_ok("Clone successful!")
except subprocess.CalledProcessError as e:
log = f"Error cloning repository {self.repo}: {e.stderr.decode()}"
Logger.print_error(log)
raise
def _checkout(self):
try:
command = ["git", "checkout", f"{self.branch}"]
subprocess.run(command, cwd=self.target_dir, check=True)
Logger.print_ok("Checkout successful!")
except subprocess.CalledProcessError as e:
log = f"Error checking out branch {self.branch}: {e.stderr.decode()}"
Logger.print_error(log)
raise
def _pull(self) -> None:
try:
command = ["git", "pull"]
subprocess.run(command, cwd=self.target_dir, check=True)
except subprocess.CalledProcessError as e:
log = f"Error on git pull: {e.stderr.decode()}"
Logger.print_error(log)
raise
def _get_method(self) -> str:
return "ssh" if self.repo.startswith("git") else "https"

View File

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
EXT_MODULE_NAME = "gcode_shell_command.py"
MODULE_PATH = Path(__file__).resolve().parent
MODULE_ASSETS = MODULE_PATH.joinpath("assets")
KLIPPER_DIR = Path.home().joinpath("klipper")
KLIPPER_EXTRAS = KLIPPER_DIR.joinpath("klippy/extras")
EXTENSION_SRC = MODULE_ASSETS.joinpath(EXT_MODULE_NAME)
EXTENSION_TARGET_PATH = KLIPPER_EXTRAS.joinpath(EXT_MODULE_NAME)
EXAMPLE_CFG_SRC = MODULE_ASSETS.joinpath("shell_command.cfg")

View File

@@ -0,0 +1,94 @@
# Run a shell command via gcode
#
# Copyright (C) 2019 Eric Callahan <arksine.code@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
import os
import shlex
import subprocess
class ShellCommand:
def __init__(self, config):
self.name = config.get_name().split()[-1]
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object("gcode")
cmd = config.get("command")
cmd = os.path.expanduser(cmd)
self.command = shlex.split(cmd)
self.timeout = config.getfloat("timeout", 2.0, above=0.0)
self.verbose = config.getboolean("verbose", True)
self.proc_fd = None
self.partial_output = ""
self.gcode.register_mux_command(
"RUN_SHELL_COMMAND",
"CMD",
self.name,
self.cmd_RUN_SHELL_COMMAND,
desc=self.cmd_RUN_SHELL_COMMAND_help,
)
def _process_output(self, eventime):
if self.proc_fd is None:
return
try:
data = os.read(self.proc_fd, 4096)
except Exception:
pass
data = self.partial_output + data.decode()
if "\n" not in data:
self.partial_output = data
return
elif data[-1] != "\n":
split = data.rfind("\n") + 1
self.partial_output = data[split:]
data = data[:split]
else:
self.partial_output = ""
self.gcode.respond_info(data)
cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command"
def cmd_RUN_SHELL_COMMAND(self, params):
gcode_params = params.get("PARAMS", "")
gcode_params = shlex.split(gcode_params)
reactor = self.printer.get_reactor()
try:
proc = subprocess.Popen(
self.command + gcode_params,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
except Exception:
logging.exception("shell_command: Command {%s} failed" % (self.name))
raise self.gcode.error("Error running command {%s}" % (self.name))
if self.verbose:
self.proc_fd = proc.stdout.fileno()
self.gcode.respond_info("Running Command {%s}...:" % (self.name))
hdl = reactor.register_fd(self.proc_fd, self._process_output)
eventtime = reactor.monotonic()
endtime = eventtime + self.timeout
complete = False
while eventtime < endtime:
eventtime = reactor.pause(eventtime + 0.05)
if proc.poll() is not None:
complete = True
break
if not complete:
proc.terminate()
if self.verbose:
if self.partial_output:
self.gcode.respond_info(self.partial_output)
self.partial_output = ""
if complete:
msg = "Command {%s} finished\n" % (self.name)
else:
msg = "Command {%s} timed out" % (self.name)
self.gcode.respond_info(msg)
reactor.unregister_fd(hdl)
self.proc_fd = None
def load_config_prefix(config):
return ShellCommand(config)

View File

@@ -0,0 +1,7 @@
[gcode_shell_command hello_world]
command: echo hello world
timeout: 2.
verbose: True
[gcode_macro HELLO_WORLD]
gcode:
RUN_SHELL_COMMAND CMD=hello_world

View File

@@ -0,0 +1,127 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 os
import shutil
from typing import List
from components.klipper.klipper import Klipper
from core.backup_manager.backup_manager import BackupManager
from core.base_extension import BaseExtension
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.instance_manager import InstanceManager
from extensions.gcode_shell_cmd import (
EXTENSION_TARGET_PATH,
EXTENSION_SRC,
KLIPPER_DIR,
EXAMPLE_CFG_SRC,
KLIPPER_EXTRAS,
)
from utils.filesystem_utils import check_file_exist
from utils.input_utils import get_confirm
from utils.logger import Logger
# noinspection PyMethodMayBeStatic
class GcodeShellCmdExtension(BaseExtension):
def install_extension(self, **kwargs) -> None:
install_example = get_confirm("Create an example shell command?", False, False)
klipper_dir_exists = check_file_exist(KLIPPER_DIR)
if not klipper_dir_exists:
Logger.print_warn(
"No Klipper directory found! Unable to install extension."
)
return
extension_installed = check_file_exist(EXTENSION_TARGET_PATH)
overwrite = True
if extension_installed:
overwrite = get_confirm(
"Extension seems to be installed already. Overwrite?", True, False
)
if not overwrite:
Logger.print_warn("Installation aborted due to user request.")
return
im = InstanceManager(Klipper)
im.stop_all_instance()
try:
Logger.print_status(f"Copy extension to '{KLIPPER_EXTRAS}' ...")
shutil.copy(EXTENSION_SRC, EXTENSION_TARGET_PATH)
except OSError as e:
Logger.print_error(f"Unable to install extension: {e}")
return
if install_example:
self.install_example_cfg(im.instances)
im.start_all_instance()
Logger.print_ok("Installing G-Code Shell Command extension successful!")
def remove_extension(self, **kwargs) -> None:
extension_installed = check_file_exist(EXTENSION_TARGET_PATH)
if not extension_installed:
Logger.print_info("Extension does not seem to be installed! Skipping ...")
return
question = "Do you really want to remove the extension?"
if get_confirm(question, True, False):
try:
Logger.print_status(f"Removing '{EXTENSION_TARGET_PATH}' ...")
os.remove(EXTENSION_TARGET_PATH)
Logger.print_ok("Extension successfully removed!")
except OSError as e:
Logger.print_error(f"Unable to remove extension: {e}")
Logger.print_warn("PLEASE NOTE:")
Logger.print_warn(
"Remaining gcode shell command will cause Klipper to throw an error."
)
Logger.print_warn("Make sure to remove them from the printer.cfg!")
def install_example_cfg(self, instances: List[Klipper]):
cfg_dirs = [instance.cfg_dir for instance in instances]
# copy extension to klippy/extras
for cfg_dir in cfg_dirs:
Logger.print_status(f"Create shell_command.cfg in '{cfg_dir}' ...")
if check_file_exist(cfg_dir.joinpath("shell_command.cfg")):
Logger.print_info("File already exists! Skipping ...")
continue
try:
shutil.copy(EXAMPLE_CFG_SRC, cfg_dir)
Logger.print_ok("Done!")
except OSError as e:
Logger.warn(f"Unable to create example config: {e}")
# backup each printer.cfg before modification
bm = BackupManager()
for instance in instances:
bm.backup_file(
instance.cfg_file,
custom_filename=f"{instance.suffix}.printer.cfg",
)
# add section to printer.cfg if not already defined
section = "include shell_command.cfg"
cfg_files = [instance.cfg_file for instance in instances]
for cfg_file in cfg_files:
Logger.print_status(f"Include shell_command.cfg in '{cfg_file}' ...")
cm = ConfigManager(cfg_file)
if cm.config.has_section(section):
Logger.print_info("Section already defined! Skipping ...")
continue
cm.config.add_section(section)
cm.write_config()
Logger.print_ok("Done!")

View File

@@ -0,0 +1,9 @@
{
"metadata": {
"index": 1,
"module": "gcode_shell_cmd_extension",
"maintained_by": "dw-0",
"display_name": "G-Code Shell Command",
"description": "Allows to run a shell command from gcode."
}
}

20
kiauh/main.py Normal file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 core.menus.main_menu import MainMenu
from utils.logger import Logger
def main():
try:
MainMenu().start()
except KeyboardInterrupt:
Logger.print_ok("\nHappy printing!\n", prefix=False)

23
kiauh/utils/__init__.py Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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
MODULE_PATH = Path(__file__).resolve().parent
INVALID_CHOICE = "Invalid choice. Please select a valid value."
PRINTER_CFG_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("printer-cfg-backups")
# ================== NGINX =====================#
NGINX_SITES_AVAILABLE = Path("/etc/nginx/sites-available")
NGINX_SITES_ENABLED = Path("/etc/nginx/sites-enabled")
NGINX_CONFD = Path("/etc/nginx/conf.d")

View File

@@ -0,0 +1,6 @@
# /etc/nginx/conf.d/common_vars.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

View File

@@ -0,0 +1,95 @@
server {
listen %PORT%;
# uncomment the next line to activate IPv6
# listen [::]:%PORT%;
access_log /var/log/nginx/%NAME%-access.log;
error_log /var/log/nginx/%NAME%-error.log;
# disable this section on smaller hardware like a pi zero
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_proxied expired no-cache no-store private auth;
gzip_comp_level 4;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;
# web_path from %NAME% static files
root %ROOT_DIR%;
index index.html;
server_name _;
# disable max upload size checks
client_max_body_size 0;
# disable proxy request buffering
proxy_request_buffering off;
location / {
try_files $uri $uri/ /index.html;
}
location = /index.html {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
location /websocket {
proxy_pass http://apiserver/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}
location ~ ^/(printer|api|access|machine|server)/ {
proxy_pass http://apiserver$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
}
location /webcam/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer1/;
}
location /webcam2/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer2/;
}
location /webcam3/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer3/;
}
location /webcam4/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer4/;
}
}

View File

@@ -0,0 +1,25 @@
# /etc/nginx/conf.d/upstreams.conf
upstream apiserver {
ip_hash;
server 127.0.0.1:7125;
}
upstream mjpgstreamer1 {
ip_hash;
server 127.0.0.1:8080;
}
upstream mjpgstreamer2 {
ip_hash;
server 127.0.0.1:8081;
}
upstream mjpgstreamer3 {
ip_hash;
server 127.0.0.1:8082;
}
upstream mjpgstreamer4 {
ip_hash;
server 127.0.0.1:8083;
}

137
kiauh/utils/common.py Normal file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 datetime import datetime
from pathlib import Path
from typing import Dict, Literal, List, Type, Union
from components.klipper.klipper import Klipper
from core.instance_manager.base_instance import BaseInstance
from core.instance_manager.instance_manager import InstanceManager
from utils import PRINTER_CFG_BACKUP_DIR
from utils.constants import (
COLOR_CYAN,
RESET_FORMAT,
COLOR_YELLOW,
COLOR_GREEN,
COLOR_RED,
)
from utils.filesystem_utils import check_file_exist
from utils.logger import Logger
from utils.system_utils import check_package_install, install_system_packages
def get_current_date() -> Dict[Literal["date", "time"], str]:
"""
Get the current date |
:return: Dict holding a date and time key:value pair
"""
now: datetime = datetime.today()
date: str = now.strftime("%Y%m%d")
time: str = now.strftime("%H%M%S")
return {"date": date, "time": time}
def check_install_dependencies(deps: List[str]) -> None:
"""
Common helper method to check if dependencies are installed
and if not, install them automatically |
:param deps: List of strings of package names to check if installed
:return: None
"""
requirements = check_package_install(deps)
if requirements:
Logger.print_status("Installing dependencies ...")
Logger.print_info("The following packages need installation:")
for _ in requirements:
print(f"{COLOR_CYAN}{_}{RESET_FORMAT}")
install_system_packages(requirements)
def get_install_status_common(
instance_type: Type[BaseInstance], repo_dir: Path, env_dir: Path
) -> Dict[Literal["status", "status_code", "instances"], Union[str, int]]:
"""
Helper method to get the installation status of software components,
which only consist of 3 major parts and if those parts exist, the
component can be considered as "installed". Typically, Klipper or
Moonraker match that criteria.
:param instance_type: The component type
:param repo_dir: the repository directory
:param env_dir: the python environment directory
:return: Dictionary with status string, statuscode and instance count
"""
im = InstanceManager(instance_type)
instances_exist = len(im.instances) > 0
status = [repo_dir.exists(), env_dir.exists(), instances_exist]
if all(status):
return {
"status": "Installed:",
"status_code": 1,
"instances": len(im.instances),
}
elif not any(status):
return {
"status": "Not installed!",
"status_code": 2,
"instances": len(im.instances),
}
else:
return {
"status": "Incomplete!",
"status_code": 3,
"instances": len(im.instances),
}
def get_install_status_webui(
install_dir: Path, nginx_cfg: Path, upstreams_cfg: Path, common_cfg: Path
) -> str:
"""
Helper method to get the installation status of webuis
like Mainsail or Fluidd |
:param install_dir: folder of the static webui files
:param nginx_cfg: the webuis NGINX config
:param upstreams_cfg: the required upstreams.conf
:param common_cfg: the required common_vars.conf
:return: formatted string, containing the status
"""
dir_exist = install_dir.exists()
nginx_cfg_exist = check_file_exist(nginx_cfg)
upstreams_cfg_exist = check_file_exist(upstreams_cfg)
common_cfg_exist = check_file_exist(common_cfg)
status = [dir_exist, nginx_cfg_exist]
general_nginx_status = [upstreams_cfg_exist, common_cfg_exist]
if all(status) and all(general_nginx_status):
return f"{COLOR_GREEN}Installed!{RESET_FORMAT}"
elif not all(status):
return f"{COLOR_RED}Not installed!{RESET_FORMAT}"
else:
return f"{COLOR_YELLOW}Incomplete!{RESET_FORMAT}"
def backup_printer_config_dir():
# local import to prevent circular import
from core.backup_manager.backup_manager import BackupManager
im = InstanceManager(Klipper)
instances: List[Klipper] = im.instances
bm = BackupManager()
for instance in instances:
name = f"config-{instance.data_dir_name}"
bm.backup_directory(
name,
source=instance.cfg_dir,
target=PRINTER_CFG_BACKUP_DIR,
)

26
kiauh/utils/constants.py Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 os
import pwd
from pathlib import Path
# text colors and formats
COLOR_WHITE = "\033[37m" # white
COLOR_MAGENTA = "\033[35m" # magenta
COLOR_GREEN = "\033[92m" # bright green
COLOR_YELLOW = "\033[93m" # bright yellow
COLOR_RED = "\033[91m" # bright red
COLOR_CYAN = "\033[96m" # bright cyan
RESET_FORMAT = "\033[0m" # reset format
# current user
CURRENT_USER = pwd.getpwuid(os.getuid())[0]
SYSTEMD = Path("/etc/systemd/system")

View File

@@ -0,0 +1,281 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 re
import shutil
import subprocess
from pathlib import Path
from zipfile import ZipFile
from typing import List, Type, TypeVar, Union, Tuple
from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker
from core.config_manager.config_manager import ConfigManager
from core.instance_manager.base_instance import BaseInstance
from core.instance_manager.instance_manager import InstanceManager
from utils import (
NGINX_SITES_AVAILABLE,
MODULE_PATH,
NGINX_CONFD,
NGINX_SITES_ENABLED,
)
from utils.logger import Logger
B = TypeVar('B', bound='BaseInstance')
ConfigOption = Tuple[str, str]
def check_file_exist(file_path: Path, sudo=False) -> bool:
"""
Helper function for checking the existence of a file |
:param file_path: the absolute path of the file to check
:param sudo: use sudo if required
:return: True, if file exists, otherwise False
"""
if sudo:
try:
command = ["sudo", "find", file_path]
subprocess.check_output(command, stderr=subprocess.DEVNULL)
return True
except subprocess.CalledProcessError:
return False
else:
if file_path.exists():
return True
else:
return False
def create_symlink(source: Path, target: Path, sudo=False) -> None:
try:
cmd = ["ln", "-sf", source, target]
if sudo:
cmd.insert(0, "sudo")
subprocess.run(cmd, stderr=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as e:
Logger.print_error(f"Failed to create symlink: {e}")
raise
def remove_file(file_path: Path, sudo=False) -> None:
try:
cmd = f"{'sudo ' if sudo else ''}rm -f {file_path}"
subprocess.run(cmd, stderr=subprocess.PIPE, check=True, shell=True)
except subprocess.CalledProcessError as e:
log = f"Cannot remove file {file_path}: {e.stderr.decode()}"
Logger.print_error(log)
raise
def unzip(filepath: Path, target_dir: Path) -> None:
"""
Helper function to unzip a zip-archive into a target directory |
:param filepath: the path to the zip-file to unzip
:param target_dir: the target directory to extract the files into
:return: None
"""
with ZipFile(filepath, "r") as _zip:
_zip.extractall(target_dir)
def copy_upstream_nginx_cfg() -> None:
"""
Creates an upstream.conf in /etc/nginx/conf.d
:return: None
"""
source = MODULE_PATH.joinpath("assets/upstreams.conf")
target = NGINX_CONFD.joinpath("upstreams.conf")
try:
command = ["sudo", "cp", source, target]
subprocess.run(command, stderr=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as e:
log = f"Unable to create upstreams.conf: {e.stderr.decode()}"
Logger.print_error(log)
raise
def copy_common_vars_nginx_cfg() -> None:
"""
Creates a common_vars.conf in /etc/nginx/conf.d
:return: None
"""
source = MODULE_PATH.joinpath("assets/common_vars.conf")
target = NGINX_CONFD.joinpath("common_vars.conf")
try:
command = ["sudo", "cp", source, target]
subprocess.run(command, stderr=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as e:
log = f"Unable to create upstreams.conf: {e.stderr.decode()}"
Logger.print_error(log)
raise
def create_nginx_cfg(name: str, port: int, root_dir: Path) -> None:
"""
Creates an NGINX config from a template file and replaces all placeholders
:param name: name of the config to create
:param port: listen port
:param root_dir: directory of the static files
:return: None
"""
tmp = Path.home().joinpath(f"{name}.tmp")
shutil.copy(MODULE_PATH.joinpath("assets/nginx_cfg"), tmp)
with open(tmp, "r+") as f:
content = f.read()
content = content.replace("%NAME%", name)
content = content.replace("%PORT%", str(port))
content = content.replace("%ROOT_DIR%", str(root_dir))
f.seek(0)
f.write(content)
f.truncate()
target = NGINX_SITES_AVAILABLE.joinpath(name)
try:
command = ["sudo", "mv", tmp, target]
subprocess.run(command, stderr=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as e:
log = f"Unable to create '{target}': {e.stderr.decode()}"
Logger.print_error(log)
raise
def read_ports_from_nginx_configs() -> List[str]:
"""
Helper function to iterate over all NGINX configs and read all ports defined for listen
:return: A sorted list of listen ports
"""
if not NGINX_SITES_ENABLED.exists():
return []
port_list = []
for config in NGINX_SITES_ENABLED.iterdir():
with open(config, "r") as cfg:
lines = cfg.readlines()
for line in lines:
line = line.replace("default_server", "")
line = re.sub(r"[;:\[\]]", "", line.strip())
if line.startswith("listen") and line.split()[-1] not in port_list:
port_list.append(line.split()[-1])
return sorted(port_list, key=lambda x: int(x))
def is_valid_port(port: str, ports_in_use: List[str]) -> bool:
return port.isdigit() and port not in ports_in_use
def get_next_free_port(ports_in_use: List[str]) -> str:
valid_ports = set(range(80, 7125))
used_ports = set(map(int, ports_in_use))
return str(min(valid_ports - used_ports))
def add_config_section(section: str, instances: List[B], options: List[ConfigOption] = None) -> None:
for instance in instances:
cfg_file = instance.cfg_file
Logger.print_status(f"Add section '[{section}]' to '{cfg_file}' ...")
if not Path(cfg_file).exists():
Logger.print_warn(f"'{cfg_file}' not found!")
continue
cm = ConfigManager(cfg_file)
if cm.config.has_section(section):
Logger.print_info("Section already exist. Skipped ...")
continue
cm.config.add_section(section)
if options is not None:
for option in options:
cm.config.set(section, option[0], option[1])
cm.write_config()
def remove_config_section(section: str, instances: List[B]) -> None:
for instance in instances:
cfg_file = instance.cfg_file
Logger.print_status(f"Remove section '[{section}]' from '{cfg_file}' ...")
if not Path(cfg_file).exists():
Logger.print_warn(f"'{cfg_file}' not found!")
continue
cm = ConfigManager(cfg_file)
if not cm.config.has_section(section):
Logger.print_info("Section does not exist. Skipped ...")
continue
cm.config.remove_section(section)
cm.write_config()
def patch_moonraker_conf(
moonraker_instances: List[Moonraker],
name: str,
section_name: str,
template_file: str,
) -> None:
for instance in moonraker_instances:
cfg_file = instance.cfg_file
Logger.print_status(f"Add {name} update section to '{cfg_file}' ...")
if not Path(cfg_file).exists():
Logger.print_warn(f"'{cfg_file}' not found!")
return
cm = ConfigManager(cfg_file)
if cm.config.has_section(section_name):
Logger.print_info("Section already exist. Skipped ...")
return
template = MODULE_PATH.joinpath("assets", template_file)
with open(template, "r") as t:
template_content = "\n"
template_content += t.read()
with open(cfg_file, "a") as f:
f.write(template_content)
def remove_nginx_config(name: str) -> None:
Logger.print_status(f"Removing NGINX config for {name.capitalize()} ...")
try:
remove_file(NGINX_SITES_AVAILABLE.joinpath(name), True)
remove_file(NGINX_SITES_ENABLED.joinpath(name), True)
except subprocess.CalledProcessError as e:
log = f"Unable to remove NGINX config '{name}':\n{e.stderr.decode()}"
Logger.print_error(log)
def remove_nginx_logs(name: str) -> None:
Logger.print_status(f"Removing NGINX logs for {name.capitalize()} ...")
try:
remove_file(Path(f"/var/log/nginx/{name}-access.log"), True)
remove_file(Path(f"/var/log/nginx/{name}-error.log"), True)
im = InstanceManager(Klipper)
instances: List[Klipper] = im.instances
if not instances:
return
for instance in instances:
remove_file(instance.log_dir.joinpath(f"{name}-access.log"))
remove_file(instance.log_dir.joinpath(f"{name}-error.log"))
except (OSError, subprocess.CalledProcessError) as e:
Logger.print_error(f"Unable to remove NGINX logs:\n{e}")

148
kiauh/utils/input_utils.py Normal file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 typing import Optional, List, Union
from utils import INVALID_CHOICE
from utils.constants import COLOR_CYAN, RESET_FORMAT
from utils.logger import Logger
def get_confirm(
question: str, default_choice=True, allow_go_back=False
) -> Union[bool, None]:
"""
Helper method for validating confirmation (yes/no) user input. |
:param question: The question to display
:param default_choice: A default if input was submitted without input
:param allow_go_back: Navigate back to a previous dialog
:return: Either True or False, or None on go_back
"""
options_confirm = ["y", "yes"]
options_decline = ["n", "no"]
options_go_back = ["b", "B"]
if default_choice:
def_choice = "(Y/n)"
options_confirm.append("")
else:
def_choice = "(y/N)"
options_decline.append("")
while True:
choice = (
input(format_question(question + f" {def_choice}", None)).strip().lower()
)
if choice in options_confirm:
return True
elif choice in options_decline:
return False
elif allow_go_back and choice in options_go_back:
return None
else:
Logger.print_error(INVALID_CHOICE)
def get_number_input(
question: str, min_count: int, max_count=None, default=None, allow_go_back=False
) -> Union[int, None]:
"""
Helper method to get a number input from the user
:param question: The question to display
:param min_count: The lowest allowed value
:param max_count: The highest allowed value (or None)
:param default: Optional default value
:param allow_go_back: Navigate back to a previous dialog
:return: Either the validated number input, or None on go_back
"""
options_go_back = ["b", "B"]
_question = format_question(question, default)
while True:
_input = input(_question)
if allow_go_back and _input in options_go_back:
return None
if _input == "":
return default
try:
return validate_number_input(_input, min_count, max_count)
except ValueError:
Logger.print_error(INVALID_CHOICE)
def get_string_input(question: str, exclude=Optional[List], default=None) -> str:
"""
Helper method to get a string input from the user
:param question: The question to display
:param exclude: List of strings which are not allowed
:param default: Optional default value
:return: The validated string value
"""
while True:
_input = input(format_question(question, default)).strip()
if _input.isalnum() and _input.lower() not in exclude:
return _input
Logger.print_error(INVALID_CHOICE)
if _input in exclude:
Logger.print_error("This value is already in use/reserved.")
def get_selection_input(question: str, option_list: List, default=None) -> str:
"""
Helper method to get a selection from a list of options from the user
:param question: The question to display
:param option_list: The list of options the user can select from
:param default: Optional default value
:return: The option that was selected by the user
"""
while True:
_input = input(format_question(question, default)).strip()
if _input in option_list:
return _input
Logger.print_error(INVALID_CHOICE)
def format_question(question: str, default=None) -> str:
"""
Helper method to have a standardized formatting of questions |
:param question: The question to display
:param default: If defined, the default option will be displayed to the user
:return: The formatted question string
"""
formatted_q = question
if default is not None:
formatted_q += f" (default={default})"
return f"{COLOR_CYAN}###### {formatted_q}: {RESET_FORMAT}"
def validate_number_input(value: str, min_count: int, max_count: int) -> int:
"""
Helper method for a simple number input validation. |
:param value: The value to validate
:param min_count: The lowest allowed value
:param max_count: The highest allowed value (or None)
:return: The validated value as Integer
:raises: ValueError if value is invalid
"""
if max_count is not None:
if min_count <= int(value) <= max_count:
return int(value)
elif int(value) >= min_count:
return int(value)
raise ValueError

61
kiauh/utils/logger.py Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 utils.constants import (
COLOR_WHITE,
COLOR_GREEN,
COLOR_YELLOW,
COLOR_RED,
COLOR_MAGENTA,
RESET_FORMAT,
)
class Logger:
@staticmethod
def info(msg):
# log to kiauh.log
pass
@staticmethod
def warn(msg):
# log to kiauh.log
pass
@staticmethod
def error(msg):
# log to kiauh.log
pass
@staticmethod
def print_info(msg, prefix=True, start="", end="\n") -> None:
message = f"[INFO] {msg}" if prefix else msg
print(f"{COLOR_WHITE}{start}{message}{RESET_FORMAT}", end=end)
@staticmethod
def print_ok(msg, prefix=True, start="", end="\n") -> None:
message = f"[OK] {msg}" if prefix else msg
print(f"{COLOR_GREEN}{start}{message}{RESET_FORMAT}", end=end)
@staticmethod
def print_warn(msg, prefix=True, start="", end="\n") -> None:
message = f"[WARN] {msg}" if prefix else msg
print(f"{COLOR_YELLOW}{start}{message}{RESET_FORMAT}", end=end)
@staticmethod
def print_error(msg, prefix=True, start="", end="\n") -> None:
message = f"[ERROR] {msg}" if prefix else msg
print(f"{COLOR_RED}{start}{message}{RESET_FORMAT}", end=end)
@staticmethod
def print_status(msg, prefix=True, start="", end="\n") -> None:
message = f"\n###### {msg}" if prefix else msg
print(f"{COLOR_MAGENTA}{start}{message}{RESET_FORMAT}", end=end)

341
kiauh/utils/system_utils.py Normal file
View File

@@ -0,0 +1,341 @@
#!/usr/bin/env python3
# ======================================================================= #
# 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 os
import shutil
import socket
import subprocess
import sys
import time
import urllib.error
import urllib.request
import venv
from pathlib import Path
from typing import List, Literal
from utils.input_utils import get_confirm
from utils.logger import Logger
from utils.filesystem_utils import check_file_exist
def kill(opt_err_msg: str = "") -> None:
"""
Kills the application |
:param opt_err_msg: an optional, additional error message
:return: None
"""
if opt_err_msg:
Logger.print_error(opt_err_msg)
Logger.print_error("A critical error has occured. KIAUH was terminated.")
sys.exit(1)
def parse_packages_from_file(source_file: Path) -> List[str]:
"""
Read the package names from bash scripts, when defined like:
PKGLIST="package1 package2 package3" |
:param source_file: path of the sourcefile to read from
:return: A list of package names
"""
packages = []
print("Reading dependencies...")
with open(source_file, "r") as file:
for line in file:
line = line.strip()
if line.startswith("PKGLIST="):
line = line.replace('"', "")
line = line.replace("PKGLIST=", "")
line = line.replace("${PKGLIST}", "")
packages.extend(line.split())
return packages
def create_python_venv(target: Path) -> None:
"""
Create a python 3 virtualenv at the provided target destination |
:param target: Path where to create the virtualenv at
:return: None
"""
Logger.print_status("Set up Python virtual environment ...")
if not target.exists():
try:
venv.create(target, with_pip=True)
Logger.print_ok("Setup of virtualenv successful!")
except OSError as e:
Logger.print_error(f"Error setting up virtualenv:\n{e}")
raise
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error setting up virtualenv:\n{e.output.decode()}")
raise
else:
if get_confirm("Virtualenv already exists. Re-create?", default_choice=False):
try:
shutil.rmtree(target)
create_python_venv(target)
except OSError as e:
log = f"Error removing existing virtualenv: {e.strerror}"
Logger.print_error(log, False)
raise
else:
Logger.print_info("Skipping re-creation of virtualenv ...")
def update_python_pip(target: Path) -> None:
"""
Updates pip in the provided target destination |
:param target: Path of the virtualenv
:return: None
"""
Logger.print_status("Updating pip ...")
try:
pip_location = target.joinpath("bin/pip")
pip_exists = check_file_exist(pip_location)
if not pip_exists:
raise FileNotFoundError("Error updating pip! Not found.")
command = [pip_location, "install", "-U", "pip"]
result = subprocess.run(command, stderr=subprocess.PIPE, text=True)
if result.returncode != 0 or result.stderr:
Logger.print_error(f"{result.stderr}", False)
Logger.print_error("Updating pip failed!")
return
Logger.print_ok("Updating pip successful!")
except FileNotFoundError as e:
Logger.print_error(e)
raise
except subprocess.CalledProcessError as e:
Logger.print_error(f"Error updating pip:\n{e.output.decode()}")
raise
def install_python_requirements(target: Path, requirements: Path) -> None:
"""
Installs the python packages based on a provided requirements.txt |
:param target: Path of the virtualenv
:param requirements: Path to the requirements.txt file
:return: None
"""
try:
# always update pip before installing requirements
update_python_pip(target)
Logger.print_status("Installing Python requirements ...")
command = [target.joinpath("bin/pip"), "install", "-r", f"{requirements}"]
result = subprocess.run(command, stderr=subprocess.PIPE, text=True)
if result.returncode != 0 or result.stderr:
Logger.print_error(f"{result.stderr}", False)
Logger.print_error("Installing Python requirements failed!")
return
Logger.print_ok("Installing Python requirements successful!")
except subprocess.CalledProcessError as e:
log = f"Error installing Python requirements:\n{e.output.decode()}"
Logger.print_error(log)
raise
def update_system_package_lists(silent: bool, rls_info_change=False) -> None:
"""
Updates the systems package list |
:param silent: Log info to the console or not
:param rls_info_change: Flag for "--allow-releaseinfo-change"
:return: None
"""
cache_mtime = 0
cache_files = [
Path("/var/lib/apt/periodic/update-success-stamp"),
Path("/var/lib/apt/lists"),
]
for cache_file in cache_files:
if cache_file.exists():
cache_mtime = max(cache_mtime, os.path.getmtime(cache_file))
update_age = int(time.time() - cache_mtime)
update_interval = 6 * 3600 # 48hrs
if update_age <= update_interval:
return
if not silent:
Logger.print_status("Updating package list...")
try:
command = ["sudo", "apt-get", "update"]
if rls_info_change:
command.append("--allow-releaseinfo-change")
result = subprocess.run(command, stderr=subprocess.PIPE, text=True)
if result.returncode != 0 or result.stderr:
Logger.print_error(f"{result.stderr}", False)
Logger.print_error("Updating system package list failed!")
return
Logger.print_ok("System package list update successful!")
except subprocess.CalledProcessError as e:
kill(f"Error updating system package list:\n{e.stderr.decode()}")
def check_package_install(packages: List[str]) -> List[str]:
"""
Checks the system for installed packages |
:param packages: List of strings of package names
:return: A list containing the names of packages that are not installed
"""
not_installed = []
for package in packages:
command = ["dpkg-query", "-f'${Status}'", "--show", package]
result = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True
)
if "installed" not in result.stdout.strip("'").split():
not_installed.append(package)
else:
Logger.print_ok(f"{package} already installed.")
return not_installed
def install_system_packages(packages: List[str]) -> None:
"""
Installs a list of system packages |
:param packages: List of system package names
:return: None
"""
try:
command = ["sudo", "apt-get", "install", "-y"]
for pkg in packages:
command.append(pkg)
subprocess.run(command, stderr=subprocess.PIPE, check=True)
Logger.print_ok("Packages installed successfully.")
except subprocess.CalledProcessError as e:
kill(f"Error installing packages:\n{e.stderr.decode()}")
def mask_system_service(service_name: str) -> None:
"""
Mask a system service to prevent it from starting |
:param service_name: name of the service to mask
:return: None
"""
try:
command = ["sudo", "systemctl", "mask", service_name]
subprocess.run(command, stderr=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as e:
log = f"Unable to mask system service {service_name}: {e.stderr.decode()}"
Logger.print_error(log)
raise
# this feels hacky and not quite right, but for now it works
# see: https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib
def get_ipv4_addr() -> str:
"""
Helper function that returns the IPv4 of the current machine
by opening a socket and sending a package to an arbitrary IP. |
:return: Local IPv4 of the current machine
"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
try:
# doesn't even have to be reachable
s.connect(("192.255.255.255", 1))
return s.getsockname()[0]
except Exception:
return "127.0.0.1"
finally:
s.close()
def download_file(url: str, target: Path, show_progress=True) -> None:
"""
Helper method for downloading files from a provided URL |
:param url: the url to the file
:param target: the target path incl filename
:param show_progress: show download progress or not
:return: None
"""
try:
if show_progress:
urllib.request.urlretrieve(url, target, download_progress)
sys.stdout.write("\n")
else:
urllib.request.urlretrieve(url, target)
except urllib.error.HTTPError as e:
Logger.print_error(f"Download failed! HTTP error occured: {e}")
raise
except urllib.error.URLError as e:
Logger.print_error(f"Download failed! URL error occured: {e}")
raise
except Exception as e:
Logger.print_error(f"Download failed! An error occured: {e}")
raise
def download_progress(block_num, block_size, total_size) -> None:
"""
Reporthook method for urllib.request.urlretrieve() method call in download_file() |
:param block_num:
:param block_size:
:param total_size: total filesize in bytes
:return: None
"""
downloaded = block_num * block_size
percent = 100 if downloaded >= total_size else downloaded / total_size * 100
mb = 1024 * 1024
progress = int(percent / 5)
remaining = "-" * (20 - progress)
dl = f"\rDownloading: [{'#' * progress}{remaining}]{percent:.2f}% ({downloaded / mb:.2f}/{total_size / mb:.2f}MB)"
sys.stdout.write(dl)
sys.stdout.flush()
def set_nginx_permissions() -> None:
"""
Check if permissions of the users home directory
grant execution rights to group and other and set them if not set.
Required permissions for NGINX to be able to serve Mainsail/Fluidd.
This seems to have become necessary with Ubuntu 21+. |
:return: None
"""
cmd = f"ls -ld {Path.home()} | cut -d' ' -f1"
homedir_perm = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, text=True)
homedir_perm = homedir_perm.stdout
if homedir_perm.count("x") < 3:
Logger.print_status("Granting NGINX the required permissions ...")
subprocess.run(["chmod", "og+x", Path.home()])
Logger.print_ok("Permissions granted.")
def control_systemd_service(
name: str, action: Literal["start", "stop", "restart", "disable"]
) -> None:
"""
Helper method to execute several actions for a specific systemd service. |
:param name: the service name
:param action: Either "start", "stop", "restart" or "disable"
:return: None
"""
try:
Logger.print_status(f"{action.capitalize()} {name}.service ...")
command = ["sudo", "systemctl", action, f"{name}.service"]
subprocess.run(command, stderr=subprocess.PIPE, check=True)
Logger.print_ok("OK!")
except subprocess.CalledProcessError as e:
log = f"Failed to {action} {name}.service: {e.stderr.decode()}"
Logger.print_error(log)
raise

13
pyproject.toml Normal file
View File

@@ -0,0 +1,13 @@
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
(
\.git/
| \.github/
| docs/
| resources/
| scripts/
)
'''

65
resources/autocommit.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
#####################################################################
### Please set the paths accordingly. In case you don't have all ###
### the listed folders, just keep that line commented out. ###
#####################################################################
### Path to your config folder you want to back up
#config_folder=~/klipper_config
### Path to your Klipper folder, by default that is '~/klipper'
#klipper_folder=~/klipper
### Path to your Moonraker folder, by default that is '~/moonraker'
#moonraker_folder=~/moonraker
### Path to your Mainsail folder, by default that is '~/mainsail'
#mainsail_folder=~/mainsail
### Path to your Fluidd folder, by default that is '~/fluidd'
#fluidd_folder=~/fluidd
#####################################################################
#####################################################################
#####################################################################
################ !!! DO NOT EDIT BELOW THIS LINE !!! ################
#####################################################################
grab_version() {
local klipper_commit moonraker_commit
local mainsail_ver fluidd_ver
if [[ -n ${klipper_folder} ]]; then
cd "${klipper_folder}"
klipper_commit=$(git rev-parse --short=7 HEAD)
m1="Klipper on commit: ${klipper_commit}"
fi
if [[ -n ${moonraker_folder} ]]; then
cd "${moonraker_folder}"
moonraker_commit=$(git rev-parse --short=7 HEAD)
m2="Moonraker on commit: ${moonraker_commit}"
fi
if [[ -n ${mainsail_folder} ]]; then
mainsail_ver=$(head -n 1 "${mainsail_folder}/.version")
m3="Mainsail version: ${mainsail_ver}"
fi
if [[ -n ${fluidd_folder} ]]; then
fluidd_ver=$(head -n 1 "${fluidd_folder}/.version")
m4="Fluidd version: ${fluidd_ver}"
fi
}
push_config() {
local current_date
cd "${config_folder}" || exit 1
git pull
git add .
current_date=$(date +"%Y-%m-%d %T")
git commit -m "Autocommit from ${current_date}" -m "${m1}" -m "${m2}" -m "${m3}" -m "${m4}"
git push
}
grab_version
push_config

View File

@@ -0,0 +1,6 @@
# /etc/nginx/conf.d/common_vars.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

View File

@@ -0,0 +1,11 @@
[mcu]
serial: /dev/serial/by-id/<your-mcu-id>
[virtual_sdcard]
path: %GCODES_DIR%
on_error_gcode: CANCEL_PRINT
[printer]
kinematics: none
max_velocity: 1000
max_accel: 1000

96
resources/fluidd Normal file
View File

@@ -0,0 +1,96 @@
# /etc/nginx/sites-available/fluidd
server {
listen 80;
access_log /var/log/nginx/fluidd-access.log;
error_log /var/log/nginx/fluidd-error.log;
# disable this section on smaller hardware like a pi zero
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_proxied expired no-cache no-store private auth;
gzip_comp_level 4;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;
# web_path from fluidd static files
root /home/pi/fluidd;
index index.html;
server_name _;
# disable max upload size checks
client_max_body_size 0;
# disable proxy request buffering
proxy_request_buffering off;
location / {
try_files $uri $uri/ /index.html;
}
location = /index.html {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
location /websocket {
proxy_pass http://apiserver/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}
location ~ ^/(printer|api|access|machine|server)/ {
proxy_pass http://apiserver$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_read_timeout 600;
}
location /webcam/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer1/;
}
location /webcam2/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer2/;
}
location /webcam3/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer3/;
}
location /webcam4/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer4/;
}
}

View File

@@ -0,0 +1,87 @@
# Run a shell command via gcode
#
# Copyright (C) 2019 Eric Callahan <arksine.code@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import os
import shlex
import subprocess
import logging
class ShellCommand:
def __init__(self, config):
self.name = config.get_name().split()[-1]
self.printer = config.get_printer()
self.gcode = self.printer.lookup_object('gcode')
cmd = config.get('command')
cmd = os.path.expanduser(cmd)
self.command = shlex.split(cmd)
self.timeout = config.getfloat('timeout', 2., above=0.)
self.verbose = config.getboolean('verbose', True)
self.proc_fd = None
self.partial_output = ""
self.gcode.register_mux_command(
"RUN_SHELL_COMMAND", "CMD", self.name,
self.cmd_RUN_SHELL_COMMAND,
desc=self.cmd_RUN_SHELL_COMMAND_help)
def _process_output(self, eventime):
if self.proc_fd is None:
return
try:
data = os.read(self.proc_fd, 4096)
except Exception:
pass
data = self.partial_output + data.decode()
if '\n' not in data:
self.partial_output = data
return
elif data[-1] != '\n':
split = data.rfind('\n') + 1
self.partial_output = data[split:]
data = data[:split]
else:
self.partial_output = ""
self.gcode.respond_info(data)
cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command"
def cmd_RUN_SHELL_COMMAND(self, params):
gcode_params = params.get('PARAMS','')
gcode_params = shlex.split(gcode_params)
reactor = self.printer.get_reactor()
try:
proc = subprocess.Popen(
self.command + gcode_params, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except Exception:
logging.exception(
"shell_command: Command {%s} failed" % (self.name))
raise self.gcode.error("Error running command {%s}" % (self.name))
if self.verbose:
self.proc_fd = proc.stdout.fileno()
self.gcode.respond_info("Running Command {%s}...:" % (self.name))
hdl = reactor.register_fd(self.proc_fd, self._process_output)
eventtime = reactor.monotonic()
endtime = eventtime + self.timeout
complete = False
while eventtime < endtime:
eventtime = reactor.pause(eventtime + .05)
if proc.poll() is not None:
complete = True
break
if not complete:
proc.terminate()
if self.verbose:
if self.partial_output:
self.gcode.respond_info(self.partial_output)
self.partial_output = ""
if complete:
msg = "Command {%s} finished\n" % (self.name)
else:
msg = "Command {%s} timed out" % (self.name)
self.gcode.respond_info(msg)
reactor.unregister_fd(hdl)
self.proc_fd = None
def load_config_prefix(config):
return ShellCommand(config)

1
resources/klipper.env Normal file
View File

@@ -0,0 +1 @@
KLIPPER_ARGS="%KLIPPER_DIR%/klippy/klippy.py %CFG% -I %PRINTER% -l %LOG% -a %UDS%"

18
resources/klipper.service Normal file
View File

@@ -0,0 +1,18 @@
[Unit]
Description=Klipper 3D Printer Firmware SV1
Documentation=https://www.klipper3d.org/
After=network-online.target
Wants=udev.target
[Install]
WantedBy=multi-user.target
[Service]
Type=simple
User=%USER%
RemainAfterExit=yes
WorkingDirectory=%KLIPPER_DIR%
EnvironmentFile=%ENV_FILE%
ExecStart=%ENV%/bin/python $KLIPPER_ARGS
Restart=always
RestartSec=10

96
resources/mainsail Normal file
View File

@@ -0,0 +1,96 @@
# /etc/nginx/sites-available/mainsail
server {
listen 80;
access_log /var/log/nginx/mainsail-access.log;
error_log /var/log/nginx/mainsail-error.log;
# disable this section on smaller hardware like a pi zero
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_proxied expired no-cache no-store private auth;
gzip_comp_level 4;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;
# web_path from mainsail static files
root /home/pi/mainsail;
index index.html;
server_name _;
# disable max upload size checks
client_max_body_size 0;
# disable proxy request buffering
proxy_request_buffering off;
location / {
try_files $uri $uri/ /index.html;
}
location = /index.html {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
location /websocket {
proxy_pass http://apiserver/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}
location ~ ^/(printer|api|access|machine|server)/ {
proxy_pass http://apiserver$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_read_timeout 600;
}
location /webcam/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer1/;
}
location /webcam2/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer2/;
}
location /webcam3/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer3/;
}
location /webcam4/ {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
access_log off;
error_log off;
proxy_pass http://mjpgstreamer4/;
}
}

View File

@@ -0,0 +1,79 @@
### Windows users: To edit this file use Notepad++, VSCode, Atom or SublimeText.
### Do not use Notepad or WordPad.
### MacOSX users: If you use Textedit to edit this file make sure to use
### "plain text format" and "disable smart quotes" in "Textedit > Preferences"
### Configure which camera to use
#
# Available options are:
# - auto: tries first usb webcam, if that's not available tries raspi cam
# - usb: only tries usb webcam
# - raspi: only tries raspi cam
#
# Defaults to auto
#
#camera="auto"
### Additional options to supply to MJPG Streamer for the USB camera
#
# See https://faq.octoprint.org/mjpg-streamer-config for available options
#
# Defaults to a resolution of 640x480 px and a framerate of 10 fps
#
#camera_usb_options="-r 640x480 -f 10"
### Additional webcam devices known to cause problems with -f
#
# Apparently there a some devices out there that with the current
# mjpg_streamer release do not support the -f parameter (for specifying
# the capturing framerate) and will just refuse to output an image if it
# is supplied.
#
# The webcam daemon will detect those devices by their USB Vendor and Product
# ID and remove the -f parameter from the options provided to mjpg_streamer.
#
# By default, this is done for the following devices:
# Logitech C170 (046d:082b)
# GEMBIRD (1908:2310)
# Genius F100 (0458:708c)
# Cubeternet GL-UPC822 UVC WebCam (1e4e:0102)
#
# Using the following option it is possible to add additional devices. If
# your webcam happens to show above symptoms, try determining your cam's
# vendor and product id via lsusb, activating the line below by removing # and
# adding it, e.g. for two broken cameras "aabb:ccdd" and "aabb:eeff"
#
# additional_brokenfps_usb_devices=("aabb:ccdd" "aabb:eeff")
#
#
#additional_brokenfps_usb_devices=()
### Additional options to supply to MJPG Streamer for the RasPi Cam
#
# See https://faq.octoprint.org/mjpg-streamer-config for available options
#
# Defaults to 10fps
#
#camera_raspi_options="-fps 10"
### Configuration of camera HTTP output
#
# Usually you should NOT need to change this at all! Only touch if you
# know what you are doing and what the parameters mean.
#
# Below settings are used in the mjpg-streamer call like this:
#
# -o "output_http.so -w $camera_http_webroot $camera_http_options"
#
# Current working directory is the mjpg-streamer base directory.
#
#camera_http_webroot="./www-mainsail"
#camera_http_options="-n"
### EXPERIMENTAL
# Support for different streamer types.
#
# Available options:
# mjpeg [default] - stable MJPG-streamer
#camera_streamer=mjpeg

View File

@@ -0,0 +1,303 @@
#!/bin/bash
########################################################################
### DO NOT EDIT THIS FILE TO CHANGE THE CONFIG!!! ###
### ---------------------------------------------------------------- ###
### There is no need to edit this file for changing resolution, ###
### frame rates or any other mjpg-streamer parameters. Please edit ###
### /home/pi/klipper_config/webcam.txt instead - that's what it's ###
### there for! You can even do this with your Pi powered down by ###
### directly accessing the file when using the SD card as thumb ###
### drive in your regular computer. ###
########################################################################
MJPGSTREAMER_HOME=/home/pi/mjpg-streamer
MJPGSTREAMER_INPUT_USB="input_uvc.so"
MJPGSTREAMER_INPUT_RASPICAM="input_raspicam.so"
brokenfps_usb_devices=("046d:082b" "1908:2310" "0458:708c" "1e4e:0102" "0471:0311" "038f:6001" "046d:0804" "046d:0825" "046d:0994" "0ac8:3450")
config_dir="/home/pi/klipper_config"
echo "Starting up webcamDaemon..."
echo ""
cfg_files=()
#cfg_files+=/boot/mainsail.txt
if [[ -d ${config_dir} ]]; then
cfg_files+=( `ls ${config_dir}/webcam*.txt` )
fi
array_camera_config=()
array_camera=()
array_camera_usb_options=()
array_camera_usb_device=()
array_camera_raspi_options=()
array_camera_http_webroot=()
array_camera_http_options=()
array_additional_brokenfps_usb_devices=()
array_camera_device=()
array_assigned_device=()
echo "--- Configuration: ----------------------------"
for cfg_file in ${cfg_files[@]}; do
# init configuration - DO NOT EDIT, USE /home/pi/klipper_config/webcam*.txt INSTEAD!
camera="auto"
camera_usb_options="-r 640x480 -f 10"
camera_raspi_options="-fps 10"
camera_http_webroot="./www-mjpgstreamer"
camera_http_options="-n"
additional_brokenfps_usb_devices=()
if [[ -e ${cfg_file} ]]; then
source "$cfg_file"
fi
usb_options="$camera_usb_options"
# if webcam device is explicitly given in /home/pi/klipper_config/webcam*.txt, save the path of the device
# to a variable and remove its parameter from usb_options
extracted_device=`echo $usb_options | sed 's@.*-d \(/dev/\(video[0-9]\+\|v4l/[^ ]*\)\).*@\1@'`
if [ "$extracted_device" != "$usb_options" ]
then
# the camera options refer to a device, save it in a variable
# replace video device parameter with empty string and strip extra whitespace
usb_options=`echo $usb_options | sed 's/\-d \/dev\/\(video[0-9]\+\|v4l\/[^ ]*\)//g' | awk '$1=$1'`
else
extracted_device=""
fi
# echo configuration
echo "cfg_file: $cfg_file"
echo "camera: $camera"
echo "usb options: $camera_usb_options"
echo "raspi options: $camera_raspi_options"
echo "http options: -w $camera_http_webroot $camera_http_options"
echo ""
echo "Explicitly USB device: $extracted_device"
echo "-----------------------------------------------"
echo ""
array_camera_config+=( $cfg_file )
array_camera+=( $camera )
array_camera_usb_options+=("$usb_options")
array_camera_usb_device+=("$extracted_device")
array_camera_raspi_options+=("$camera_raspi_options")
array_camera_http_webroot+=("$camera_http_webroot")
array_camera_http_options+=("$camera_http_options")
array_camera_brokenfps_usb_devices+=("${brokenfps_usb_devices[*]} ${additional_brokenfps_usb_devices[*]}")
array_camera_device+=("")
done
# check if array contains a string
function containsString() {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
# cleans up when the script receives a SIGINT or SIGTERM
function cleanup() {
# make sure that all child processed die when we die
local pids=$(jobs -pr)
[ -n "$pids" ] && kill $pids
exit 0
}
# says goodbye when the script shuts down
function goodbye() {
# say goodbye
echo ""
echo "Goodbye..."
echo ""
}
# runs MJPG Streamer, using the provided input plugin + configuration
function runMjpgStreamer {
input=$1
# There are problems with 0x000137ab firmware on VL805 (Raspberry Pi 4}).
# Try to autodetect offending firmware and temporarily fix the issue
# by changing power management mode
echo "Checking for VL805 (Raspberry Pi 4)..."
if [[ -f /usr/bin/vl805 ]]; then
VL805_VERSION=$(/usr/bin/vl805)
VL805_VERSION=${VL805_VERSION#*: }
echo " - version 0x${VL805_VERSION} detected"
case "$VL805_VERSION" in
00013701)
echo " - nothing to be done. It shouldn't cause USB problems."
;;
000137ab)
echo -e " - \e[31mThis version is known to cause problems with USB cameras.\e[39m"
echo -e " You may want to downgrade to 0x0013701."
echo -e " - [FIXING] Trying the setpci -s 01:00.0 0xD4.B=0x41 hack to mitigate the"
echo -e " issue. It disables ASPM L1 on the VL805. Your board may (or may not) get"
echo -e " slightly hotter. For details see:"
echo -e " https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=244421"
setpci -s 01:00.0 0xD4.B=0x41
;;
*)
echo " - unknown firmware version. Doing nothing."
;;
esac
else
echo " - It seems that you don't have VL805 (Raspberry Pi 4)."
echo " There should be no problems with USB (a.k.a. select() timeout)"
fi
pushd $MJPGSTREAMER_HOME > /dev/null 2>&1
echo Running ./mjpg_streamer -o "output_http.so -w $camera_http_webroot $camera_http_options" -i "$input"
LD_LIBRARY_PATH=. ./mjpg_streamer -o "output_http.so -w $camera_http_webroot $camera_http_options" -i "$input" &
sleep 1 &
sleep_pid=$!
wait ${sleep_pid}
popd > /dev/null 2>&1
}
# starts up the RasPiCam
function startRaspi {
logger -s "Starting Raspberry Pi camera"
runMjpgStreamer "$MJPGSTREAMER_INPUT_RASPICAM $camera_raspi_options"
}
# starts up the USB webcam
function startUsb {
options="$usb_options"
device="video0"
# check for parameter and set the device if it is given as a parameter
input=$1
if [[ -n $input ]]; then
device=`basename "$input"`
fi
# add video device into options
options="$options -d /dev/$device"
uevent_file="/sys/class/video4linux/$device/device/uevent"
if [ -e $uevent_file ]; then
# let's see what kind of webcam we have here, fetch vid and pid...
product=`cat $uevent_file | grep PRODUCT | cut -d"=" -f2`
vid=`echo $product | cut -d"/" -f1`
pid=`echo $product | cut -d"/" -f2`
vidpid=`printf "%04x:%04x" "0x$vid" "0x$pid"`
# ... then look if it is in our list of known broken-fps-devices and if so remove
# the -f parameter from the options (if it's in there, else that's just a no-op)
for identifier in ${brokenfps_usb_devices[@]};
do
if [ "$vidpid" = "$identifier" ]; then
echo
echo "Camera model $vidpid is known to not work with -f parameter, stripping it out"
echo
options=`echo $options | sed -e "s/\(\s\+\|^\)-f\s\+[0-9]\+//g"`
fi
done
fi
logger -s "Starting USB webcam"
runMjpgStreamer "$MJPGSTREAMER_INPUT_USB $options"
}
# make sure our cleanup function gets called when we receive SIGINT, SIGTERM
trap "cleanup" SIGINT SIGTERM
# say goodbye when we EXIT
trap "goodbye" EXIT
# we need this to prevent the later calls to vcgencmd from blocking
# I have no idea why, but that's how it is...
vcgencmd version > /dev/null 2>&1
# keep mjpg streamer running if some camera is attached
while true; do
# get list of usb video devices into an array
video_devices=($(find /dev -regextype sed -regex '\/dev/video[0-9]\+' | sort -nk1.11 2> /dev/null))
# add list of raspi camera into an array
if [ "`vcgencmd get_camera`" = "supported=1 detected=1" ]; then
video_devices+=( "raspi" )
fi
echo "Found video devices:"
printf '%s\n' "${video_devices[@]}"
for scan_mode in "usb" "usb-auto" "raspi" "auto"; do
camera=$scan_mode
if [[ "usb-auto" == "$scan_mode" ]]; then
camera="usb"
fi
for ((i=0;i<${#array_camera[@]};i++)); do
if [[ -z ${array_camera_device[${i}]} ]] && [[ $camera == ${array_camera[${i}]} ]]; then
camera_config="${array_camera_config[${i}]}"
usb_options="${array_camera_usb_options[${i}]}"
camera_usb_device="${array_camera_usb_device[${i}]}"
camera_raspi_options="${array_camera_raspi_options[${i}]}"
camera_http_webroot="${array_camera_http_webroot[${i}]}"
camera_http_options="${array_camera_http_options[${i}]}"
brokenfps_usb_devices="${array_camera_brokenfps_usb_devices[${i}]}"
if [[ ${camera_usb_device} ]] && { [[ "usb" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; }; then
# usb device is explicitly set in options
usb_device_path=`readlink -f ${camera_usb_device}`
if containsString "$usb_device_path" "${array_camera_device[@]}"; then
if [[ "auto" != ${scan_mode} ]]; then
array_camera_device[${i}]="alredy_in_use"
echo "config file='$camera_config':Video device already in use."
continue
fi
elif containsString "$usb_device_path" "${video_devices[@]}"; then
array_camera_device[${i}]="$usb_device_path"
# explicitly set usb device was found in video_devices array, start usb with the found device
echo "config file='$camera_config':USB device was set in options and found in devices, start MJPG-streamer with the configured USB video device: $usb_device_path"
startUsb "$usb_device_path"
continue
fi
elif [[ -z ${camera_usb_device} ]] && { [[ "usb-auto" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; }; then
for video_device in "${video_devices[@]}"; do
if [[ "raspi" != "$video_device" ]]; then
if containsString "$video_device" "${array_camera_device[@]}"; then
: #already in use
else
array_camera_device[${i}]="$video_device"
# device is not set explicitly in options, start usb with first found usb camera as the device
echo "config file='$camera_config':USB device was not set in options, start MJPG-streamer with the first found video device: ${video_device}"
startUsb "${video_device}"
break
fi
fi
done
if [[ -n ${array_camera_device[${i}]} ]]; then
continue
fi
fi
if [[ "raspi" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; then
video_device="raspi"
if containsString "$video_device" "${array_camera_device[@]}"; then
if [[ "auto" != ${scan_mode} ]]; then
array_camera_device[${i}]="alredy_in_use"
echo "config file='$camera_config':RasPiCam device already in use."
fi
elif containsString "$video_device" "${video_devices[@]}"; then
array_camera_device[${i}]="$video_device"
echo "config file='$camera_config':Start MJPG-streamer with video device: ${video_device}"
startRaspi
sleep 30 &
sleep_pid=$!
wait ${sleep_pid}
fi
fi
fi
done
done
array_assigned_device=( ${array_camera_device[*]} )
if [[ ${#array_camera[@]} -eq ${#array_assigned_device[@]} ]]; then
echo "Done bring up all configured video device"
exit 0
else
echo "Scan again in two minutes"
sleep 120 &
sleep_pid=$!
wait ${sleep_pid}
fi
done

Some files were not shown because too many files have changed in this diff Show More