The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .editorconfig
├── .github
    ├── FUNDING.yml
    ├── ISSUE_TEMPLATE
    │   ├── bug_report.yml
    │   ├── config.yml
    │   └── feature_request.yml
    └── workflows
    │   └── release-ff-and-tag.yml
├── .gitignore
├── .shellcheckrc
├── LICENSE
├── README.md
├── default.kiauh.cfg
├── docs
    ├── changelog.md
    └── gcode_shell_command.md
├── kiauh.py
├── kiauh.sh
├── kiauh
    ├── __init__.py
    ├── components
    │   ├── __init__.py
    │   ├── crowsnest
    │   │   ├── __init__.py
    │   │   └── crowsnest.py
    │   ├── klipper
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │   │   ├── klipper.env
    │   │   │   ├── klipper.service
    │   │   │   └── printer.cfg
    │   │   ├── klipper.py
    │   │   ├── klipper_dialogs.py
    │   │   ├── klipper_utils.py
    │   │   ├── menus
    │   │   │   ├── __init__.py
    │   │   │   └── klipper_remove_menu.py
    │   │   └── services
    │   │   │   ├── __init__.py
    │   │   │   ├── klipper_instance_service.py
    │   │   │   └── klipper_setup_service.py
    │   ├── klipper_firmware
    │   │   ├── __init__.py
    │   │   ├── firmware_utils.py
    │   │   ├── flash_options.py
    │   │   └── menus
    │   │   │   ├── klipper_build_menu.py
    │   │   │   ├── klipper_flash_error_menu.py
    │   │   │   ├── klipper_flash_help_menu.py
    │   │   │   └── klipper_flash_menu.py
    │   ├── klipperscreen
    │   │   ├── __init__.py
    │   │   └── klipperscreen.py
    │   ├── log_uploads
    │   │   ├── __init__.py
    │   │   ├── log_upload_utils.py
    │   │   └── menus
    │   │   │   └── log_upload_menu.py
    │   ├── moonraker
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │   │   ├── moonraker.conf
    │   │   │   ├── moonraker.env
    │   │   │   └── moonraker.service
    │   │   ├── menus
    │   │   │   ├── __init__.py
    │   │   │   └── moonraker_remove_menu.py
    │   │   ├── moonraker.py
    │   │   ├── moonraker_dialogs.py
    │   │   ├── services
    │   │   │   ├── __init__.py
    │   │   │   ├── moonraker_instance_service.py
    │   │   │   └── moonraker_setup_service.py
    │   │   └── utils
    │   │   │   ├── __init__.py
    │   │   │   ├── sysdeps_parser.py
    │   │   │   └── utils.py
    │   └── webui_client
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │       ├── common_vars.conf
    │   │       ├── nginx_cfg
    │   │       └── upstreams.conf
    │   │   ├── base_data.py
    │   │   ├── client_config
    │   │       ├── __init__.py
    │   │       ├── client_config_remove.py
    │   │       └── client_config_setup.py
    │   │   ├── client_dialogs.py
    │   │   ├── client_remove.py
    │   │   ├── client_setup.py
    │   │   ├── client_utils.py
    │   │   ├── fluidd_data.py
    │   │   ├── mainsail_data.py
    │   │   └── menus
    │   │       ├── __init__.py
    │   │       ├── client_install_menu.py
    │   │       └── client_remove_menu.py
    ├── core
    │   ├── __init__.py
    │   ├── backup_manager
    │   │   ├── __init__.py
    │   │   └── backup_manager.py
    │   ├── constants.py
    │   ├── decorators.py
    │   ├── instance_manager
    │   │   ├── __init__.py
    │   │   ├── base_instance.py
    │   │   └── instance_manager.py
    │   ├── logger.py
    │   ├── menus
    │   │   ├── __init__.py
    │   │   ├── advanced_menu.py
    │   │   ├── backup_menu.py
    │   │   ├── base_menu.py
    │   │   ├── install_menu.py
    │   │   ├── main_menu.py
    │   │   ├── remove_menu.py
    │   │   ├── repo_select_menu.py
    │   │   ├── settings_menu.py
    │   │   └── update_menu.py
    │   ├── services
    │   │   ├── __init__.py
    │   │   └── message_service.py
    │   ├── settings
    │   │   ├── __init__.py
    │   │   └── kiauh_settings.py
    │   ├── spinner.py
    │   ├── submodules
    │   │   ├── __init__.py
    │   │   └── simple_config_parser
    │   │   │   ├── .editorconfig
    │   │   │   ├── .gitignore
    │   │   │   ├── LICENSE
    │   │   │   ├── README.md
    │   │   │   ├── pyproject.toml
    │   │   │   ├── requirements-dev.txt
    │   │   │   ├── src
    │   │   │       └── simple_config_parser
    │   │   │       │   ├── __init__.py
    │   │   │       │   ├── constants.py
    │   │   │       │   └── simple_config_parser.py
    │   │   │   └── tests
    │   │   │       ├── __init__.py
    │   │   │       ├── assets
    │   │   │           ├── klipper_config.txt
    │   │   │           ├── test_config_1.cfg
    │   │   │           ├── test_config_2.cfg
    │   │   │           ├── test_config_3.cfg
    │   │   │           └── write_tests
    │   │   │           │   ├── add_option
    │   │   │           │       ├── expected.cfg
    │   │   │           │       └── input.cfg
    │   │   │           │   ├── remove_option
    │   │   │           │       ├── expected.cfg
    │   │   │           │       └── input.cfg
    │   │   │           │   └── remove_section
    │   │   │           │       ├── expected.cfg
    │   │   │           │       └── input.cfg
    │   │   │       ├── line_matching
    │   │   │           ├── __init__.py
    │   │   │           ├── match_empty_line
    │   │   │           │   ├── __init__.py
    │   │   │           │   ├── test_data
    │   │   │           │   │   ├── matching_data.txt
    │   │   │           │   │   └── non_matching_data.txt
    │   │   │           │   └── test_match_empty_line.py
    │   │   │           ├── match_line_comment
    │   │   │           │   ├── __init__.py
    │   │   │           │   ├── test_data
    │   │   │           │   │   ├── matching_data.txt
    │   │   │           │   │   └── non_matching_data.txt
    │   │   │           │   └── test_match_line_comment.py
    │   │   │           ├── match_option
    │   │   │           │   ├── __init__.py
    │   │   │           │   ├── test_data
    │   │   │           │   │   ├── matching_data.txt
    │   │   │           │   │   └── non_matching_data.txt
    │   │   │           │   └── test_match_option.py
    │   │   │           ├── match_option_block_start
    │   │   │           │   ├── __init__.py
    │   │   │           │   ├── test_data
    │   │   │           │   │   ├── matching_data.txt
    │   │   │           │   │   └── non_matching_data.txt
    │   │   │           │   └── test_match_options_block_start.py
    │   │   │           └── match_section
    │   │   │           │   ├── __init__,py.py
    │   │   │           │   ├── test_data
    │   │   │           │       ├── matching_data.txt
    │   │   │           │       └── non_matching_data.txt
    │   │   │           │   └── test_match_section.py
    │   │   │       ├── line_parsing
    │   │   │           ├── __init__.py
    │   │   │           └── test_line_parsing.py
    │   │   │       ├── public_api
    │   │   │           ├── __init__.py
    │   │   │           ├── conftest.py
    │   │   │           ├── test_options_api.py
    │   │   │           ├── test_read_file.py
    │   │   │           ├── test_sections_api.py
    │   │   │           └── test_write_file.py
    │   │   │       ├── utils.py
    │   │   │       └── value_conversion
    │   │   │           ├── __init__.py
    │   │   │           └── test_get_conv.py
    │   └── types
    │   │   ├── __init__.py
    │   │   ├── color.py
    │   │   └── component_status.py
    ├── extensions
    │   ├── __init__.py
    │   ├── base_extension.py
    │   ├── extensions_menu.py
    │   ├── gcode_shell_cmd
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │   │   ├── gcode_shell_command.py
    │   │   │   └── shell_command.cfg
    │   │   ├── gcode_shell_cmd_extension.py
    │   │   └── metadata.json
    │   ├── klipper_backup
    │   │   ├── __init__.py
    │   │   ├── klipper_backup_extension.py
    │   │   └── metadata.json
    │   ├── mainsail_theme_installer
    │   │   ├── __init__.py
    │   │   ├── mainsail_theme_installer_extension.py
    │   │   └── metadata.json
    │   ├── mobileraker
    │   │   ├── __init__.py
    │   │   ├── metadata.json
    │   │   └── mobileraker_extension.py
    │   ├── obico
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │   │   ├── moonraker-obico.env
    │   │   │   └── moonraker-obico.service
    │   │   ├── metadata.json
    │   │   ├── moonraker_obico.py
    │   │   └── moonraker_obico_extension.py
    │   ├── octoapp
    │   │   ├── __init__.py
    │   │   ├── metadata.json
    │   │   ├── octoapp.py
    │   │   └── octoapp_extension.py
    │   ├── octoeverywhere
    │   │   ├── __init__.py
    │   │   ├── metadata.json
    │   │   ├── octoeverywhere.py
    │   │   └── octoeverywhere_extension.py
    │   ├── pretty_gcode
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │   │   └── pgcode.local.conf
    │   │   ├── metadata.json
    │   │   └── pretty_gcode_extension.py
    │   ├── simply_print
    │   │   ├── __init__.py
    │   │   ├── metadata.json
    │   │   └── simply_print_extension.py
    │   ├── spoolman
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │   │   └── docker-compose.yml
    │   │   ├── metadata.json
    │   │   ├── spoolman.py
    │   │   └── spoolman_extension.py
    │   └── telegram_bot
    │   │   ├── __init__.py
    │   │   ├── assets
    │   │       ├── moonraker-telegram-bot.env
    │   │       └── moonraker-telegram-bot.service
    │   │   ├── metadata.json
    │   │   ├── moonraker_telegram_bot.py
    │   │   └── moonraker_telegram_bot_extension.py
    ├── main.py
    ├── procedures
    │   ├── __init__.py
    │   ├── switch_repo.py
    │   └── system.py
    └── utils
    │   ├── __init__.py
    │   ├── common.py
    │   ├── config_utils.py
    │   ├── fs_utils.py
    │   ├── git_utils.py
    │   ├── input_utils.py
    │   ├── instance_type.py
    │   ├── instance_utils.py
    │   └── sys_utils.py
├── pyproject.toml
├── pyrightconfig.json
├── requirements-dev.txt
├── resources
    ├── autocommit.sh
    ├── common_vars.conf
    ├── example.printer.cfg
    ├── fluidd
    ├── gcode_shell_command.py
    ├── klipper.env
    ├── klipper.service
    ├── mainsail
    ├── mjpg-streamer
    │   ├── webcam.txt
    │   ├── webcamd
    │   └── webcamd.service
    ├── moonraker-telegram-bot.env
    ├── moonraker-telegram-bot.service
    ├── moonraker.conf
    ├── moonraker.env
    ├── moonraker.service
    ├── screenshots
    │   ├── kiauh.png
    │   ├── rpi_imager1.png
    │   └── rpi_imager2.png
    ├── shell_command.cfg
    └── upstreams.conf
└── scripts
    ├── backup.sh
    ├── crowsnest.sh
    ├── flash_klipper.sh
    ├── fluidd.sh
    ├── gcode_shell_command.sh
    ├── globals.sh
    ├── klipper.sh
    ├── klipperscreen.sh
    ├── mainsail.sh
    ├── mjpg-streamer.sh
    ├── mobileraker.sh
    ├── moonraker-telegram-bot.sh
    ├── moonraker.sh
    ├── nginx.sh
    ├── obico.sh
    ├── octoapp.sh
    ├── octoeverywhere.sh
    ├── octoprint.sh
    ├── pretty_gcode.sh
    ├── rollback.sh
    ├── spoolman.sh
    ├── switch_klipper_repo.sh
    ├── ui
        ├── advanced_menu.sh
        ├── backup_menu.sh
        ├── general_ui.sh
        ├── install_menu.sh
        ├── main_menu.sh
        ├── remove_menu.sh
        ├── settings_menu.sh
        └── update_menu.sh
    ├── upload_log.sh
    └── utilities.sh


/.editorconfig:
--------------------------------------------------------------------------------
 1 | root = true
 2 | 
 3 | [*]
 4 | indent_style = space
 5 | indent_size = 4
 6 | insert_final_newline = true
 7 | trim_trailing_whitespace = true
 8 | charset = utf-8
 9 | end_of_line = lf
10 | 
11 | [*.py]
12 | max_line_length = 88
13 | 
14 | [*.{sh,yml,yaml,json}]
15 | indent_size = 2


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


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
 1 | name: Bug report
 2 | description: Create a report to help us improve
 3 | labels: ["bug"]
 4 | body:
 5 |   - type: markdown
 6 |     attributes:
 7 |       value: |
 8 |         This issue form is for reporting bugs only!
 9 |         If you have a feature request, please use [feature_request](/new?template=feature_request.yml)
10 |   - type: textarea
11 |     id: distro
12 |     attributes:
13 |       label: Linux Distribution
14 |       description: >-
15 |         The linux distribution the issue occured on
16 |     validations:
17 |       required: true
18 |   - type: textarea
19 |     id: what-happened
20 |     attributes:
21 |       label: What happened
22 |       description: >-
23 |         A clear and concise description of what the bug is.
24 |     validations:
25 |       required: true
26 |   - type: textarea
27 |     id: expected-behavior
28 |     attributes:
29 |       label: What did you expect to happen
30 |       description: >-
31 |         A clear and concise description of what you expected to happen.
32 |     validations:
33 |       required: true
34 |   - type: textarea
35 |     id: repro-steps
36 |     attributes:
37 |       label: How to reproduce
38 |       description: >-
39 |         Minimal and precise steps to reproduce this bug.
40 |     validations:
41 |       required: true
42 |   - type: textarea
43 |     id: additional-info
44 |     attributes:
45 |       label: Additional information
46 |       description: |
47 |         If you have any additional information for us, use the field below.
48 | 
49 |         Please note, you can attach screenshots or screen recordings here, by
50 |         dragging and dropping files in the field below.
51 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 |   - name: Klipper Discord
4 |     url: https://discord.klipper3d.org/
5 |     about: Quickest way to get in contact
6 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
 1 | name: Feature request
 2 | description: Suggest an idea for this project
 3 | labels: ["feature request"]
 4 | body:
 5 |   - type: markdown
 6 |     attributes:
 7 |       value: |
 8 |         This issue form is for feature requests only!
 9 |         If you've found a bug, please use [bug_report](/new?template=bug_report.yml)
10 |   - type: textarea
11 |     id: problem-description
12 |     attributes:
13 |       label: Is your feature request related to a problem? Please describe
14 |       description: >-
15 |         A clear and concise description of what the problem is.
16 |     validations:
17 |       required: true
18 |   - type: textarea
19 |     id: solution-description
20 |     attributes:
21 |       label: Describe the solution you'd like
22 |       description: >-
23 |         A clear and concise description of what you want to happen.
24 |     validations:
25 |       required: true
26 |   - type: textarea
27 |     id: possible-alternatives
28 |     attributes:
29 |       label: Describe alternatives you've considered
30 |       description: >-
31 |         A clear and concise description of any alternative solutions or features you've considered.
32 |   - type: textarea
33 |     id: additional-info
34 |     attributes:
35 |       label: Additional information
36 |       description: |
37 |         If you have any additional information for us, use the field below.
38 | 
39 |         Please note, you can attach screenshots or screen recordings here, by
40 |         dragging and dropping files in the field below.
41 | 


--------------------------------------------------------------------------------
/.github/workflows/release-ff-and-tag.yml:
--------------------------------------------------------------------------------
 1 | name: Release - Fast-Forward and Tag
 2 | on:
 3 |   workflow_dispatch:
 4 |     inputs:
 5 |       tag_name:
 6 |         description: 'Provide a tag name (e.g. v1.0.0)'
 7 |         required: true
 8 |         type: string
 9 | 
10 | jobs:
11 |   ff-and-tag:
12 |     runs-on: ubuntu-latest
13 |     permissions:
14 |       contents: write
15 |     steps:
16 |       - name: Checkout
17 |         uses: actions/checkout@v4
18 |         with:
19 |           fetch-depth: 0
20 |           ref: 'master'
21 |       - name: Merge Fast Forward
22 |         uses: MaximeHeckel/github-action-merge-fast-forward@v1.1.0
23 |         with:
24 |           branchtomerge: origin/develop
25 |           branch: master
26 |         env:
27 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 |       - name: Create and Push Tag
29 |         run: |
30 |           git tag ${{ inputs.tag_name }}
31 |           git push origin ${{ inputs.tag_name }}
32 |         env:
33 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | .idea
 2 | .vscode
 3 | .pytest_cache
 4 | .jupyter
 5 | *.ipynb
 6 | *.ipynb_checkpoints
 7 | *.tmp
 8 | __pycache__
 9 | .kiauh-env
10 | *.code-workspace
11 | *.iml
12 | kiauh.cfg
13 | 


--------------------------------------------------------------------------------
/.shellcheckrc:
--------------------------------------------------------------------------------
 1 | source=scripts
 2 | 
 3 | enable=avoid-nullary-conditions
 4 | enable=deprecate-which
 5 | enable=quote-safe-variables
 6 | enable=require-variable-braces
 7 | enable=require-double-brackets
 8 | 
 9 | # SC2162: `read` without `-r` will mangle backslashes.
10 | # https://github.com/koalaman/shellcheck/wiki/SC2162
11 | disable=SC2162
12 | 
13 | # SC2164: Use `cd ... || exit` in case `cd` fails
14 | # https://github.com/koalaman/shellcheck/wiki/SC2164
15 | disable=SC2164
16 | 


--------------------------------------------------------------------------------
/default.kiauh.cfg:
--------------------------------------------------------------------------------
 1 | [kiauh]
 2 | backup_before_update: False
 3 | 
 4 | [klipper]
 5 | # add custom repositories here, if at least one is given, the first in the list will be used by default
 6 | # otherwise the official repository is used
 7 | #
 8 | # format: https://github.com/username/repository, branch
 9 | # example: https://github.com/Klipper3d/klipper, master
10 | #
11 | # branch is optional, if given, it must be preceded by a comma, if not given, 'master' is used
12 | repositories:
13 |     https://github.com/Klipper3d/klipper
14 | 
15 | [moonraker]
16 | # Moonraker supports two optional Python packages that can be used to reduce its CPU load
17 | # If set to true, those packages will be installed during the Moonraker installation
18 | optional_speedups: True
19 | 
20 | # add custom repositories here, if at least one is given, the first in the list will be used by default
21 | # otherwise the official repository is used
22 | #
23 | # format: https://github.com/username/repository, branch
24 | # example: https://github.com/Arksine/moonraker, master
25 | #
26 | # branch is optional, if given, it must be preceded by a comma, if not given, 'master' is used
27 | repositories:
28 |     https://github.com/Arksine/moonraker
29 | 
30 | [mainsail]
31 | port: 80
32 | unstable_releases: False
33 | 
34 | [fluidd]
35 | port: 80
36 | unstable_releases: False
37 | 


--------------------------------------------------------------------------------
/docs/gcode_shell_command.md:
--------------------------------------------------------------------------------
 1 | # G-Code Shell Command Extension
 2 | 
 3 | ### Creator of this extension is [Arksine](https://github.com/Arksine).
 4 | 
 5 | This is a brief explanation of how to use the shell command extension for Klipper, which you can install with KIAUH.
 6 | 
 7 | After installing the extension you can execute linux commands or even scripts from within Klipper with custom commands defined in your printer.cfg.
 8 | 
 9 | #### How to configure a shell command:
10 | 
11 | ```shell
12 | # Runs a linux command or script from within klipper.  Note that sudo commands
13 | # that require password authentication are disallowed. All executable scripts
14 | # should include a shebang.
15 | # [gcode_shell_command my_shell_cmd]
16 | #command:
17 | #  The linux shell command/script to be executed.  This parameter must be
18 | #  provided
19 | #timeout: 2.
20 | #  The timeout in seconds until the command is forcably terminated.  Default
21 | #  is 2 seconds.
22 | #verbose: True
23 | #  If enabled, the command's output will be forwarded to the terminal.  Its
24 | #  recommended to set this to false for commands that my run in quick
25 | #  succession.  Default is True.
26 | ```
27 | 
28 | 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:
29 | `RUN_SHELL_COMMAND CMD=name`
30 | 
31 | Example:
32 | 
33 | ```
34 | [gcode_shell_command hello_world]
35 | command: echo hello world
36 | timeout: 2.
37 | verbose: True
38 | ```
39 | 
40 | Execute with:
41 | `RUN_SHELL_COMMAND CMD=hello_world`
42 | 
43 | ### Passing parameters:
44 | 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`.
45 | 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, 
46 | 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`) 
47 | and writes the value of the parameter into a textfile called `test.txt` located in the home directory.
48 | 
49 | Content of the `gcode_shell_command` and the `gcode_macro`:
50 | ```
51 | [gcode_shell_command print_to_file]
52 | command: sh /home/pi/klipper_config/script.sh
53 | timeout: 30.
54 | verbose: True
55 | 
56 | [gcode_macro GET_TEMP]
57 | gcode:
58 |     {% set temp = printer.extruder.temperature %}
59 |     { action_respond_info("%s" % (temp)) }
60 |     RUN_SHELL_COMMAND CMD=print_to_file PARAMS={temp}
61 | ```
62 | 
63 | Content of `script.sh`:
64 | ```shell
65 | #!/bin/sh
66 | 
67 | echo "temp is: $1"
68 | echo "$1" >> "${HOME}/test.txt"
69 | ```
70 | 
71 | ## Warning
72 | 
73 | 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.
74 | Use this extension at your own risk and only if you know what you are doing!
75 | 


--------------------------------------------------------------------------------
/kiauh.py:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env python3
 2 | 
 3 | # ======================================================================= #
 4 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 5 | #                                                                         #
 6 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 7 | #  https://github.com/dw-0/kiauh                                          #
 8 | #                                                                         #
 9 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
10 | # ======================================================================= #
11 | 
12 | from kiauh.main import main
13 | 
14 | if __name__ == "__main__":
15 |     main()
16 | 


--------------------------------------------------------------------------------
/kiauh/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | import sys
11 | from pathlib import Path
12 | 
13 | PROJECT_ROOT = Path(__file__).resolve().parent.parent
14 | APPLICATION_ROOT = Path(__file__).resolve().parent
15 | sys.path.append(str(APPLICATION_ROOT))
16 | 


--------------------------------------------------------------------------------
/kiauh/components/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/crowsnest/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | from core.backup_manager import BACKUP_ROOT_DIR
13 | from core.constants import SYSTEMD
14 | 
15 | # repo
16 | CROWSNEST_REPO = "https://github.com/mainsail-crew/crowsnest.git"
17 | 
18 | # names
19 | CROWSNEST_SERVICE_NAME = "crowsnest.service"
20 | 
21 | # directories
22 | CROWSNEST_DIR = Path.home().joinpath("crowsnest")
23 | CROWSNEST_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("crowsnest-backups")
24 | 
25 | # files
26 | CROWSNEST_MULTI_CONFIG = CROWSNEST_DIR.joinpath("tools/.config")
27 | CROWSNEST_INSTALL_SCRIPT = CROWSNEST_DIR.joinpath("tools/install.sh")
28 | CROWSNEST_BIN_FILE = Path("/usr/local/bin/crowsnest")
29 | CROWSNEST_LOGROTATE_FILE = Path("/etc/logrotate.d/crowsnest")
30 | CROWSNEST_SERVICE_FILE = SYSTEMD.joinpath(CROWSNEST_SERVICE_NAME)
31 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | from core.backup_manager import BACKUP_ROOT_DIR
13 | 
14 | MODULE_PATH = Path(__file__).resolve().parent
15 | 
16 | KLIPPER_REPO_URL = "https://github.com/Klipper3d/klipper.git"
17 | 
18 | # names
19 | KLIPPER_LOG_NAME = "klippy.log"
20 | KLIPPER_CFG_NAME = "printer.cfg"
21 | KLIPPER_SERIAL_NAME = "klippy.serial"
22 | KLIPPER_UDS_NAME = "klippy.sock"
23 | KLIPPER_ENV_FILE_NAME = "klipper.env"
24 | KLIPPER_SERVICE_NAME = "klipper.service"
25 | 
26 | # directories
27 | KLIPPER_DIR = Path.home().joinpath("klipper")
28 | KLIPPER_KCONFIGS_DIR = Path.home().joinpath("klipper-kconfigs")
29 | KLIPPER_ENV_DIR = Path.home().joinpath("klippy-env")
30 | KLIPPER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("klipper-backups")
31 | 
32 | # files
33 | KLIPPER_REQ_FILE = KLIPPER_DIR.joinpath("scripts/klippy-requirements.txt")
34 | KLIPPER_INSTALL_SCRIPT = KLIPPER_DIR.joinpath("scripts/install-ubuntu-22.04.sh")
35 | KLIPPER_SERVICE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{KLIPPER_SERVICE_NAME}")
36 | KLIPPER_ENV_FILE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{KLIPPER_ENV_FILE_NAME}")
37 | 
38 | 
39 | EXIT_KLIPPER_SETUP = "Exiting Klipper setup ..."
40 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper/assets/klipper.env:
--------------------------------------------------------------------------------
1 | KLIPPER_ARGS="%KLIPPER_DIR%/klippy/klippy.py %CFG% -I %SERIAL% -l %LOG% -a %UDS%"
2 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper/assets/klipper.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=Klipper 3D Printer Firmware SV1
 3 | Documentation=https://www.klipper3d.org/
 4 | After=network-online.target
 5 | Wants=udev.target
 6 | 
 7 | [Install]
 8 | WantedBy=multi-user.target
 9 | 
10 | [Service]
11 | Type=simple
12 | User=%USER%
13 | RemainAfterExit=yes
14 | WorkingDirectory=%KLIPPER_DIR%
15 | EnvironmentFile=%ENV_FILE%
16 | ExecStart=%ENV%/bin/python $KLIPPER_ARGS
17 | Restart=always
18 | RestartSec=10
19 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper/assets/printer.cfg:
--------------------------------------------------------------------------------
 1 | [mcu]
 2 | serial: /dev/serial/by-id/<your-mcu-id>
 3 | 
 4 | [virtual_sdcard]
 5 | path: %GCODES_DIR%
 6 | on_error_gcode: CANCEL_PRINT
 7 | 
 8 | [printer]
 9 | kinematics: none
10 | max_velocity: 1000
11 | max_accel: 1000
12 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper/menus/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/klipper/menus/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/klipper/services/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/klipper/services/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/klipper/services/klipper_instance_service.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from typing import List
12 | 
13 | from components.klipper.klipper import Klipper
14 | from utils.instance_utils import get_instances
15 | 
16 | 
17 | class KlipperInstanceService:
18 |     __cls_instance = None
19 |     __instances: List[Klipper] = []
20 | 
21 |     def __new__(cls) -> "KlipperInstanceService":
22 |         if cls.__cls_instance is None:
23 |             cls.__cls_instance = super(KlipperInstanceService, cls).__new__(cls)
24 |         return cls.__cls_instance
25 | 
26 |     def __init__(self) -> None:
27 |         if not hasattr(self, "__initialized"):
28 |             self.__initialized = False
29 |         if self.__initialized:
30 |             return
31 |         self.__initialized = True
32 | 
33 |     def load_instances(self) -> None:
34 |         self.__instances = get_instances(Klipper)
35 | 
36 |     def create_new_instance(self, suffix: str) -> Klipper:
37 |         instance = Klipper(suffix)
38 |         self.__instances.append(instance)
39 |         return instance
40 | 
41 |     def get_all_instances(self) -> List[Klipper]:
42 |         return self.__instances
43 | 
44 |     def get_instance_by_suffix(self, suffix: str) -> Klipper | None:
45 |         instances: List[Klipper] = [i for i in self.__instances if i.suffix == suffix]
46 |         return instances[0] if instances else None
47 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper_firmware/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from components.klipper import KLIPPER_DIR
11 | 
12 | SD_FLASH_SCRIPT = KLIPPER_DIR.joinpath("scripts/flash-sdcard.sh")
13 | 


--------------------------------------------------------------------------------
/kiauh/components/klipper_firmware/flash_options.py:
--------------------------------------------------------------------------------
  1 | # ======================================================================= #
  2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
  3 | #                                                                         #
  4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
  5 | #  https://github.com/dw-0/kiauh                                          #
  6 | #                                                                         #
  7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
  8 | # ======================================================================= #
  9 | from __future__ import annotations
 10 | 
 11 | from dataclasses import field
 12 | from enum import Enum
 13 | from typing import List
 14 | 
 15 | 
 16 | class FlashMethod(Enum):
 17 |     REGULAR = "Regular"
 18 |     SD_CARD = "SD Card"
 19 | 
 20 | 
 21 | class FlashCommand(Enum):
 22 |     FLASH = "flash"
 23 |     SERIAL_FLASH = "serialflash"
 24 | 
 25 | 
 26 | class ConnectionType(Enum):
 27 |     USB = "USB"
 28 |     USB_DFU = "USB (DFU)"
 29 |     USB_RP2040 = "USB (RP2040)"
 30 |     UART = "UART"
 31 | 
 32 | 
 33 | class FlashOptions:
 34 |     _instance = None
 35 |     _flash_method: FlashMethod | None = None
 36 |     _flash_command: FlashCommand | None = None
 37 |     _connection_type: ConnectionType | None = None
 38 |     _mcu_list: List[str] = field(default_factory=list)
 39 |     _selected_mcu: str = ""
 40 |     _selected_board: str = ""
 41 |     _selected_baudrate: int = 250000
 42 |     _selected_kconfig: str = ".config"
 43 | 
 44 |     def __new__(cls, *args, **kwargs):
 45 |         if not cls._instance:
 46 |             cls._instance = super(FlashOptions, cls).__new__(cls, *args, **kwargs)
 47 |         return cls._instance
 48 | 
 49 |     @classmethod
 50 |     def destroy(cls) -> None:
 51 |         cls._instance = None
 52 | 
 53 |     @property
 54 |     def flash_method(self) -> FlashMethod | None:
 55 |         return self._flash_method
 56 | 
 57 |     @flash_method.setter
 58 |     def flash_method(self, value: FlashMethod | None):
 59 |         self._flash_method = value
 60 | 
 61 |     @property
 62 |     def flash_command(self) -> FlashCommand | None:
 63 |         return self._flash_command
 64 | 
 65 |     @flash_command.setter
 66 |     def flash_command(self, value: FlashCommand | None):
 67 |         self._flash_command = value
 68 | 
 69 |     @property
 70 |     def connection_type(self) -> ConnectionType | None:
 71 |         return self._connection_type
 72 | 
 73 |     @connection_type.setter
 74 |     def connection_type(self, value: ConnectionType | None):
 75 |         self._connection_type = value
 76 | 
 77 |     @property
 78 |     def mcu_list(self) -> List[str]:
 79 |         return self._mcu_list
 80 | 
 81 |     @mcu_list.setter
 82 |     def mcu_list(self, value: List[str]) -> None:
 83 |         self._mcu_list = value
 84 | 
 85 |     @property
 86 |     def selected_mcu(self) -> str:
 87 |         return self._selected_mcu
 88 | 
 89 |     @selected_mcu.setter
 90 |     def selected_mcu(self, value: str) -> None:
 91 |         self._selected_mcu = value
 92 | 
 93 |     @property
 94 |     def selected_board(self) -> str:
 95 |         return self._selected_board
 96 | 
 97 |     @selected_board.setter
 98 |     def selected_board(self, value: str) -> None:
 99 |         self._selected_board = value
100 | 
101 |     @property
102 |     def selected_baudrate(self) -> int:
103 |         return self._selected_baudrate
104 | 
105 |     @selected_baudrate.setter
106 |     def selected_baudrate(self, value: int) -> None:
107 |         self._selected_baudrate = value
108 | 
109 |     @property
110 |     def selected_kconfig(self) -> str:
111 |         return self._selected_kconfig
112 | 
113 |     @selected_kconfig.setter
114 |     def selected_kconfig(self, value: str) -> None:
115 |         self._selected_kconfig = value
116 | 


--------------------------------------------------------------------------------
/kiauh/components/klipperscreen/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from pathlib import Path
10 | 
11 | from core.backup_manager import BACKUP_ROOT_DIR
12 | from core.constants import SYSTEMD
13 | 
14 | # repo
15 | KLIPPERSCREEN_REPO = "https://github.com/KlipperScreen/KlipperScreen.git"
16 | 
17 | # names
18 | KLIPPERSCREEN_SERVICE_NAME = "KlipperScreen.service"
19 | KLIPPERSCREEN_UPDATER_SECTION_NAME = "update_manager KlipperScreen"
20 | KLIPPERSCREEN_LOG_NAME = "KlipperScreen.log"
21 | 
22 | # directories
23 | KLIPPERSCREEN_DIR = Path.home().joinpath("KlipperScreen")
24 | KLIPPERSCREEN_ENV_DIR = Path.home().joinpath(".KlipperScreen-env")
25 | KLIPPERSCREEN_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("klipperscreen-backups")
26 | 
27 | # files
28 | KLIPPERSCREEN_REQ_FILE = KLIPPERSCREEN_DIR.joinpath(
29 |     "scripts/KlipperScreen-requirements.txt"
30 | )
31 | KLIPPERSCREEN_INSTALL_SCRIPT = KLIPPERSCREEN_DIR.joinpath(
32 |     "scripts/KlipperScreen-install.sh"
33 | )
34 | KLIPPERSCREEN_SERVICE_FILE = SYSTEMD.joinpath(KLIPPERSCREEN_SERVICE_NAME)
35 | 


--------------------------------------------------------------------------------
/kiauh/components/log_uploads/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | from typing import Dict, Literal, Union
12 | 
13 | FileKey = Literal["filepath", "display_name"]
14 | LogFile = Dict[FileKey, Union[str, Path]]
15 | 


--------------------------------------------------------------------------------
/kiauh/components/log_uploads/log_upload_utils.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | import urllib.request
11 | from pathlib import Path
12 | from typing import List
13 | 
14 | from components.klipper.klipper import Klipper
15 | from components.log_uploads import LogFile
16 | from core.logger import Logger
17 | from utils.instance_utils import get_instances
18 | 
19 | 
20 | def get_logfile_list() -> List[LogFile]:
21 |     log_dirs: List[Path] = [
22 |         instance.base.log_dir for instance in get_instances(Klipper)
23 |     ]
24 | 
25 |     logfiles: List[LogFile] = []
26 |     for _dir in log_dirs:
27 |         for f in _dir.iterdir():
28 |             logfiles.append({"filepath": f, "display_name": get_display_name(f)})
29 | 
30 |     return logfiles
31 | 
32 | 
33 | def get_display_name(filepath: Path) -> str:
34 |     printer = " ".join(filepath.parts[-3].split("_")[:-1])
35 |     name = filepath.name
36 | 
37 |     return f"{printer}: {name}"
38 | 
39 | 
40 | def upload_logfile(logfile: LogFile) -> None:
41 |     file = logfile.get("filepath")
42 |     name = logfile.get("display_name")
43 |     Logger.print_status(f"Uploading the following logfile from {name} ...")
44 | 
45 |     with open(file, "rb") as f:
46 |         headers = {"x-random": ""}
47 |         req = urllib.request.Request("http://paste.c-net.org/", headers=headers, data=f)
48 |         try:
49 |             response = urllib.request.urlopen(req)
50 |             link = response.read().decode("utf-8")
51 |             Logger.print_ok("Upload successful! Access it via the following link:")
52 |             Logger.print_ok(f">>>> {link}", False)
53 |         except Exception as e:
54 |             Logger.print_error("Uploading logfile failed!")
55 |             Logger.print_error(str(e))
56 | 


--------------------------------------------------------------------------------
/kiauh/components/log_uploads/menus/log_upload_menu.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | import textwrap
12 | from typing import Type
13 | 
14 | from components.log_uploads.log_upload_utils import get_logfile_list, upload_logfile
15 | from core.logger import Logger
16 | from core.menus import Option
17 | from core.menus.base_menu import BaseMenu
18 | from core.types.color import Color
19 | 
20 | 
21 | # noinspection PyMethodMayBeStatic
22 | class LogUploadMenu(BaseMenu):
23 |     def __init__(self, previous_menu: Type[BaseMenu] | None = None):
24 |         super().__init__()
25 |         self.title = "Log Upload"
26 |         self.title_color = Color.YELLOW
27 |         self.previous_menu: Type[BaseMenu] | None = previous_menu
28 |         self.logfile_list = get_logfile_list()
29 | 
30 |     def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
31 |         from core.menus.main_menu import MainMenu
32 | 
33 |         self.previous_menu = previous_menu if previous_menu is not None else MainMenu
34 | 
35 |     def set_options(self) -> None:
36 |         self.options = {
37 |             f"{index}": Option(self.upload, opt_index=f"{index}")
38 |             for index in range(len(self.logfile_list))
39 |         }
40 | 
41 |     def print_menu(self) -> None:
42 |         menu = textwrap.dedent(
43 |             """
44 |             ╟───────────────────────────────────────────────────────╢
45 |             ║ You can select the following logfiles for uploading:  ║
46 |             ║                                                       ║
47 |             """
48 |         )[1:]
49 | 
50 |         for logfile in enumerate(self.logfile_list):
51 |             line = f"{logfile[0]}) {logfile[1].get('display_name')}"
52 |             menu += f"║ {line:<54}║\n"
53 |         menu += "╟───────────────────────────────────────────────────────╢\n"
54 | 
55 |         print(menu, end="")
56 | 
57 |     def upload(self, **kwargs):
58 |         try:
59 |             index: int | None = kwargs.get("opt_index", None)
60 |             if index is None:
61 |                 raise Exception("opt_index is None")
62 | 
63 |             index = int(index)
64 |             upload_logfile(self.logfile_list[index])
65 |         except Exception as e:
66 |             Logger.print_error(e)
67 |             Logger.print_error("Log upload failed!")
68 | 


--------------------------------------------------------------------------------
/kiauh/components/moonraker/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | from core.backup_manager import BACKUP_ROOT_DIR
13 | 
14 | MODULE_PATH = Path(__file__).resolve().parent
15 | 
16 | MOONRAKER_REPO_URL = "https://github.com/Arksine/moonraker.git"
17 | 
18 | # names
19 | MOONRAKER_CFG_NAME = "moonraker.conf"
20 | MOONRAKER_LOG_NAME = "moonraker.log"
21 | MOONRAKER_SERVICE_NAME = "moonraker.service"
22 | MOONRAKER_DEFAULT_PORT = 7125
23 | MOONRAKER_ENV_FILE_NAME = "moonraker.env"
24 | 
25 | # directories
26 | MOONRAKER_DIR = Path.home().joinpath("moonraker")
27 | MOONRAKER_ENV_DIR = Path.home().joinpath("moonraker-env")
28 | MOONRAKER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("moonraker-backups")
29 | MOONRAKER_DB_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("moonraker-db-backups")
30 | 
31 | # files
32 | MOONRAKER_INSTALL_SCRIPT = MOONRAKER_DIR.joinpath("scripts/install-moonraker.sh")
33 | MOONRAKER_REQ_FILE = MOONRAKER_DIR.joinpath("scripts/moonraker-requirements.txt")
34 | MOONRAKER_SPEEDUPS_REQ_FILE = MOONRAKER_DIR.joinpath("scripts/moonraker-speedups.txt")
35 | MOONRAKER_DEPS_JSON_FILE = MOONRAKER_DIR.joinpath("scripts/system-dependencies.json")
36 | # introduced due to
37 | # https://github.com/Arksine/moonraker/issues/349
38 | # https://github.com/Arksine/moonraker/pull/346
39 | POLKIT_LEGACY_FILE = Path("/etc/polkit-1/localauthority/50-local.d/10-moonraker.pkla")
40 | POLKIT_FILE = Path("/etc/polkit-1/rules.d/moonraker.rules")
41 | POLKIT_USR_FILE = Path("/usr/share/polkit-1/rules.d/moonraker.rules")
42 | POLKIT_SCRIPT = MOONRAKER_DIR.joinpath("scripts/set-policykit-rules.sh")
43 | MOONRAKER_SERVICE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{MOONRAKER_SERVICE_NAME}")
44 | MOONRAKER_ENV_FILE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{MOONRAKER_ENV_FILE_NAME}")
45 | 
46 | 
47 | EXIT_MOONRAKER_SETUP = "Exiting Moonraker setup ..."
48 | 


--------------------------------------------------------------------------------
/kiauh/components/moonraker/assets/moonraker.conf:
--------------------------------------------------------------------------------
 1 | [server]
 2 | host: 0.0.0.0
 3 | port: %PORT%
 4 | klippy_uds_address: %UDS%
 5 | 
 6 | [authorization]
 7 | trusted_clients:
 8 |     10.0.0.0/8
 9 |     127.0.0.0/8
10 |     169.254.0.0/16
11 |     172.16.0.0/12
12 |     192.168.0.0/16
13 |     FC00::/7
14 |     FE80::/10
15 |     ::1/128
16 | cors_domains:
17 |     *.lan
18 |     *.local
19 |     *://localhost
20 |     *://localhost:*
21 |     *://my.mainsail.xyz
22 |     *://app.fluidd.xyz
23 | 
24 | [octoprint_compat]
25 | 
26 | [history]
27 | 
28 | [update_manager]
29 | channel: dev
30 | refresh_interval: 168
31 | 


--------------------------------------------------------------------------------
/kiauh/components/moonraker/assets/moonraker.env:
--------------------------------------------------------------------------------
1 | MOONRAKER_ARGS="%MOONRAKER_DIR%/moonraker/moonraker.py -d %PRINTER_DATA%"


--------------------------------------------------------------------------------
/kiauh/components/moonraker/assets/moonraker.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=API Server for Klipper SV1
 3 | Documentation=https://moonraker.readthedocs.io/
 4 | Requires=network-online.target
 5 | After=network-online.target
 6 | 
 7 | [Install]
 8 | WantedBy=multi-user.target
 9 | 
10 | [Service]
11 | Type=simple
12 | User=%USER%
13 | SupplementaryGroups=moonraker-admin
14 | RemainAfterExit=yes
15 | WorkingDirectory=%MOONRAKER_DIR%
16 | EnvironmentFile=%ENV_FILE%
17 | ExecStart=%ENV%/bin/python $MOONRAKER_ARGS
18 | Restart=always
19 | RestartSec=10
20 | 


--------------------------------------------------------------------------------
/kiauh/components/moonraker/menus/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/moonraker/menus/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/moonraker/moonraker_dialogs.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | import textwrap
11 | from typing import List
12 | 
13 | from components.klipper.klipper import Klipper
14 | from components.moonraker.moonraker import Moonraker
15 | from core.menus.base_menu import print_back_footer
16 | from core.types.color import Color
17 | 
18 | 
19 | def print_moonraker_overview(
20 |     klipper_instances: List[Klipper],
21 |     moonraker_instances: List[Moonraker],
22 |     show_index=False,
23 |     show_select_all=False,
24 | ):
25 |     headline = Color.apply("The following instances were found:", Color.GREEN)
26 |     dialog = textwrap.dedent(
27 |         f"""
28 |         ╔═══════════════════════════════════════════════════════╗
29 |         ║{headline:^64}║
30 |         ╟───────────────────────────────────────────────────────╢
31 |         """
32 |     )[1:]
33 | 
34 |     if show_select_all:
35 |         select_all = Color.apply("a) Select all", Color.YELLOW)
36 |         dialog += f"║ {select_all:<63}║\n"
37 |         dialog += "║                                                       ║\n"
38 | 
39 |     instance_map = {
40 |         k.service_file_path.stem: (
41 |             k.service_file_path.stem.replace("klipper", "moonraker")
42 |             if k.suffix in [m.suffix for m in moonraker_instances]
43 |             else ""
44 |         )
45 |         for k in klipper_instances
46 |     }
47 | 
48 |     for i, k in enumerate(instance_map):
49 |         mr_name = instance_map.get(k)
50 |         m = f"<-> {mr_name}" if mr_name != "" else ""
51 |         line = Color.apply(f"{f'{i + 1})' if show_index else '●'} {k} {m}", Color.CYAN)
52 |         dialog += f"║ {line:<63}║\n"
53 | 
54 |     warn_l1 = Color.apply("PLEASE NOTE:", Color.YELLOW)
55 |     warn_l2 = Color.apply(
56 |         "If you select an instance with an existing Moonraker", Color.YELLOW
57 |     )
58 |     warn_l3 = Color.apply(
59 |         "instance, that Moonraker instance will be re-created!", Color.YELLOW
60 |     )
61 |     warning = textwrap.dedent(
62 |         f"""
63 |         ║                                                       ║
64 |         ╟───────────────────────────────────────────────────────╢
65 |         ║ {warn_l1:<63}║
66 |         ║ {warn_l2:<63}║
67 |         ║ {warn_l3:<63}║
68 |         ╟───────────────────────────────────────────────────────╢
69 |         """
70 |     )[1:]
71 | 
72 |     dialog += warning
73 | 
74 |     print(dialog, end="")
75 |     print_back_footer()
76 | 


--------------------------------------------------------------------------------
/kiauh/components/moonraker/services/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/moonraker/services/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/moonraker/services/moonraker_instance_service.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from typing import Dict, List
12 | 
13 | from components.moonraker.moonraker import Moonraker
14 | from utils.instance_utils import get_instances
15 | 
16 | 
17 | class MoonrakerInstanceService:
18 |     __cls_instance = None
19 |     __instances: List[Moonraker] = []
20 | 
21 |     def __new__(cls) -> "MoonrakerInstanceService":
22 |         if cls.__cls_instance is None:
23 |             cls.__cls_instance = super(MoonrakerInstanceService, cls).__new__(cls)
24 |         return cls.__cls_instance
25 | 
26 |     def __init__(self) -> None:
27 |         if not hasattr(self, "__initialized"):
28 |             self.__initialized = False
29 |         if self.__initialized:
30 |             return
31 |         self.__initialized = True
32 | 
33 |     def load_instances(self) -> None:
34 |         self.__instances = get_instances(Moonraker)
35 | 
36 |     def create_new_instance(self, suffix: str) -> Moonraker:
37 |         instance = Moonraker(suffix)
38 |         self.__instances.append(instance)
39 |         return instance
40 | 
41 |     def get_all_instances(self) -> List[Moonraker]:
42 |         return self.__instances
43 | 
44 |     def get_instance_by_suffix(self, suffix: str) -> Moonraker | None:
45 |         instances: List[Moonraker] = [i for i in self.__instances if i.suffix == suffix]
46 |         return instances[0] if instances else None
47 | 
48 |     def get_instance_port_map(self) -> Dict[str, int]:
49 |         return {i.suffix: i.port for i in self.__instances}
50 | 


--------------------------------------------------------------------------------
/kiauh/components/moonraker/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/moonraker/utils/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/webui_client/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | MODULE_PATH = Path(__file__).resolve().parent
13 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/assets/common_vars.conf:
--------------------------------------------------------------------------------
1 | # /etc/nginx/conf.d/common_vars.conf
2 | 
3 | map $http_upgrade $connection_upgrade {
4 |     default upgrade;
5 |     '' close;
6 | }


--------------------------------------------------------------------------------
/kiauh/components/webui_client/assets/nginx_cfg:
--------------------------------------------------------------------------------
 1 | server {
 2 |     listen %PORT%;
 3 |     # uncomment the next line to activate IPv6
 4 |     # listen [::]:%PORT%;
 5 | 
 6 |     access_log /var/log/nginx/%NAME%-access.log;
 7 |     error_log /var/log/nginx/%NAME%-error.log;
 8 | 
 9 |     # disable this section on smaller hardware like a pi zero
10 |     gzip on;
11 |     gzip_vary on;
12 |     gzip_proxied any;
13 |     gzip_proxied expired no-cache no-store private auth;
14 |     gzip_comp_level 4;
15 |     gzip_buffers 16 8k;
16 |     gzip_http_version 1.1;
17 |     gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;
18 | 
19 |     # web_path from %NAME% static files
20 |     root %ROOT_DIR%;
21 | 
22 |     index index.html;
23 |     server_name _;
24 | 
25 |     # disable max upload size checks
26 |     client_max_body_size 0;
27 | 
28 |     # disable proxy request buffering
29 |     proxy_request_buffering off;
30 | 
31 |     location / {
32 |         try_files $uri $uri/ /index.html;
33 |     }
34 | 
35 |     location = /index.html {
36 |         add_header Cache-Control "no-store, no-cache, must-revalidate";
37 |     }
38 | 
39 |     location /websocket {
40 |         proxy_pass http://apiserver/websocket;
41 |         proxy_http_version 1.1;
42 |         proxy_set_header Upgrade $http_upgrade;
43 |         proxy_set_header Connection $connection_upgrade;
44 |         proxy_set_header Host $http_host;
45 |         proxy_set_header X-Real-IP $remote_addr;
46 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
47 |         proxy_read_timeout 86400;
48 |     }
49 | 
50 |     location ~ ^/(printer|api|access|machine|server)/ {
51 |         proxy_pass http://apiserver$request_uri;
52 |         proxy_http_version 1.1;
53 |         proxy_set_header Upgrade $http_upgrade;
54 |         proxy_set_header Host $http_host;
55 |         proxy_set_header X-Real-IP $remote_addr;
56 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57 |         proxy_set_header X-Scheme $scheme;
58 |     }
59 | 
60 |     location /webcam/ {
61 |         postpone_output 0;
62 |         proxy_buffering off;
63 |         proxy_ignore_headers X-Accel-Buffering;
64 |         access_log off;
65 |         error_log off;
66 |         proxy_pass http://mjpgstreamer1/;
67 |     }
68 | 
69 |     location /webcam2/ {
70 |         postpone_output 0;
71 |         proxy_buffering off;
72 |         proxy_ignore_headers X-Accel-Buffering;
73 |         access_log off;
74 |         error_log off;
75 |         proxy_pass http://mjpgstreamer2/;
76 |     }
77 | 
78 |     location /webcam3/ {
79 |         postpone_output 0;
80 |         proxy_buffering off;
81 |         proxy_ignore_headers X-Accel-Buffering;
82 |         access_log off;
83 |         error_log off;
84 |         proxy_pass http://mjpgstreamer3/;
85 |     }
86 | 
87 |     location /webcam4/ {
88 |         postpone_output 0;
89 |         proxy_buffering off;
90 |         proxy_ignore_headers X-Accel-Buffering;
91 |         access_log off;
92 |         error_log off;
93 |         proxy_pass http://mjpgstreamer4/;
94 |     }
95 | }
96 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/assets/upstreams.conf:
--------------------------------------------------------------------------------
 1 | # /etc/nginx/conf.d/upstreams.conf
 2 | upstream apiserver {
 3 |     ip_hash;
 4 |     server 127.0.0.1:7125;
 5 | }
 6 | 
 7 | upstream mjpgstreamer1 {
 8 |     ip_hash;
 9 |     server 127.0.0.1:8080;
10 | }
11 | 
12 | upstream mjpgstreamer2 {
13 |     ip_hash;
14 |     server 127.0.0.1:8081;
15 | }
16 | 
17 | upstream mjpgstreamer3 {
18 |     ip_hash;
19 |     server 127.0.0.1:8082;
20 | }
21 | 
22 | upstream mjpgstreamer4 {
23 |     ip_hash;
24 |     server 127.0.0.1:8083;
25 | }


--------------------------------------------------------------------------------
/kiauh/components/webui_client/base_data.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from __future__ import annotations
11 | 
12 | from abc import ABC
13 | from dataclasses import dataclass
14 | from enum import Enum
15 | from pathlib import Path
16 | 
17 | 
18 | class WebClientType(Enum):
19 |     MAINSAIL: str = "mainsail"
20 |     FLUIDD: str = "fluidd"
21 | 
22 | 
23 | class WebClientConfigType(Enum):
24 |     MAINSAIL: str = "mainsail-config"
25 |     FLUIDD: str = "fluidd-config"
26 | 
27 | 
28 | @dataclass()
29 | class BaseWebClient(ABC):
30 |     """Base class for webclient data"""
31 | 
32 |     client: WebClientType
33 |     name: str
34 |     display_name: str
35 |     client_dir: Path
36 |     config_file: Path
37 |     backup_dir: Path
38 |     repo_path: str
39 |     download_url: str
40 |     nginx_config: Path
41 |     nginx_access_log: Path
42 |     nginx_error_log: Path
43 |     client_config: BaseWebClientConfig
44 | 
45 | 
46 | @dataclass()
47 | class BaseWebClientConfig(ABC):
48 |     """Base class for webclient config data"""
49 | 
50 |     client_config: WebClientConfigType
51 |     name: str
52 |     display_name: str
53 |     config_filename: str
54 |     config_dir: Path
55 |     backup_dir: Path
56 |     repo_url: str
57 |     config_section: str
58 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/client_config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/webui_client/client_config/__init__.py


--------------------------------------------------------------------------------
/kiauh/components/webui_client/client_config/client_config_remove.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | 
11 | from typing import List
12 | 
13 | from components.klipper.klipper import Klipper
14 | from components.moonraker.moonraker import Moonraker
15 | from components.webui_client.base_data import BaseWebClientConfig
16 | from core.logger import Logger
17 | from core.services.message_service import Message
18 | from core.types.color import Color
19 | from utils.config_utils import remove_config_section
20 | from utils.fs_utils import run_remove_routines
21 | from utils.instance_type import InstanceType
22 | from utils.instance_utils import get_instances
23 | 
24 | 
25 | def run_client_config_removal(
26 |     client_config: BaseWebClientConfig,
27 |     kl_instances: List[Klipper],
28 |     mr_instances: List[Moonraker],
29 | ) -> Message:
30 |     completion_msg = Message(
31 |         title=f"{client_config.display_name} Removal Process completed",
32 |         color=Color.GREEN,
33 |     )
34 |     Logger.print_status(f"Removing {client_config.display_name} ...")
35 |     if run_remove_routines(client_config.config_dir):
36 |         completion_msg.text.append(f"● {client_config.display_name} removed")
37 | 
38 |     completion_msg = remove_moonraker_config_section(
39 |         completion_msg, client_config, mr_instances
40 |     )
41 | 
42 |     completion_msg = remove_printer_config_section(
43 |         completion_msg, client_config, kl_instances
44 |     )
45 | 
46 |     if completion_msg.text:
47 |         completion_msg.text.insert(0, "The following actions were performed:")
48 |     else:
49 |         completion_msg.color = Color.YELLOW
50 |         completion_msg.centered = True
51 |         completion_msg.text = ["Nothing to remove."]
52 | 
53 |     return completion_msg
54 | 
55 | 
56 | def remove_cfg_symlink(client_config: BaseWebClientConfig, message: Message) -> Message:
57 |     instances: List[Klipper] = get_instances(Klipper)
58 |     kl_instances = []
59 |     for instance in instances:
60 |         cfg = instance.base.cfg_dir.joinpath(client_config.config_filename)
61 |         if run_remove_routines(cfg):
62 |             kl_instances.append(instance)
63 |     text = f"{client_config.display_name} removed from instance"
64 |     return update_msg(kl_instances, message, text)
65 | 
66 | 
67 | def remove_printer_config_section(
68 |     message: Message, client_config: BaseWebClientConfig, kl_instances: List[Klipper]
69 | ) -> Message:
70 |     kl_section = client_config.config_section
71 |     kl_instances = remove_config_section(kl_section, kl_instances)
72 |     text = f"Klipper config section '{kl_section}' removed for instance"
73 |     return update_msg(kl_instances, message, text)
74 | 
75 | 
76 | def remove_moonraker_config_section(
77 |     message: Message, client_config: BaseWebClientConfig, mr_instances: List[Moonraker]
78 | ) -> Message:
79 |     mr_section = f"update_manager {client_config.name}"
80 |     mr_instances = remove_config_section(mr_section, mr_instances)
81 |     text = f"Moonraker config section '{mr_section}' removed for instance"
82 |     return update_msg(mr_instances, message, text)
83 | 
84 | 
85 | def update_msg(instances: List[InstanceType], message: Message, text: str) -> Message:
86 |     if not instances:
87 |         return message
88 | 
89 |     instance_names = [i.service_file_path.stem for i in instances]
90 |     message.text.append(f"● {text}: {', '.join(instance_names)}")
91 |     return message
92 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/client_dialogs.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from typing import List
11 | 
12 | from components.webui_client.base_data import BaseWebClient
13 | from core.logger import DialogType, Logger
14 | 
15 | 
16 | def print_moonraker_not_found_dialog(name: str) -> None:
17 |     Logger.print_dialog(
18 |         DialogType.WARNING,
19 |         [
20 |             "No local Moonraker installation was found!",
21 |             "\n\n",
22 |             f"It is possible to install {name} without a local Moonraker installation. "
23 |             "If you continue, you need to make sure, that Moonraker is installed on "
24 |             f"another machine in your network. Otherwise {name} will NOT work "
25 |             "correctly.",
26 |         ],
27 |     )
28 | 
29 | 
30 | def print_client_already_installed_dialog(name: str) -> None:
31 |     Logger.print_dialog(
32 |         DialogType.WARNING,
33 |         [
34 |             f"{name} seems to be already installed!",
35 |             f"If you continue, your current {name} installation will be overwritten.",
36 |         ],
37 |     )
38 | 
39 | 
40 | def print_client_port_select_dialog(
41 |     name: str, port: int, ports_in_use: List[int]
42 | ) -> None:
43 |     dialog_content: List[str] = [
44 |         f"Please select the port, {name} should be served on. If your are unsure "
45 |         f"what to select, hit Enter to apply the suggested value of: {port}",
46 |         "\n\n",
47 |         f"In case you need {name} to be served on a specific port, you can set it "
48 |         f"now. Make sure that the port is not already used by another application "
49 |         f"on your system!",
50 |     ]
51 | 
52 |     if ports_in_use:
53 |         dialog_content.extend(
54 |             [
55 |                 "\n\n",
56 |                 "The following ports were found to be already in use:",
57 |                 *[f"● {p}" for p in ports_in_use if p != port],
58 |             ]
59 |         )
60 | 
61 |     Logger.print_dialog(DialogType.CUSTOM, dialog_content)
62 | 
63 | 
64 | def print_install_client_config_dialog(client: BaseWebClient) -> None:
65 |     name = client.display_name
66 |     url = client.client_config.repo_url.replace(".git", "")
67 |     Logger.print_dialog(
68 |         DialogType.INFO,
69 |         [
70 |             f"It is recommended to use special macros in order to have {name} fully "
71 |             f"functional and working.",
72 |             "\n\n",
73 |             f"The recommended macros for {name} can be seen here:",
74 |             url,
75 |             "\n\n",
76 |             "If you already use these macros skip this step. Otherwise you should "
77 |             "consider to answer with 'Y' to download the recommended macros.",
78 |         ],
79 |     )
80 | 
81 | 
82 | def print_ipv6_warning_dialog() -> None:
83 |     Logger.print_dialog(
84 |         DialogType.WARNING,
85 |         [
86 |             "It looks like IPv6 is enabled on this system!",
87 |             "This may cause issues with the installation of NGINX in the following "
88 |             "steps! It is recommended to disable IPv6 on your system to avoid this issue.",
89 |             "\n\n",
90 |             "If you think this warning is a false alarm, and you are sure that "
91 |             "IPv6 is disabled, you can continue with the installation.",
92 |         ],
93 |     )
94 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/fluidd_data.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from __future__ import annotations
11 | 
12 | from dataclasses import dataclass
13 | from pathlib import Path
14 | 
15 | from components.webui_client.base_data import (
16 |     BaseWebClient,
17 |     BaseWebClientConfig,
18 |     WebClientConfigType,
19 |     WebClientType,
20 | )
21 | from core.backup_manager import BACKUP_ROOT_DIR
22 | from core.constants import NGINX_SITES_AVAILABLE
23 | 
24 | 
25 | @dataclass()
26 | class FluiddConfigWeb(BaseWebClientConfig):
27 |     client_config: WebClientConfigType = WebClientConfigType.FLUIDD
28 |     name: str = client_config.value
29 |     display_name: str = name.title()
30 |     config_dir: Path = Path.home().joinpath("fluidd-config")
31 |     config_filename: str = "fluidd.cfg"
32 |     config_section: str = f"include {config_filename}"
33 |     backup_dir: Path = BACKUP_ROOT_DIR.joinpath("fluidd-config-backups")
34 |     repo_url: str = "https://github.com/fluidd-core/fluidd-config.git"
35 | 
36 | 
37 | @dataclass()
38 | class FluiddData(BaseWebClient):
39 |     BASE_DL_URL = "https://github.com/fluidd-core/fluidd/releases"
40 | 
41 |     client: WebClientType = WebClientType.FLUIDD
42 |     name: str = client.value
43 |     display_name: str = name.capitalize()
44 |     client_dir: Path = Path.home().joinpath("fluidd")
45 |     config_file: Path = client_dir.joinpath("config.json")
46 |     backup_dir: Path = BACKUP_ROOT_DIR.joinpath("fluidd-backups")
47 |     repo_path: str = "fluidd-core/fluidd"
48 |     nginx_config: Path = NGINX_SITES_AVAILABLE.joinpath("fluidd")
49 |     nginx_access_log: Path = Path("/var/log/nginx/fluidd-access.log")
50 |     nginx_error_log: Path = Path("/var/log/nginx/fluidd-error.log")
51 |     client_config: BaseWebClientConfig = None
52 |     download_url: str | None = None
53 | 
54 |     def __post_init__(self):
55 |         from components.webui_client.client_utils import get_download_url
56 | 
57 |         self.client_config = FluiddConfigWeb()
58 |         self.download_url = get_download_url(self.BASE_DL_URL, self)
59 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/mainsail_data.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from __future__ import annotations
11 | 
12 | from dataclasses import dataclass
13 | from pathlib import Path
14 | 
15 | from components.webui_client.base_data import (
16 |     BaseWebClient,
17 |     BaseWebClientConfig,
18 |     WebClientConfigType,
19 |     WebClientType,
20 | )
21 | from core.backup_manager import BACKUP_ROOT_DIR
22 | from core.constants import NGINX_SITES_AVAILABLE
23 | 
24 | 
25 | @dataclass()
26 | class MainsailConfigWeb(BaseWebClientConfig):
27 |     client_config: WebClientConfigType = WebClientConfigType.MAINSAIL
28 |     name: str = client_config.value
29 |     display_name: str = name.title()
30 |     config_dir: Path = Path.home().joinpath("mainsail-config")
31 |     config_filename: str = "mainsail.cfg"
32 |     config_section: str = f"include {config_filename}"
33 |     backup_dir: Path = BACKUP_ROOT_DIR.joinpath("mainsail-config-backups")
34 |     repo_url: str = "https://github.com/mainsail-crew/mainsail-config.git"
35 | 
36 | 
37 | @dataclass()
38 | class MainsailData(BaseWebClient):
39 |     BASE_DL_URL: str = "https://github.com/mainsail-crew/mainsail/releases"
40 | 
41 |     client: WebClientType = WebClientType.MAINSAIL
42 |     name: str = WebClientType.MAINSAIL.value
43 |     display_name: str = name.capitalize()
44 |     client_dir: Path = Path.home().joinpath("mainsail")
45 |     config_file: Path = client_dir.joinpath("config.json")
46 |     backup_dir: Path = BACKUP_ROOT_DIR.joinpath("mainsail-backups")
47 |     repo_path: str = "mainsail-crew/mainsail"
48 |     nginx_config: Path = NGINX_SITES_AVAILABLE.joinpath("mainsail")
49 |     nginx_access_log: Path = Path("/var/log/nginx/mainsail-access.log")
50 |     nginx_error_log: Path = Path("/var/log/nginx/mainsail-error.log")
51 |     client_config: BaseWebClientConfig = None
52 |     download_url: str | None = None
53 | 
54 |     def __post_init__(self):
55 |         from components.webui_client.client_utils import get_download_url
56 | 
57 |         self.client_config = MainsailConfigWeb()
58 |         self.download_url = get_download_url(self.BASE_DL_URL, self)
59 | 


--------------------------------------------------------------------------------
/kiauh/components/webui_client/menus/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/components/webui_client/menus/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/backup_manager/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | BACKUP_ROOT_DIR = Path.home().joinpath("kiauh-backups")
13 | 


--------------------------------------------------------------------------------
/kiauh/core/constants.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | import os
11 | import pwd
12 | from pathlib import Path
13 | 
14 | from core.backup_manager import BACKUP_ROOT_DIR
15 | 
16 | # global dependencies
17 | GLOBAL_DEPS = ["git", "wget", "curl", "unzip", "dfu-util", "python3-virtualenv"]
18 | 
19 | # strings
20 | INVALID_CHOICE = "Invalid choice. Please select a valid value."
21 | 
22 | # current user
23 | CURRENT_USER = pwd.getpwuid(os.getuid())[0]
24 | 
25 | # dirs
26 | SYSTEMD = Path("/etc/systemd/system")
27 | PRINTER_DATA_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("printer-data-backups")
28 | NGINX_SITES_AVAILABLE = Path("/etc/nginx/sites-available")
29 | NGINX_SITES_ENABLED = Path("/etc/nginx/sites-enabled")
30 | NGINX_CONFD = Path("/etc/nginx/conf.d")
31 | 


--------------------------------------------------------------------------------
/kiauh/core/decorators.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | import warnings
12 | from typing import Callable
13 | 
14 | 
15 | def deprecated(info: str = "", replaced_by: Callable | None = None) -> Callable:
16 |     def decorator(func) -> Callable:
17 |         def wrapper(*args, **kwargs):
18 |             msg = f"{info}{replaced_by.__name__ if replaced_by else ''}"
19 |             warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
20 |             return func(*args, **kwargs)
21 | 
22 |         return wrapper
23 | 
24 |     return decorator
25 | 


--------------------------------------------------------------------------------
/kiauh/core/instance_manager/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/instance_manager/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/instance_manager/base_instance.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from __future__ import annotations
11 | 
12 | import re
13 | from dataclasses import dataclass, field
14 | from pathlib import Path
15 | from typing import List
16 | 
17 | from utils.fs_utils import get_data_dir
18 | 
19 | SUFFIX_BLACKLIST: List[str] = ["None", "mcu", "obico", "bambu", "companion"]
20 | 
21 | 
22 | @dataclass(repr=True)
23 | class BaseInstance:
24 |     instance_type: type
25 |     suffix: str
26 |     log_file_name: str | None = None
27 |     data_dir: Path = field(init=False)
28 |     base_folders: List[Path] = field(init=False)
29 |     cfg_dir: Path = field(init=False)
30 |     log_dir: Path = field(init=False)
31 |     gcodes_dir: Path = field(init=False)
32 |     comms_dir: Path = field(init=False)
33 |     sysd_dir: Path = field(init=False)
34 |     is_legacy_instance: bool = field(init=False)
35 | 
36 |     def __post_init__(self):
37 |         self.data_dir = get_data_dir(self.instance_type, self.suffix)
38 |         # the following attributes require the data_dir to be set
39 |         self.cfg_dir = self.data_dir.joinpath("config")
40 |         self.log_dir = self.data_dir.joinpath("logs")
41 |         self.gcodes_dir = self.data_dir.joinpath("gcodes")
42 |         self.comms_dir = self.data_dir.joinpath("comms")
43 |         self.sysd_dir = self.data_dir.joinpath("systemd")
44 |         self.is_legacy_instance = self._set_is_legacy_instance()
45 |         self.base_folders = [
46 |             self.data_dir,
47 |             self.cfg_dir,
48 |             self.log_dir,
49 |             self.gcodes_dir,
50 |             self.comms_dir,
51 |             self.sysd_dir,
52 |         ]
53 | 
54 |     def _set_is_legacy_instance(self) -> bool:
55 |         legacy_pattern = r"^(?!printer)(.+)_data"
56 |         match = re.search(legacy_pattern, self.data_dir.name)
57 | 
58 |         return True if (match and self.suffix != "") else False
59 | 


--------------------------------------------------------------------------------
/kiauh/core/menus/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from dataclasses import dataclass
12 | from enum import Enum
13 | from typing import Any, Callable, Type
14 | 
15 | 
16 | @dataclass
17 | class Option:
18 |     """
19 |     Represents a menu option.
20 |     :param method: Method that will be used to call the menu option
21 |     :param opt_index: Can be used to pass the user input to the menu option
22 |     :param opt_data: Can be used to pass any additional data to the menu option
23 |     """
24 | 
25 |     def __repr__(self):
26 |         return f"Option(method={self.method.__name__}, opt_index={self.opt_index}, opt_data={self.opt_data})"
27 | 
28 |     method: Type[Callable]
29 |     opt_index: str = ""
30 |     opt_data: Any = None
31 | 
32 | 
33 | class FooterType(Enum):
34 |     QUIT = "QUIT"
35 |     BACK = "BACK"
36 |     BACK_HELP = "BACK_HELP"
37 |     BLANK = "BLANK"
38 | 


--------------------------------------------------------------------------------
/kiauh/core/menus/repo_select_menu.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from typing import List, Literal, Type
12 | 
13 | from core.logger import Logger
14 | from core.menus import Option
15 | from core.menus.base_menu import BaseMenu
16 | from core.settings.kiauh_settings import KiauhSettings, Repository
17 | from core.types.color import Color
18 | from procedures.switch_repo import run_switch_repo_routine
19 | 
20 | 
21 | class RepoSelectMenu(BaseMenu):
22 |     def __init__(
23 |         self,
24 |         name: Literal["klipper", "moonraker"],
25 |         repos: List[Repository],
26 |         previous_menu: Type[BaseMenu] | None = None,
27 |     ) -> None:
28 |         super().__init__()
29 |         self.title_color = Color.CYAN
30 |         self.previous_menu = previous_menu
31 |         self.settings = KiauhSettings()
32 |         self.input_label_txt = "Select repository"
33 |         self.name = name
34 |         self.repos = repos
35 | 
36 |         if self.name == "klipper":
37 |             self.title = "Klipper Repository Selection Menu"
38 | 
39 |         elif self.name == "moonraker":
40 |             self.title = "Moonraker Repository Selection Menu"
41 | 
42 |     def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
43 |         from core.menus.settings_menu import SettingsMenu
44 | 
45 |         self.previous_menu = (
46 |             previous_menu if previous_menu is not None else SettingsMenu
47 |         )
48 | 
49 |     def set_options(self) -> None:
50 |         self.options = {}
51 | 
52 |         if not self.repos:
53 |             return
54 | 
55 |         for idx, repo in enumerate(self.repos, start=1):
56 |             self.options[str(idx)] = Option(
57 |                 method=self.select_repository, opt_data=repo
58 |             )
59 | 
60 |     def print_menu(self) -> None:
61 |         menu = "╟───────────────────────────────────────────────────────╢\n"
62 |         menu += "║ Available Repositories:                               ║\n"
63 |         menu += "╟───────────────────────────────────────────────────────╢\n"
64 | 
65 |         for idx, repo in enumerate(self.repos, start=1):
66 |             url = f"● Repo: {repo.url.replace('.git', '')}"
67 |             branch = f"└► Branch: {repo.branch}"
68 |             menu += f"║ {idx}) {Color.apply(url, Color.CYAN):<59} ║\n"
69 |             menu += f"║    {Color.apply(branch, Color.CYAN):<59} ║\n"
70 | 
71 |         menu += "╟───────────────────────────────────────────────────────╢\n"
72 |         print(menu, end="")
73 | 
74 |     def select_repository(self, **kwargs) -> None:
75 |         repo: Repository = kwargs.get("opt_data")
76 |         Logger.print_status(
77 |             f"Switching to {self.name.capitalize()}'s new source repository ..."
78 |         )
79 |         run_switch_repo_routine(self.name, repo.url, repo.branch)
80 | 


--------------------------------------------------------------------------------
/kiauh/core/services/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/services/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/services/message_service.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from dataclasses import dataclass, field
12 | from typing import List
13 | 
14 | from core.logger import DialogType, Logger
15 | from core.types.color import Color
16 | 
17 | 
18 | @dataclass()
19 | class Message:
20 |     title: str = field(default="")
21 |     text: List[str] = field(default_factory=list)
22 |     color: Color = field(default=Color.WHITE)
23 |     centered: bool = field(default=False)
24 | 
25 | 
26 | class MessageService:
27 |     __cls_instance = None
28 |     __message: Message | None
29 | 
30 |     def __new__(cls) -> "MessageService":
31 |         if cls.__cls_instance is None:
32 |             cls.__cls_instance = super(MessageService, cls).__new__(cls)
33 |         return cls.__cls_instance
34 | 
35 |     def __init__(self) -> None:
36 |         if not hasattr(self, "__initialized"):
37 |             self.__initialized = False
38 |         if self.__initialized:
39 |             return
40 |         self.__initialized = True
41 |         self.__message = None
42 | 
43 |     def set_message(self, message: Message) -> None:
44 |         self.__message = message
45 | 
46 |     def display_message(self) -> None:
47 |         if self.__message is None:
48 |             return
49 | 
50 |         Logger.print_dialog(
51 |             title=DialogType.CUSTOM,
52 |             content=self.__message.text,
53 |             custom_title=self.__message.title,
54 |             custom_color=self.__message.color,
55 |             center_content=self.__message.centered,
56 |         )
57 | 
58 |         self.__clear_message()
59 | 
60 |     def __clear_message(self) -> None:
61 |         self.__message = None
62 | 


--------------------------------------------------------------------------------
/kiauh/core/settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/settings/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/spinner.py:
--------------------------------------------------------------------------------
 1 | import sys
 2 | import threading
 3 | import time
 4 | from typing import List, Literal
 5 | 
 6 | from core.types.color import Color
 7 | 
 8 | SpinnerColor = Literal["white", "red", "green", "yellow"]
 9 | 
10 | 
11 | class Spinner:
12 |     def __init__(
13 |         self,
14 |         message: str = "Loading",
15 |         interval: float = 0.2,
16 |     ) -> None:
17 |         self.message = f"{message} ..."
18 |         self.interval = interval
19 |         self._stop_event = threading.Event()
20 |         self._thread = threading.Thread(target=self._animate)
21 | 
22 |     def _animate(self) -> None:
23 |         animation: List[str] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
24 |         while not self._stop_event.is_set():
25 |             for char in animation:
26 |                 sys.stdout.write(f"\r{Color.GREEN}{char}{Color.RST} {self.message}")
27 |                 sys.stdout.flush()
28 |                 time.sleep(self.interval)
29 |                 if self._stop_event.is_set():
30 |                     break
31 |         sys.stdout.write("\r" + " " * (len(self.message) + 1) + "\r")
32 |         sys.stdout.flush()
33 | 
34 |     def start(self) -> None:
35 |         self._stop_event.clear()
36 |         if not self._thread.is_alive():
37 |             self._thread = threading.Thread(target=self._animate)
38 |             self._thread.start()
39 | 
40 |     def stop(self) -> None:
41 |         self._stop_event.set()
42 |         self._thread.join()
43 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/.editorconfig:
--------------------------------------------------------------------------------
 1 | # see https://editorconfig.org/
 2 | root = true
 3 | 
 4 | [*]
 5 | end_of_line = lf
 6 | trim_trailing_whitespace = true
 7 | indent_style = space
 8 | insert_final_newline = true
 9 | indent_size = 4
10 | charset = utf-8
11 | 
12 | [*.py]
13 | max_line_length = 88
14 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/.gitignore:
--------------------------------------------------------------------------------
 1 | *.py[cod]
 2 | *.pyc
 3 | __pycache__
 4 | .pytest_cache/
 5 | 
 6 | .idea/
 7 | .vscode/
 8 | 
 9 | .venv*/
10 | venv*/
11 | 
12 | .coverage
13 | htmlcov/
14 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/README.md:
--------------------------------------------------------------------------------
 1 | # Simple Config Parser
 2 | 
 3 | A custom config parser inspired by Python's configparser module.
 4 | Specialized for handling Klipper style config files.
 5 | 
 6 | ---
 7 | 
 8 | ### When parsing a config file, it will be split into the following elements:
 9 | - Header: All lines before the first section
10 | - Section: A section is defined by a line starting with a `[` and ending with a `]`
11 | - Option: A line starting with a word, followed by a `:` or `=` and a value
12 | - Option Block: A line starting with a word, followed by a `:` or `=` and a newline
13 | - Comment: A line starting with a `#` or `;`
14 | - Blank: A line containing only whitespace characters
15 | 
16 | ---
17 | 
18 | ### Internally, the config is stored as a dictionary of sections, each containing a header and a list of elements:
19 | ```python
20 | config = {
21 |     "section_name": {
22 |         "header": "[section_name]\n",
23 |         "elements": [
24 |                 {
25 |                     "type": "comment",
26 |                     "content": "# This is a comment\n"
27 |                 },
28 |                 {
29 |                     "type": "option",
30 |                     "name": "option1",
31 |                     "value": "value1",
32 |                     "raw": "option1: value1\n"
33 |                 },
34 |                 {
35 |                     "type": "blank",
36 |                     "content": "\n"
37 |                 },
38 |                 {
39 |                     "type": "option_block",
40 |                     "name": "option2",
41 |                     "value": [
42 |                         "value2",
43 |                         "value3"
44 |                         ],
45 |                     "raw": "option2:"
46 |                 }
47 |             ]
48 |         }
49 |     }
50 | ```
51 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/pyproject.toml:
--------------------------------------------------------------------------------
 1 | [project]
 2 | name = "simple-config-parser"
 3 | version = "0.0.1"
 4 | description = "A simple config parser for Python"
 5 | authors = [
 6 |     {name = "Dominik Willner", email = "th33xitus@gmail.com"},
 7 | ]
 8 | readme = "README.md"
 9 | license = {text = "GPL-3.0-only"}
10 | requires-python = ">=3.8"
11 | 
12 | [project.urls]
13 | homepage = "https://github.com/dw-0/simple-config-parser"
14 | repository = "https://github.com/dw-0/simple-config-parser"
15 | documentation = "https://github.com/dw-0/simple-config-parser"
16 | 
17 | [project.optional-dependencies]
18 | dev=["ruff"]
19 | 
20 | [tool.ruff]
21 | required-version = ">=0.3.4"
22 | respect-gitignore = true
23 | exclude = [".git",".github", "./docs"]
24 | line-length = 88
25 | indent-width = 4
26 | output-format = "full"
27 | 
28 | [tool.ruff.format]
29 | indent-style = "space"
30 | line-ending = "lf"
31 | quote-style = "double"
32 | 
33 | [tool.ruff.lint]
34 | extend-select = ["I"]
35 | 
36 | [tool.pytest.ini_options]
37 | minversion = "8.2.1"
38 | testpaths = ["tests/**/*.py"]
39 | addopts = "-svvv --cov --cov-config=pyproject.toml --cov-report=html"
40 | 
41 | [tool.coverage.run]
42 | branch = true
43 | source = ["src.simple_config_parser"]
44 | 
45 | [tool.coverage.report]
46 | # Regexes for lines to exclude from consideration
47 | exclude_also = [
48 |     # Don't complain about missing debug-only code:
49 |     "def __repr__",
50 |     "if self\\.debug",
51 | 
52 |     # Don't complain if tests don't hit defensive assertion code:
53 |     "raise AssertionError",
54 |     "raise NotImplementedError",
55 | 
56 |     # Don't complain if non-runnable code isn't run:
57 |     "if 0:",
58 |     "if __name__ == .__main__.:",
59 | 
60 |     # Don't complain about abstract methods, they aren't run:
61 |     "@(abc\\.)?abstractmethod",
62 |     ]
63 | 
64 | [tool.coverage.html]
65 | title = "SimpleConfigParser Coverage Report"
66 | directory = "htmlcov"
67 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | ruff >= 0.3.4
2 | pytest >= 8.2.1
3 | pytest-cov >= 5.0.0
4 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/src/simple_config_parser/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/src/simple_config_parser/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/src/simple_config_parser/constants.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | import re
 9 | from enum import Enum
10 | 
11 | # definition of section line:
12 | #  - then line MUST start with an opening square bracket - it is the first section marker
13 | #  - the section marker MUST be followed by at least one character - it is the section name
14 | #  - the section name MUST be followed by a closing square bracket - it is the second section marker
15 | #  - the second section marker MAY be followed by any amount of whitespace characters
16 | #  - the second section marker MAY be followed by a # or ; - it is the comment marker
17 | #  - the inline comment MAY be of any length and character
18 | SECTION_RE = re.compile(r"^\[(\S.*\S|\S)]\s*([#;].*)?
quot;)
19 | 
20 | # definition of option line:
21 | #  - the line MUST start with a word - it is the option name
22 | #  - the option name MUST be followed by a colon or an equal sign - it is the separator
23 | #  - the separator MUST be followed by a value
24 | #    - the separator MAY have any amount of leading or trailing whitespaces
25 | #    - the separator MUST NOT be directly followed by a colon or equal sign
26 | #  - the value MAY be of any length and character
27 | #    - the value MAY contain any amount of trailing whitespaces
28 | #    - the value MAY be followed by a # or ; - it is the comment marker
29 | #  - the inline comment MAY be of any length and character
30 | OPTION_RE = re.compile(r"^([^;#:=\s]+)\s?[:=]\s*([^;#:=\s][^;#]*?)\s*([#;].*)?
quot;)
31 | # definition of options block start line:
32 | #  - the line MUST start with a word - it is the option name
33 | #  - the option name MUST be followed by a colon or an equal sign - it is the separator
34 | #  - the separator MUST NOT be followed by a value
35 | #    - the separator MAY have any amount of leading or trailing whitespaces
36 | #    - the separator MUST NOT be directly followed by a colon or equal sign
37 | #    - the separator MAY be followed by a # or ; - it is the comment marker
38 | #  - the inline comment MAY be of any length and character
39 | OPTIONS_BLOCK_START_RE = re.compile(r"^([^;#:=\s]+)\s*[:=]\s*([#;].*)?
quot;)
40 | 
41 | # definition of comment line:
42 | #  - the line MAY start with any amount of whitespace characters
43 | #  - the line MUST contain a # or ; - it is the comment marker
44 | #  - the comment marker MAY be followed by any amount of whitespace characters
45 | #  - the comment MAY be of any length and character
46 | LINE_COMMENT_RE = re.compile(r"^\s*[#;].*")
47 | 
48 | # definition of empty line:
49 | #  - the line MUST contain only whitespace characters
50 | EMPTY_LINE_RE = re.compile(r"^\s*
quot;)
51 | 
52 | BOOLEAN_STATES = {
53 |     "1": True,
54 |     "yes": True,
55 |     "true": True,
56 |     "on": True,
57 |     "0": False,
58 |     "no": False,
59 |     "false": False,
60 |     "off": False,
61 | }
62 | 
63 | HEADER_IDENT = "#_header"
64 | 
65 | INDENT = " " * 4
66 | 
67 | class LineType(Enum):
68 |     OPTION = "option"
69 |     OPTION_BLOCK = "option_block"
70 |     COMMENT = "comment"
71 |     BLANK = "blank"
72 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/test_config_1.cfg:
--------------------------------------------------------------------------------
 1 | # a comment at the very top
 2 | # should be treated as the file header
 3 | 
 4 | # up to the first section, including all blank lines
 5 | 
 6 | [section_1]
 7 | option_1: value_1
 8 | option_1_1: True # this is a boolean
 9 | option_1_2: 5 ; this is an integer
10 | option_1_3: 1.123 #;this is a float
11 | 
12 | [section_2] ; comment
13 | option_2: value_2
14 | 
15 | ; comment
16 | 
17 | [section_3]
18 | option_3: value_3 # comment
19 | 
20 | [section_4]
21 | # comment
22 | option_4: value_4
23 | 
24 | [section number 5]
25 | #option_5: value_5
26 | option_5 = this.is.value-5
27 | multi_option:
28 |     # these are multi-line values
29 |     value_5_1
30 |     value_5_2 ; here is a comment
31 |     value_5_3
32 | option_5_1: value_5_1
33 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/test_config_2.cfg:
--------------------------------------------------------------------------------
 1 | # a comment at the very top
 2 | # should be treated as the file header
 3 | 
 4 | # up to the first section, including all blank lines
 5 | 
 6 | [section_1]
 7 | option_1: value_1
 8 | option_1_1: True # this is a boolean
 9 | option_1_2: 5 ; this is an integer
10 | option_1_3: 1.123 #;this is a float
11 | 
12 | [section_2] ; comment
13 | option_2: value_2
14 | 
15 | ; comment
16 | 
17 | [section_3]
18 | option_3: value_3 # comment
19 | 
20 | [section_4]
21 | # comment
22 | option_4: value_4
23 | 
24 | [section number 5]
25 | #option_5: value_5
26 | option_5 = this.is.value-5
27 | multi_option:
28 |     # these are multi-line values
29 |     value_5_1
30 |     value_5_2 ; here is a comment
31 |     value_5_3
32 | option_5_1: value_5_1
33 | # config ending with a comment
34 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/test_config_3.cfg:
--------------------------------------------------------------------------------
 1 | # a comment at the very top
 2 | # should be treated as the file header
 3 | 
 4 | # up to the first section, including all blank lines
 5 | 
 6 | [section_1]
 7 | option_1: value_1
 8 | option_1_1: True # this is a boolean
 9 | option_1_2: 5 ; this is an integer
10 | option_1_3: 1.123 #;this is a float
11 | 
12 | [section_2] ; comment
13 | option_2: value_2
14 | 
15 | ; comment
16 | 
17 | [section_3]
18 | option_3: value_3 # comment
19 | 
20 | [section_4]
21 | # comment
22 | option_4: value_4
23 | 
24 | [section number 5]
25 | #option_5: value_5
26 | option_5 = this.is.value-5
27 | multi_option:
28 |     # these are multi-line values
29 |     value_5_1
30 |     value_5_2 ; here is a comment
31 |     value_5_3
32 | option_5_1: value_5_1
33 | 
34 | [gcode_macro M117]
35 | rename_existing: M117.1
36 | gcode:
37 |     {% if rawparams %}
38 |        {% set escaped_msg = rawparams.split(';', 1)[0].split('\x23', 1)[0]|replace('"', '\\"') %}
39 |        SET_DISPLAY_TEXT MSG="{escaped_msg}"
40 |        RESPOND TYPE=command MSG="{escaped_msg}"
41 |     {% else %}
42 |         SET_DISPLAY_TEXT
43 |     {% endif %}
44 | 
45 | # SDCard 'looping' (aka Marlin M808 commands) support
46 | #
47 | # Support SDCard looping
48 | [sdcard_loop]
49 | [gcode_macro M486]
50 | gcode:
51 |     # Parameters known to M486 are as follows:
52 |     #   [C<flag>] Cancel the current object
53 |     #   [P<index>] Cancel the object with the given index
54 |     #   [S<index>] Set the index of the current object.
55 |     #       If the object with the given index has been canceled, this will cause
56 |     #       the firmware to skip to the next object. The value -1 is used to
57 |     #       indicate something that isn’t an object and shouldn’t be skipped.
58 |     #   [T<count>] Reset the state and set the number of objects
59 |     #   [U<index>] Un-cancel the object with the given index. This command will be
60 |     #       ignored if the object has already been skipped
61 | 
62 |     {% if 'exclude_object' not in printer %}
63 |         {action_raise_error("[exclude_object] is not enabled")}
64 |     {% endif %}
65 | 
66 |     {% if 'T' in params %}
67 |         EXCLUDE_OBJECT RESET=1
68 | 
69 |       {% for i in range(params.T | int) %}
70 |         EXCLUDE_OBJECT_DEFINE NAME={i}
71 |       {% endfor %}
72 |     {% endif %}
73 | 
74 |     {% if 'C' in params %}
75 |       EXCLUDE_OBJECT CURRENT=1
76 |     {% endif %}
77 | 
78 |     {% if 'P' in params %}
79 |       EXCLUDE_OBJECT NAME={params.P}
80 |     {% endif %}
81 | 
82 |     {% if 'S' in params %}
83 |         {% if params.S == '-1' %}
84 |             {% if printer.exclude_object.current_object %}
85 |                 EXCLUDE_OBJECT_END NAME={printer.exclude_object.current_object}
86 |             {% endif %}
87 |         {% else %}
88 |             EXCLUDE_OBJECT_START NAME={params.S}
89 |         {% endif %}
90 |     {% endif %}
91 | 
92 |     {% if 'U' in params %}
93 |         EXCLUDE_OBJECT RESET=1 NAME={params.U}
94 |     {% endif %}
95 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/write_tests/add_option/expected.cfg:
--------------------------------------------------------------------------------
1 | [section_1]
2 | # comment
3 | option_1: value_1
4 | option_2: value_2 ; comment
5 | new_option: new_value
6 | 
7 | [section_2]
8 | option_3: value_3
9 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/write_tests/add_option/input.cfg:
--------------------------------------------------------------------------------
1 | [section_1]
2 | # comment
3 | option_1: value_1
4 | option_2: value_2 ; comment
5 | 
6 | [section_2]
7 | option_3: value_3
8 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/write_tests/remove_option/expected.cfg:
--------------------------------------------------------------------------------
1 | [section_1]
2 | # comment
3 | option_1: value_1
4 | option_2: value_2 ; comment
5 | 
6 | [section_2]
7 | option_3: value_3
8 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/write_tests/remove_option/input.cfg:
--------------------------------------------------------------------------------
1 | [section_1]
2 | # comment
3 | option_1: value_1
4 | option_to_remove: value_to_remove
5 | option_2: value_2 ; comment
6 | 
7 | [section_2]
8 | option_3: value_3
9 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/write_tests/remove_section/expected.cfg:
--------------------------------------------------------------------------------
1 | [section_1]
2 | option_1: value_1
3 | option_2: value_2
4 | 
5 | # comment
6 | [section_2]
7 | option_5: value_5
8 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/assets/write_tests/remove_section/input.cfg:
--------------------------------------------------------------------------------
 1 | [section_1]
 2 | option_1: value_1
 3 | option_2: value_2
 4 | 
 5 | # comment
 6 | [section_to_remove]
 7 | option_3: value_3
 8 | option_4: value_4
 9 | 
10 | [section_2]
11 | option_5: value_5
12 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_matching/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_empty_line/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_empty_line/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_empty_line/test_data/matching_data.txt:
--------------------------------------------------------------------------------
1 | 
2 | 
3 | 
4 | 
5 | 
6 | 
7 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_empty_line/test_data/non_matching_data.txt:
--------------------------------------------------------------------------------
1 | not_empty
2 | [also_not_empty]
3 | #
4 | ;
5 |  ;
6 |   #
7 | option: value
8 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_empty_line/test_match_empty_line.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | 
 9 | from pathlib import Path
10 | 
11 | import pytest
12 | 
13 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
14 | from tests.utils import load_testdata_from_file
15 | 
16 | BASE_DIR = Path(__file__).parent.joinpath("test_data")
17 | MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
18 | NON_MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("non_matching_data.txt")
19 | 
20 | 
21 | @pytest.fixture
22 | def parser():
23 |     return SimpleConfigParser()
24 | 
25 | 
26 | @pytest.mark.parametrize("line", load_testdata_from_file(MATCHING_TEST_DATA_PATH))
27 | def test_match_line_comment(parser, line):
28 |     """Test that a line matches the definition of a line comment"""
29 |     assert (
30 |         parser._match_empty_line(line) is True
31 |     ), f"Expected line '{line}' to match line comment definition!"
32 | 
33 | 
34 | @pytest.mark.parametrize("line", load_testdata_from_file(NON_MATCHING_TEST_DATA_PATH))
35 | def test_non_matching_line_comment(parser, line):
36 |     """Test that a line does not match the definition of a line comment"""
37 |     assert (
38 |         parser._match_empty_line(line) is False
39 |     ), f"Expected line '{line}' to not match line comment definition!"
40 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_line_comment/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_line_comment/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_line_comment/test_data/matching_data.txt:
--------------------------------------------------------------------------------
 1 | ;[example_section]
 2 | #[example_section]
 3 | # [example_section]
 4 | ; [example_section]
 5 | ;[gcode_macro CANCEL_PRINT]
 6 | #[gcode_macro CANCEL_PRINT]
 7 | # [gcode_macro CANCEL_PRINT]
 8 | ; [gcode_macro CANCEL_PRINT]
 9 | ;[gcode_macro SET_PAUSE_NEXT_LAYER]
10 | #[gcode_macro SET_PAUSE_NEXT_LAYER]
11 | # [gcode_macro SET_PAUSE_NEXT_LAYER]
12 | ; [gcode_macro SET_PAUSE_NEXT_LAYER]
13 | ;[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
14 | #[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
15 | # [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
16 | ; [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
17 |  ;[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
18 |  #[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
19 |  # [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
20 |  ; [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
21 |   ;[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
22 |   #[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
23 |   # [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
24 |   ; [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
25 |    ;[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
26 |    #[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
27 |    # [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
28 |    ; [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
29 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_line_comment/test_data/non_matching_data.txt:
--------------------------------------------------------------------------------
1 | not_a_comment: nono
2 | 
3 | [also not a comment]
4 | not_a_comment: ; comment
5 | not_a_comment: # comment
6 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_line_comment/test_match_line_comment.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | 
 9 | from pathlib import Path
10 | 
11 | import pytest
12 | 
13 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
14 | from tests.utils import load_testdata_from_file
15 | 
16 | BASE_DIR = Path(__file__).parent.joinpath("test_data")
17 | MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
18 | NON_MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("non_matching_data.txt")
19 | 
20 | 
21 | @pytest.fixture
22 | def parser():
23 |     return SimpleConfigParser()
24 | 
25 | 
26 | @pytest.mark.parametrize("line", load_testdata_from_file(MATCHING_TEST_DATA_PATH))
27 | def test_match_line_comment(parser, line):
28 |     """Test that a line matches the definition of a line comment"""
29 |     assert (
30 |         parser._match_line_comment(line) is True
31 |     ), f"Expected line '{line}' to match line comment definition!"
32 | 
33 | 
34 | @pytest.mark.parametrize("line", load_testdata_from_file(NON_MATCHING_TEST_DATA_PATH))
35 | def test_non_matching_line_comment(parser, line):
36 |     """Test that a line does not match the definition of a line comment"""
37 |     assert (
38 |         parser._match_line_comment(line) is False
39 |     ), f"Expected line '{line}' to not match line comment definition!"
40 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option/test_data/non_matching_data.txt:
--------------------------------------------------------------------------------
 1 | [section]
 2 | [section with spaces]
 3 | [section with spaces and comments] ; comment 1
 4 | [section with spaces and comments] # comment 2
 5 |  indented_option: value
 6 | option_with_no_value:
 7 | another_option_with_no_value:
 8 |  indented_option_with_no_value:
 9 | # position_min: 0
10 | # homing_speed: 5.0
11 | 
12 | ### this is a comment
13 | ; this is also a comment
14 | # [section]
15 | # [section with spaces]
16 | # [section with spaces and comments] ; comment 1
17 | ;[section]
18 | ;[section with spaces]
19 | ;[section with spaces and comments] ; comment 1
20 | # commented_option: value
21 | #commented_option: value
22 | ;commented_option: value
23 | ; commented_option: value
24 | #
25 | ;
26 | option_1 :: value
27 | option_1:: value
28 | option_1 ::value
29 | option_2 == value
30 | option_2== value
31 | option_2 ==value
32 | option_1 := value
33 | option_1:= value
34 | option_1 :=value
35 | option_2 := value
36 | option_2:= value
37 | option_2 :=value
38 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option/test_match_option.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | 
 9 | from pathlib import Path
10 | 
11 | import pytest
12 | 
13 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
14 | from tests.utils import load_testdata_from_file
15 | 
16 | BASE_DIR = Path(__file__).parent.joinpath("test_data")
17 | MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
18 | NON_MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("non_matching_data.txt")
19 | 
20 | 
21 | @pytest.fixture
22 | def parser():
23 |     return SimpleConfigParser()
24 | 
25 | 
26 | @pytest.mark.parametrize("line", load_testdata_from_file(MATCHING_TEST_DATA_PATH))
27 | def test_match_option(parser, line):
28 |     """Test that a line matches the definition of an option"""
29 |     assert (
30 |         parser._match_option(line) is True
31 |     ), f"Expected line '{line}' to match option definition!"
32 | 
33 | 
34 | @pytest.mark.parametrize("line", load_testdata_from_file(NON_MATCHING_TEST_DATA_PATH))
35 | def test_non_matching_option(parser, line):
36 |     """Test that a line does not match the definition of an option"""
37 |     assert (
38 |         parser._match_option(line) is False
39 |     ), f"Expected line '{line}' to not match option definition!"
40 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option_block_start/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option_block_start/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option_block_start/test_data/matching_data.txt:
--------------------------------------------------------------------------------
 1 | trusted_clients:
 2 | gcode:
 3 | cors_domains:
 4 | an_options_block_start_with_comment: ; this is a comment
 5 | an_options_block_start_with_comment: # this is a comment
 6 | options_block_start_with_comment:;this is a comment
 7 | options_block_start_with_comment :;this is a comment
 8 | options_block_start_with_comment:#this is a comment
 9 | options_block_start_with_comment :#this is a comment
10 | parameter_temperature_(°C):
11 | parameter_temperature_(°C)=
12 | parameter_humidity_(%_RH):
13 | parameter_humidity_(%_RH) :
14 | parameter_spool_weight_(%):
15 | parameter_spool_weight_(%) =
16 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option_block_start/test_data/non_matching_data.txt:
--------------------------------------------------------------------------------
 1 | type: jsonfile
 2 | path: /dev/shm/drying_box.json
 3 | baud: 250000
 4 | minimum_cruise_ratio: 0.5
 5 | square_corner_velocity: 5.0
 6 | full_steps_per_rotation: 200
 7 | position_min: 0
 8 | homing_speed: 5.0
 9 | # baud: 250000
10 | # minimum_cruise_ratio: 0.5
11 | # square_corner_velocity: 5.0
12 | # full_steps_per_rotation: 200
13 | # position_min: 0
14 | # homing_speed: 5.0
15 | 
16 | ### this is a comment
17 | ; this is also a comment
18 | ;
19 | #
20 | homing_speed::
21 | homing_speed::
22 | homing_speed ::
23 | homing_speed ::
24 | homing_speed==
25 | homing_speed==
26 | homing_speed ==
27 | homing_speed ==
28 | homing_speed :=
29 | homing_speed :=
30 | homing_speed =:
31 | homing_speed =:
32 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_option_block_start/test_match_options_block_start.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | 
 9 | from pathlib import Path
10 | 
11 | import pytest
12 | 
13 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
14 | from tests.utils import load_testdata_from_file
15 | 
16 | BASE_DIR = Path(__file__).parent.joinpath("test_data")
17 | MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
18 | NON_MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("non_matching_data.txt")
19 | 
20 | 
21 | @pytest.fixture
22 | def parser():
23 |     return SimpleConfigParser()
24 | 
25 | 
26 | @pytest.mark.parametrize("line", load_testdata_from_file(MATCHING_TEST_DATA_PATH))
27 | def test_match_options_block_start(parser, line):
28 |     """Test that a line matches the definition of an options block start"""
29 |     assert (
30 |         parser._match_options_block_start(line) is True
31 |     ), f"Expected line '{line}' to match options block start definition!"
32 | 
33 | 
34 | @pytest.mark.parametrize("line", load_testdata_from_file(NON_MATCHING_TEST_DATA_PATH))
35 | def test_non_matching_options_block_start(parser, line):
36 |     """Test that a line does not match the definition of an options block start"""
37 |     assert (
38 |         parser._match_options_block_start(line) is False
39 |     ), f"Expected line '{line}' to not match options block start definition!"
40 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_section/__init__,py.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_section/__init__,py.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_section/test_data/matching_data.txt:
--------------------------------------------------------------------------------
  1 | [example_section]
  2 | [gcode_macro CANCEL_PRINT]
  3 | [gcode_macro SET_PAUSE_NEXT_LAYER]
  4 | [gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
  5 | [update_manager moonraker-obico]
  6 | [include moonraker_obico_macros.cfg]
  7 | [include moonraker-obico-update.cfg]
  8 | [example_section two]
  9 | [valid_content]
 10 | [valid content]
 11 | [content123]
 12 | [a]
 13 | [valid_content] # comment
 14 | [something];comment
 15 | [mcu]
 16 | [printer]
 17 | [printer]
 18 | [stepper_x]
 19 | [stepper_y]
 20 | [stepper_z]
 21 | [printer]
 22 | [stepper_a]
 23 | [stepper_b]
 24 | [stepper_c]
 25 | [delta_calibrate]
 26 | [printer]
 27 | [stepper_left]
 28 | [stepper_right]
 29 | [stepper_bed]
 30 | [stepper_arm]
 31 | [delta_calibrate]
 32 | [extruder]
 33 | [heater_bed]
 34 | [bed_mesh]
 35 | [bed_tilt]
 36 | [bed_screws]
 37 | [screws_tilt_adjust]
 38 | [z_tilt]
 39 | [quad_gantry_level]
 40 | [skew_correction]
 41 | [z_thermal_adjust]
 42 | [safe_z_home]
 43 | [homing_override]
 44 | [endstop_phase stepper_z]
 45 | [gcode_macro my_cmd]
 46 | [delayed_gcode my_delayed_gcode]
 47 | [save_variables]
 48 | [idle_timeout]
 49 | [virtual_sdcard]
 50 | [sdcard_loop]
 51 | [force_move]
 52 | [pause_resume]
 53 | [firmware_retraction]
 54 | [gcode_arcs]
 55 | [respond]
 56 | [exclude_object]
 57 | [input_shaper]
 58 | [adxl345]
 59 | [lis2dw]
 60 | [mpu9250 my_accelerometer]
 61 | [resonance_tester]
 62 | [board_pins my_aliases]
 63 | [duplicate_pin_override]
 64 | [probe]
 65 | [bltouch]
 66 | [smart_effector]
 67 | [probe_eddy_current my_eddy_probe]
 68 | [axis_twist_compensation]
 69 | [stepper_z1]
 70 | [extruder1]
 71 | [dual_carriage]
 72 | [extruder_stepper my_extra_stepper]
 73 | [manual_stepper my_stepper]
 74 | [verify_heater heater_config_name]
 75 | [homing_heaters]
 76 | [thermistor my_thermistor]
 77 | [adc_temperature my_sensor]
 78 | [heater_generic my_generic_heater]
 79 | [temperature_sensor my_sensor]
 80 | [temperature_probe my_probe]
 81 | [fan]
 82 | [heater_fan heatbreak_cooling_fan]
 83 | [controller_fan my_controller_fan]
 84 | [temperature_fan my_temp_fan]
 85 | [fan_generic extruder_partfan]
 86 | [led my_led]
 87 | [neopixel my_neopixel]
 88 | [dotstar my_dotstar]
 89 | [pca9533 my_pca9533]
 90 | [pca9632 my_pca9632]
 91 | [servo my_servo]
 92 | [gcode_button my_gcode_button]
 93 | [output_pin my_pin]
 94 | [pwm_tool my_tool]
 95 | [pwm_cycle_time my_pin]
 96 | [static_digital_output my_output_pins]
 97 | [multi_pin my_multi_pin]
 98 | [tmc2130 stepper_x]
 99 | [tmc2208 stepper_x]
100 | [tmc2209 stepper_x]
101 | [tmc2660 stepper_x]
102 | [tmc2240 stepper_x]
103 | [tmc5160 stepper_x]
104 | [ad5206 my_digipot]
105 | [mcp4451 my_digipot]
106 | [mcp4728 my_dac]
107 | [mcp4018 my_digipot]
108 | [display]
109 | [display_data my_group_name my_data_name]
110 | [display_template my_template_name]
111 | [display_glyph my_display_glyph]
112 | [menu __some_list __some_name]
113 | [menu some_name]
114 | [menu some_list]
115 | [menu some_list some_command]
116 | [menu some_list some_input]
117 | [filament_switch_sensor my_sensor]
118 | [filament_motion_sensor my_sensor]
119 | [tsl1401cl_filament_width_sensor]
120 | [hall_filament_width_sensor]
121 | [load_cell]
122 | [sx1509 my_sx1509]
123 | [samd_sercom my_sercom]
124 | [adc_scaled my_name]
125 | [replicape]
126 | [palette2]
127 | [angle my_angle_sensor]
128 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_section/test_data/non_matching_data.txt:
--------------------------------------------------------------------------------
 1 | section: invalid
 2 | not_a_valid_section
 3 | [missing_square_bracket
 4 | missing_square_bracket]
 5 | []
 6 | [ ]
 7 |   [indented_section]
 8 |            [indented_section]  # comment
 9 |        [indented_section]  ; comment
10 | ;[commented_section]
11 | #[another_commented_section]
12 | ; [commented_section]
13 | # [another_commented_section]
14 | this_is_an_option: 123
15 |   this_is_an_indented_option: 123
16 | this_is_an_option_block_start:
17 | 
18 | #
19 | ;
20 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_matching/match_section/test_match_section.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | 
 9 | from pathlib import Path
10 | 
11 | import pytest
12 | 
13 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
14 | from tests.utils import load_testdata_from_file
15 | 
16 | BASE_DIR = Path(__file__).parent.joinpath("test_data")
17 | MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("matching_data.txt")
18 | NON_MATCHING_TEST_DATA_PATH = BASE_DIR.joinpath("non_matching_data.txt")
19 | 
20 | 
21 | @pytest.fixture
22 | def parser():
23 |     return SimpleConfigParser()
24 | 
25 | 
26 | @pytest.mark.parametrize("line", load_testdata_from_file(MATCHING_TEST_DATA_PATH))
27 | def test_match_section(parser, line):
28 |     """Test that a line matches the definition of a section"""
29 |     assert (
30 |         parser._match_section(line) is True
31 |     ), f"Expected line '{line}' to match section definition!"
32 | 
33 | 
34 | @pytest.mark.parametrize("line", load_testdata_from_file(NON_MATCHING_TEST_DATA_PATH))
35 | def test_non_matching_section(parser, line):
36 |     """Test that a line does not match the definition of a section"""
37 |     assert (
38 |         parser._match_section(line) is False
39 |     ), f"Expected line '{line}' to not match section definition!"
40 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_parsing/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/line_parsing/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/line_parsing/test_line_parsing.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | import json
 9 | from pathlib import Path
10 | 
11 | import pytest
12 | 
13 | from src.simple_config_parser.constants import HEADER_IDENT, LineType
14 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
15 | from tests.utils import load_testdata_from_file
16 | 
17 | BASE_DIR = Path(__file__).parent.parent.joinpath("assets")
18 | TEST_DATA_PATH = BASE_DIR.joinpath("test_config_1.cfg")
19 | 
20 | 
21 | @pytest.fixture
22 | def parser():
23 |     parser = SimpleConfigParser()
24 |     for line in load_testdata_from_file(TEST_DATA_PATH):
25 |         parser._parse_line(line)  # noqa
26 | 
27 |     return parser
28 | 
29 | 
30 | def test_section_parsing(parser):
31 |     expected_keys = {"section_1", "section_2", "section_3", "section_4"}
32 |     assert expected_keys.issubset(
33 |         parser.config.keys()
34 |     ), f"Expected keys: {expected_keys}, got: {parser.config.keys()}"
35 |     assert parser.in_option_block is False
36 |     assert parser.current_section == parser.get_sections()[-1]
37 |     assert parser.config["section_2"] is not None
38 |     assert parser.config["section_2"]["header"] == "[section_2] ; comment"
39 |     assert parser.config["section_2"]["elements"] is not None
40 |     assert len(parser.config["section_2"]["elements"]) > 0
41 | 
42 | 
43 | def test_option_parsing(parser):
44 |     assert parser.config["section_1"]["elements"][0]["type"] == LineType.OPTION.value
45 |     assert parser.config["section_1"]["elements"][0]["name"] == "option_1"
46 |     assert parser.config["section_1"]["elements"][0]["value"] == "value_1"
47 |     assert parser.config["section_1"]["elements"][0]["raw"] == "option_1: value_1"
48 | 
49 | 
50 | def test_header_parsing(parser):
51 |     header = parser.config[HEADER_IDENT]
52 |     assert isinstance(header, list)
53 |     assert len(header) > 0
54 | 
55 | 
56 | def test_option_block_parsing(parser):
57 |     section = "section number 5"
58 |     option_block = None
59 |     for element in parser.config[section]["elements"]:
60 |         if (element["type"] == LineType.OPTION_BLOCK.value and
61 |             element["name"] == "multi_option"):
62 |             option_block = element
63 |             break
64 | 
65 |     assert option_block is not None, "multi_option block not found"
66 |     assert option_block["type"] == LineType.OPTION_BLOCK.value
67 |     assert option_block["name"] == "multi_option"
68 |     assert option_block["raw"] == "multi_option:"
69 | 
70 |     expected_values = [
71 |         "# these are multi-line values",
72 |         "value_5_1",
73 |         "value_5_2 ; here is a comment",
74 |         "value_5_3"
75 |     ]
76 |     assert option_block["value"] == expected_values, (
77 |         f"Expected values: {expected_values}, "
78 |         f"got: {option_block['value']}"
79 |     )
80 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/public_api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/public_api/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/public_api/conftest.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | from pathlib import Path
 9 | 
10 | import pytest
11 | 
12 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
13 | from tests.utils import load_testdata_from_file
14 | 
15 | BASE_DIR = Path(__file__).parent.parent.joinpath("assets")
16 | CONFIG_FILES = ["test_config_1.cfg", "test_config_2.cfg", "test_config_3.cfg"]
17 | 
18 | 
19 | @pytest.fixture(params=CONFIG_FILES)
20 | def parser(request):
21 |     parser = SimpleConfigParser()
22 |     file_path = BASE_DIR.joinpath(request.param)
23 |     for line in load_testdata_from_file(file_path):
24 |         parser._parse_line(line)  # noqa
25 | 
26 |     return parser
27 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/public_api/test_read_file.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | from pathlib import Path
 9 | 
10 | from src.simple_config_parser.simple_config_parser import (
11 |     SimpleConfigParser,
12 | )
13 | 
14 | BASE_DIR = Path(__file__).parent.parent.joinpath("assets")
15 | TEST_DATA_PATH = BASE_DIR.joinpath("test_config_1.cfg")
16 | 
17 | 
18 | def test_read_file():
19 |     parser = SimpleConfigParser()
20 |     parser.read_file(TEST_DATA_PATH)
21 |     assert parser.config is not None
22 |     assert parser.config.keys() is not None
23 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/public_api/test_sections_api.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | 
 9 | import pytest
10 | 
11 | from src.simple_config_parser.simple_config_parser import (
12 |     DuplicateSectionError,
13 | )
14 | 
15 | 
16 | def test_get_sections(parser):
17 |     expected_keys = {
18 |         "section_1",
19 |         "section_2",
20 |         "section_3",
21 |         "section_4",
22 |         "section number 5",
23 |     }
24 |     assert expected_keys.issubset(
25 |         parser.get_sections()
26 |     ), f"Expected keys: {expected_keys}, got: {parser.get_sections()}"
27 | 
28 | 
29 | def test_has_section(parser):
30 |     assert parser.has_section("section_1") is True
31 |     assert parser.has_section("not_available") is False
32 | 
33 | 
34 | def test_add_section(parser):
35 |     pre_add_count = len(parser.get_sections())
36 |     parser.add_section("new_section")
37 |     parser.add_section("new_section2")
38 |     assert parser.has_section("new_section") is True
39 |     assert parser.has_section("new_section2") is True
40 |     assert len(parser.get_sections()) == pre_add_count + 2
41 | 
42 |     new_section = parser.config["new_section"]
43 |     assert isinstance(new_section, dict)
44 |     assert new_section["header"] == "[new_section]\n"
45 |     assert new_section["elements"] is not None
46 |     assert new_section["elements"] == []
47 | 
48 |     new_section2 = parser.config["new_section2"]
49 |     assert isinstance(new_section2, dict)
50 |     assert new_section2["header"] == "[new_section2]\n"
51 |     assert new_section2["elements"] is not None
52 |     assert new_section2["elements"] == []
53 | 
54 | 
55 | def test_add_section_duplicate(parser):
56 |     with pytest.raises(DuplicateSectionError):
57 |         parser.add_section("section_1")
58 | 
59 | 
60 | def test_remove_section(parser):
61 |     pre_remove_count = len(parser.get_sections())
62 |     parser.remove_section("section_1")
63 |     assert parser.has_section("section_1") is False
64 |     assert len(parser.get_sections()) == pre_remove_count - 1
65 |     assert "section_1" not in parser.config
66 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/utils.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | from pathlib import Path
 9 | 
10 | 
11 | def load_testdata_from_file(file_path: Path):
12 |     """Helper function to load test data from a text file"""
13 | 
14 |     with open(file_path, "r") as f:
15 |         return [line.replace("\n", "") for line in f]
16 | 


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/value_conversion/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/submodules/simple_config_parser/tests/value_conversion/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/submodules/simple_config_parser/tests/value_conversion/test_get_conv.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2024 Dominik Willner <th33xitus@gmail.com>               #
 3 | #                                                                         #
 4 | #  https://github.com/dw-0/simple-config-parser                           #
 5 | #                                                                         #
 6 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 7 | # ======================================================================= #
 8 | from pathlib import Path
 9 | 
10 | import pytest
11 | 
12 | from src.simple_config_parser.simple_config_parser import SimpleConfigParser
13 | from tests.utils import load_testdata_from_file
14 | 
15 | BASE_DIR = Path(__file__).parent.parent.joinpath("assets")
16 | TEST_DATA_PATH = BASE_DIR.joinpath("test_config_1.cfg")
17 | 
18 | 
19 | @pytest.fixture
20 | def parser():
21 |     parser = SimpleConfigParser()
22 |     for line in load_testdata_from_file(TEST_DATA_PATH):
23 |         parser._parse_line(line)  # noqa
24 | 
25 |     return parser
26 | 
27 | 
28 | def test_get_int_conv(parser):
29 |     should_be_int = parser._get_conv("section_1", "option_1_2", int)
30 |     assert isinstance(should_be_int, int)
31 | 
32 | 
33 | def test_get_float_conv(parser):
34 |     should_be_float = parser._get_conv("section_1", "option_1_3", float)
35 |     assert isinstance(should_be_float, float)
36 | 
37 | 
38 | def test_get_bool_conv(parser):
39 |     should_be_bool = parser._get_conv(
40 |         "section_1", "option_1_1", parser._convert_to_boolean
41 |     )
42 |     assert isinstance(should_be_bool, bool)
43 | 
44 | 
45 | def test_get_int_conv_fallback(parser):
46 |     should_be_fallback_int = parser._get_conv(
47 |         "section_1", "option_128", int, fallback=128
48 |     )
49 |     assert isinstance(should_be_fallback_int, int)
50 |     assert should_be_fallback_int == 128
51 |     assert parser._get_conv("section_1", "option_128", int, None) is None
52 | 
53 | 
54 | def test_get_float_conv_fallback(parser):
55 |     should_be_fallback_float = parser._get_conv(
56 |         "section_1", "option_128", float, fallback=1.234
57 |     )
58 |     assert isinstance(should_be_fallback_float, float)
59 |     assert should_be_fallback_float == 1.234
60 | 
61 |     assert parser._get_conv("section_1", "option_128", float, None) is None
62 | 
63 | 
64 | def test_get_bool_conv_fallback(parser):
65 |     should_be_fallback_bool = parser._get_conv(
66 |         "section_1", "option_128", parser._convert_to_boolean, fallback=True
67 |     )
68 |     assert isinstance(should_be_fallback_bool, bool)
69 |     assert should_be_fallback_bool is True
70 | 
71 |     assert (
72 |         parser._get_conv("section_1", "option_128", parser._convert_to_boolean, None)
73 |         is None
74 |     )
75 | 
76 | 
77 | def test_get_int_conv_exception(parser):
78 |     with pytest.raises(ValueError):
79 |         parser._get_conv("section_1", "option_1", int)
80 | 
81 | 
82 | def test_get_float_conv_exception(parser):
83 |     with pytest.raises(ValueError):
84 |         parser._get_conv("section_1", "option_1", float)
85 | 
86 | 
87 | def test_get_bool_conv_exception(parser):
88 |     with pytest.raises(ValueError):
89 |         parser._get_conv("section_1", "option_1", parser._convert_to_boolean)
90 | 


--------------------------------------------------------------------------------
/kiauh/core/types/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/core/types/__init__.py


--------------------------------------------------------------------------------
/kiauh/core/types/color.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from enum import Enum
12 | 
13 | 
14 | class Color(Enum):
15 |     WHITE = "\033[37m"  # white
16 |     MAGENTA = "\033[35m"  # magenta
17 |     GREEN = "\033[92m"  # bright green
18 |     YELLOW = "\033[93m"  # bright yellow
19 |     RED = "\033[91m"  # bright red
20 |     CYAN = "\033[96m"  # bright cyan
21 |     RST = "\033[0m"  # reset format
22 | 
23 |     def __str__(self):
24 |         return self.value
25 | 
26 |     @staticmethod
27 |     def apply(text: str | int, color: "Color") -> str:
28 |         """Apply a given color to a given text string."""
29 |         return f"{color}{text}{Color.RST}"
30 | 


--------------------------------------------------------------------------------
/kiauh/core/types/component_status.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from dataclasses import dataclass
12 | from typing import Dict, Literal
13 | 
14 | StatusText = Literal["Installed", "Not installed", "Incomplete"]
15 | StatusCode = Literal[0, 1, 2]
16 | StatusMap: Dict[StatusCode, StatusText] = {
17 |     0: "Not installed",
18 |     1: "Incomplete",
19 |     2: "Installed",
20 | }
21 | 
22 | 
23 | @dataclass
24 | class ComponentStatus:
25 |     status: StatusCode
26 |     owner: str | None = None
27 |     repo: str | None = None
28 |     repo_url: str | None = None
29 |     branch: str = ""
30 |     local: str | None = None
31 |     remote: str | None = None
32 |     instances: int | None = None
33 | 


--------------------------------------------------------------------------------
/kiauh/extensions/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | EXTENSION_ROOT = Path(__file__).resolve().parents[1].joinpath("extensions")
13 | 


--------------------------------------------------------------------------------
/kiauh/extensions/base_extension.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from abc import ABC, abstractmethod
11 | from typing import Dict
12 | 
13 | 
14 | # noinspection PyUnusedLocal
15 | # noinspection PyMethodMayBeStatic
16 | class BaseExtension(ABC):
17 |     def __init__(self, metadata: Dict[str, str]):
18 |         self.metadata = metadata
19 | 
20 |     @abstractmethod
21 |     def install_extension(self, **kwargs) -> None:
22 |         raise NotImplementedError
23 | 
24 |     def update_extension(self, **kwargs) -> None:
25 |         raise NotImplementedError
26 | 
27 |     @abstractmethod
28 |     def remove_extension(self, **kwargs) -> None:
29 |         raise NotImplementedError
30 | 


--------------------------------------------------------------------------------
/kiauh/extensions/gcode_shell_cmd/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | EXT_MODULE_NAME = "gcode_shell_command.py"
13 | MODULE_PATH = Path(__file__).resolve().parent
14 | MODULE_ASSETS = MODULE_PATH.joinpath("assets")
15 | KLIPPER_DIR = Path.home().joinpath("klipper")
16 | KLIPPER_EXTRAS = KLIPPER_DIR.joinpath("klippy/extras")
17 | EXTENSION_SRC = MODULE_ASSETS.joinpath(EXT_MODULE_NAME)
18 | EXTENSION_TARGET_PATH = KLIPPER_EXTRAS.joinpath(EXT_MODULE_NAME)
19 | EXAMPLE_CFG_SRC = MODULE_ASSETS.joinpath("shell_command.cfg")
20 | 


--------------------------------------------------------------------------------
/kiauh/extensions/gcode_shell_cmd/assets/gcode_shell_command.py:
--------------------------------------------------------------------------------
 1 | # Run a shell command via gcode
 2 | #
 3 | # Copyright (C) 2019  Eric Callahan <arksine.code@gmail.com>
 4 | #
 5 | # This file may be distributed under the terms of the GNU GPLv3 license.
 6 | import logging
 7 | import os
 8 | import shlex
 9 | import subprocess
10 | 
11 | 
12 | class ShellCommand:
13 |     def __init__(self, config):
14 |         self.name = config.get_name().split()[-1]
15 |         self.printer = config.get_printer()
16 |         self.gcode = self.printer.lookup_object("gcode")
17 |         cmd = config.get("command")
18 |         cmd = os.path.expanduser(cmd)
19 |         self.command = shlex.split(cmd)
20 |         self.timeout = config.getfloat("timeout", 2.0, above=0.0)
21 |         self.verbose = config.getboolean("verbose", True)
22 |         self.proc_fd = None
23 |         self.partial_output = ""
24 |         self.gcode.register_mux_command(
25 |             "RUN_SHELL_COMMAND",
26 |             "CMD",
27 |             self.name,
28 |             self.cmd_RUN_SHELL_COMMAND,
29 |             desc=self.cmd_RUN_SHELL_COMMAND_help,
30 |         )
31 | 
32 |     def _process_output(self, eventime):
33 |         if self.proc_fd is None:
34 |             return
35 |         try:
36 |             data = os.read(self.proc_fd, 4096)
37 |         except Exception:
38 |             pass
39 |         data = self.partial_output + data.decode()
40 |         if "\n" not in data:
41 |             self.partial_output = data
42 |             return
43 |         elif data[-1] != "\n":
44 |             split = data.rfind("\n") + 1
45 |             self.partial_output = data[split:]
46 |             data = data[:split]
47 |         else:
48 |             self.partial_output = ""
49 |         self.gcode.respond_info(data)
50 | 
51 |     cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command"
52 | 
53 |     def cmd_RUN_SHELL_COMMAND(self, params):
54 |         gcode_params = params.get("PARAMS", "")
55 |         gcode_params = shlex.split(gcode_params)
56 |         reactor = self.printer.get_reactor()
57 |         try:
58 |             proc = subprocess.Popen(
59 |                 self.command + gcode_params,
60 |                 stdout=subprocess.PIPE,
61 |                 stderr=subprocess.STDOUT,
62 |             )
63 |         except Exception:
64 |             logging.exception("shell_command: Command {%s} failed" % (self.name))
65 |             raise self.gcode.error("Error running command {%s}" % (self.name))
66 |         if self.verbose:
67 |             self.proc_fd = proc.stdout.fileno()
68 |             self.gcode.respond_info("Running Command {%s}...:" % (self.name))
69 |             hdl = reactor.register_fd(self.proc_fd, self._process_output)
70 |         eventtime = reactor.monotonic()
71 |         endtime = eventtime + self.timeout
72 |         complete = False
73 |         while eventtime < endtime:
74 |             eventtime = reactor.pause(eventtime + 0.05)
75 |             if proc.poll() is not None:
76 |                 complete = True
77 |                 break
78 |         if not complete:
79 |             proc.terminate()
80 |         if self.verbose:
81 |             if self.partial_output:
82 |                 self.gcode.respond_info(self.partial_output)
83 |                 self.partial_output = ""
84 |             if complete:
85 |                 msg = "Command {%s} finished\n" % (self.name)
86 |             else:
87 |                 msg = "Command {%s} timed out" % (self.name)
88 |             self.gcode.respond_info(msg)
89 |             reactor.unregister_fd(hdl)
90 |             self.proc_fd = None
91 | 
92 | 
93 | def load_config_prefix(config):
94 |     return ShellCommand(config)
95 | 


--------------------------------------------------------------------------------
/kiauh/extensions/gcode_shell_cmd/assets/shell_command.cfg:
--------------------------------------------------------------------------------
1 | [gcode_shell_command hello_world]
2 | command: echo hello world
3 | timeout: 2.
4 | verbose: True
5 | [gcode_macro HELLO_WORLD]
6 | gcode:
7 |     RUN_SHELL_COMMAND CMD=hello_world


--------------------------------------------------------------------------------
/kiauh/extensions/gcode_shell_cmd/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 1,
 4 |     "module": "gcode_shell_cmd_extension",
 5 |     "maintained_by": "dw-0",
 6 |     "display_name": "G-Code Shell Command",
 7 |     "description": ["Run a shell commands from gcode."]
 8 |   }
 9 | }
10 | 


--------------------------------------------------------------------------------
/kiauh/extensions/klipper_backup/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2023 - 2024 Staubgeborener and Tylerjet                  #
 3 | #  https://github.com/Staubgeborener/klipper-backup                       #
 4 | #  https://klipperbackup.xyz                                              #
 5 | #                                                                         #
 6 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 7 | #  https://github.com/dw-0/kiauh                                          #
 8 | #                                                                         #
 9 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
10 | # ======================================================================= #
11 | 
12 | from pathlib import Path
13 | 
14 | EXT_MODULE_NAME = "klipper_backup_extension.py"
15 | MODULE_PATH = Path(__file__).resolve().parent
16 | MOONRAKER_CONF = Path.home().joinpath("printer_data", "config", "moonraker.conf")
17 | KLIPPERBACKUP_DIR = Path.home().joinpath("klipper-backup")
18 | KLIPPERBACKUP_CONFIG_DIR = Path.home().joinpath("config_backup")
19 | KLIPPERBACKUP_REPO_URL = "https://github.com/staubgeborener/klipper-backup"
20 | 


--------------------------------------------------------------------------------
/kiauh/extensions/klipper_backup/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 4,
 4 |     "module": "klipper_backup_extension",
 5 |     "maintained_by": "Staubgeborener",
 6 |     "display_name": "Klipper-Backup",
 7 |     "description": ["Backup all your Klipper files to GitHub"],
 8 |     "updates": true
 9 |   }
10 | }
11 | 


--------------------------------------------------------------------------------
/kiauh/extensions/mainsail_theme_installer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/extensions/mainsail_theme_installer/__init__.py


--------------------------------------------------------------------------------
/kiauh/extensions/mainsail_theme_installer/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 2,
 4 |     "module": "mainsail_theme_installer_extension",
 5 |     "maintained_by": "dw-0",
 6 |     "display_name": "Mainsail Theme Installer",
 7 |     "description": ["Install Mainsail Themes maintained by the Mainsail community."]
 8 |   }
 9 | }
10 | 


--------------------------------------------------------------------------------
/kiauh/extensions/mobileraker/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | from core.backup_manager import BACKUP_ROOT_DIR
13 | from core.constants import SYSTEMD
14 | 
15 | # repo
16 | MOBILERAKER_REPO = "https://github.com/Clon1998/mobileraker_companion.git"
17 | 
18 | # names
19 | MOBILERAKER_SERVICE_NAME = "mobileraker.service"
20 | MOBILERAKER_UPDATER_SECTION_NAME = "update_manager mobileraker"
21 | MOBILERAKER_LOG_NAME = "mobileraker.log"
22 | 
23 | # directories
24 | MOBILERAKER_DIR = Path.home().joinpath("mobileraker_companion")
25 | MOBILERAKER_ENV_DIR = Path.home().joinpath("mobileraker-env")
26 | MOBILERAKER_BACKUP_DIR = BACKUP_ROOT_DIR.joinpath("mobileraker-backups")
27 | 
28 | # files
29 | MOBILERAKER_INSTALL_SCRIPT = MOBILERAKER_DIR.joinpath("scripts/install.sh")
30 | MOBILERAKER_REQ_FILE = MOBILERAKER_DIR.joinpath("scripts/mobileraker-requirements.txt")
31 | MOBILERAKER_SERVICE_FILE = SYSTEMD.joinpath(MOBILERAKER_SERVICE_NAME)
32 | 


--------------------------------------------------------------------------------
/kiauh/extensions/mobileraker/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "metadata": {
 3 |         "index": 3,
 4 |         "module": "mobileraker_extension",
 5 |         "maintained_by": "Clon1998",
 6 |         "display_name": "Mobileraker",
 7 |         "description": [
 8 |             "Companion for Mobileraker, enabling push notification for Klipper using Moonraker."
 9 |         ],
10 |         "updates": true
11 |     }
12 | }
13 | 


--------------------------------------------------------------------------------
/kiauh/extensions/obico/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from pathlib import Path
10 | 
11 | MODULE_PATH = Path(__file__).resolve().parent
12 | 
13 | # repo
14 | OBICO_REPO = "https://github.com/TheSpaghettiDetective/moonraker-obico.git"
15 | 
16 | # names
17 | OBICO_SERVICE_NAME = "moonraker-obico.service"
18 | OBICO_ENV_FILE_NAME = "moonraker-obico.env"
19 | OBICO_CFG_NAME = "moonraker-obico.cfg"
20 | OBICO_CFG_SAMPLE_NAME = "moonraker-obico.cfg.sample"
21 | OBICO_LOG_NAME = "moonraker-obico.log"
22 | OBICO_UPDATE_CFG_NAME = "moonraker-obico-update.cfg"
23 | OBICO_UPDATE_CFG_SAMPLE_NAME = "moonraker-obico-update.cfg.sample"
24 | OBICO_MACROS_CFG_NAME = "moonraker_obico_macros.cfg"
25 | 
26 | # directories
27 | OBICO_DIR = Path.home().joinpath("moonraker-obico")
28 | OBICO_ENV_DIR = Path.home().joinpath("moonraker-obico-env")
29 | 
30 | # files
31 | OBICO_SERVICE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{OBICO_SERVICE_NAME}")
32 | OBICO_ENV_FILE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{OBICO_ENV_FILE_NAME}")
33 | OBICO_LINK_SCRIPT = OBICO_DIR.joinpath("scripts/link.sh")
34 | OBICO_REQ_FILE = OBICO_DIR.joinpath("requirements.txt")
35 | 


--------------------------------------------------------------------------------
/kiauh/extensions/obico/assets/moonraker-obico.env:
--------------------------------------------------------------------------------
1 | OBICO_ARGS="-m moonraker_obico.app -c %CFG%"
2 | 


--------------------------------------------------------------------------------
/kiauh/extensions/obico/assets/moonraker-obico.service:
--------------------------------------------------------------------------------
 1 | #Systemd service file for moonraker-obico
 2 | [Unit]
 3 | Description=Moonraker-Obico
 4 | After=network-online.target moonraker.service
 5 | 
 6 | [Install]
 7 | WantedBy=multi-user.target
 8 | 
 9 | [Service]
10 | Type=simple
11 | User=%USER%
12 | WorkingDirectory=%OBICO_DIR%
13 | EnvironmentFile=%ENV_FILE%
14 | ExecStart=%ENV%/bin/python3 $OBICO_ARGS
15 | Restart=always
16 | RestartSec=5
17 | 


--------------------------------------------------------------------------------
/kiauh/extensions/obico/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 6,
 4 |     "module": "moonraker_obico_extension",
 5 |     "maintained_by": "Obico",
 6 |     "display_name": "Obico for Klipper",
 7 |     "description": [
 8 |         "Open source 3D Printing cloud and AI",
 9 |         "- AI-Powered Failure Detection",
10 |         "- Free Remote Monitoring and Access",
11 |         "- 25FPS High-Def Webcam Streaming",
12 |         "- Free 4.9-Star Mobile App"
13 |     ],
14 |     "updates": true
15 |   }
16 | }
17 | 


--------------------------------------------------------------------------------
/kiauh/extensions/octoapp/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from pathlib import Path
10 | 
11 | # repo
12 | OA_REPO = "https://github.com/crysxd/OctoApp-Plugin.git"
13 | 
14 | # directories
15 | OA_DIR = Path.home().joinpath("octoapp")
16 | OA_ENV_DIR = Path.home().joinpath("octoapp-env")
17 | 
18 | # files
19 | OA_REQ_FILE = OA_DIR.joinpath("requirements.txt")
20 | OA_DEPS_JSON_FILE = OA_DIR.joinpath("moonraker-system-dependencies.json")
21 | OA_INSTALL_SCRIPT = OA_DIR.joinpath("install.sh")
22 | OA_UPDATE_SCRIPT = OA_DIR.joinpath("update.sh")
23 | OA_INSTALLER_LOG_FILE = Path.home().joinpath("octoapp-installer.log")
24 | 
25 | # filenames
26 | OA_CFG_NAME = "octoapp.conf"
27 | OA_LOG_NAME = "octoapp.log"
28 | OA_SYS_CFG_NAME = "octoapp-system.cfg"
29 | 


--------------------------------------------------------------------------------
/kiauh/extensions/octoapp/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "metadata": {
 3 |         "index": 9,
 4 |         "module": "octoapp_extension",
 5 |         "maintained_by": "crysxd",
 6 |         "display_name": "OctoApp for Klipper",
 7 |         "description": [
 8 |             "Your favorite 3D printing app for iOS & Android",
 9 |             "- Print notifications on your phone & watch",
10 |             "- Control and start prints from your phone",
11 |             "- Live webcam view",
12 |             "- Live Gcode preview",
13 |             "- And much much more!"
14 |         ],
15 |         "updates": true
16 |     }
17 | }
18 | 


--------------------------------------------------------------------------------
/kiauh/extensions/octoapp/octoapp.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from dataclasses import dataclass, field
12 | from pathlib import Path
13 | from subprocess import CalledProcessError, run
14 | 
15 | from components.moonraker import MOONRAKER_CFG_NAME
16 | from components.moonraker.moonraker import Moonraker
17 | from core.instance_manager.base_instance import BaseInstance
18 | from core.logger import Logger
19 | from extensions.octoapp import (
20 |     OA_CFG_NAME,
21 |     OA_DIR,
22 |     OA_ENV_DIR,
23 |     OA_INSTALL_SCRIPT,
24 |     OA_LOG_NAME,
25 |     OA_SYS_CFG_NAME,
26 |     OA_UPDATE_SCRIPT,
27 | )
28 | from utils.sys_utils import get_service_file_path
29 | 
30 | 
31 | @dataclass
32 | class Octoapp:
33 |     suffix: str
34 |     base: BaseInstance = field(init=False, repr=False)
35 |     service_file_path: Path = field(init=False)
36 |     log_file_name = OA_LOG_NAME
37 |     dir: Path = OA_DIR
38 |     env_dir: Path = OA_ENV_DIR
39 |     data_dir: Path = field(init=False)
40 |     store_dir: Path = field(init=False)
41 |     cfg_file: Path = field(init=False)
42 |     sys_cfg_file: Path = field(init=False)
43 | 
44 |     def __post_init__(self):
45 |         self.base: BaseInstance = BaseInstance(Moonraker, self.suffix)
46 |         self.base.log_file_name = self.log_file_name
47 | 
48 |         self.service_file_path: Path = get_service_file_path(Octoapp, self.suffix)
49 |         self.store_dir = self.base.data_dir.joinpath("store")
50 |         self.cfg_file = self.base.cfg_dir.joinpath(OA_CFG_NAME)
51 |         self.sys_cfg_file = self.base.cfg_dir.joinpath(OA_SYS_CFG_NAME)
52 |         self.data_dir = self.base.data_dir
53 |         self.sys_cfg_file = self.base.cfg_dir.joinpath(OA_SYS_CFG_NAME)
54 | 
55 |     def create(self) -> None:
56 |         Logger.print_status("Creating OctoApp for Klipper Instance ...")
57 | 
58 |         try:
59 |             cmd = f"{OA_INSTALL_SCRIPT} {self.base.cfg_dir}/{MOONRAKER_CFG_NAME}"
60 |             run(cmd, check=True, shell=True)
61 | 
62 |         except CalledProcessError as e:
63 |             Logger.print_error(f"Error creating instance: {e}")
64 |             raise
65 | 
66 |     @staticmethod
67 |     def update() -> None:
68 |         try:
69 |             run(OA_UPDATE_SCRIPT.as_posix(), check=True, shell=True, cwd=OA_DIR)
70 | 
71 |         except CalledProcessError as e:
72 |             Logger.print_error(f"Error updating OctoApp for Klipper: {e}")
73 |             raise
74 | 


--------------------------------------------------------------------------------
/kiauh/extensions/octoeverywhere/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from pathlib import Path
10 | 
11 | # repo
12 | OE_REPO = "https://github.com/QuinnDamerell/OctoPrint-OctoEverywhere.git"
13 | 
14 | # directories
15 | OE_DIR = Path.home().joinpath("octoeverywhere")
16 | OE_ENV_DIR = Path.home().joinpath("octoeverywhere-env")
17 | OE_STORE_DIR = OE_DIR.joinpath("octoeverywhere-store")
18 | 
19 | # files
20 | OE_REQ_FILE = OE_DIR.joinpath("requirements.txt")
21 | OE_DEPS_JSON_FILE = OE_DIR.joinpath("moonraker-system-dependencies.json")
22 | OE_INSTALL_SCRIPT = OE_DIR.joinpath("install.sh")
23 | OE_UPDATE_SCRIPT = OE_DIR.joinpath("update.sh")
24 | OE_INSTALLER_LOG_FILE = Path.home().joinpath("octoeverywhere-installer.log")
25 | 
26 | # filenames
27 | OE_CFG_NAME = "octoeverywhere.conf"
28 | OE_LOG_NAME = "octoeverywhere.log"
29 | OE_SYS_CFG_NAME = "octoeverywhere-system.cfg"
30 | 


--------------------------------------------------------------------------------
/kiauh/extensions/octoeverywhere/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "metadata": {
 3 |         "index": 7,
 4 |         "module": "octoeverywhere_extension",
 5 |         "maintained_by": "QuinnDamerell",
 6 |         "display_name": "OctoEverywhere for Klipper",
 7 |         "description": [
 8 |             "Cloud Empower Your Klipper 3D Printers With:",
 9 |             "- Free, Private, And Secure Remote Access",
10 |             "- AI Print Failure Detection",
11 |             "- Real-time Notifications",
12 |             "- Live Streaming, and More!"
13 |         ],
14 |         "updates": true
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/kiauh/extensions/octoeverywhere/octoeverywhere.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | from dataclasses import dataclass, field
12 | from pathlib import Path
13 | from subprocess import CalledProcessError, run
14 | 
15 | from components.moonraker import MOONRAKER_CFG_NAME
16 | from components.moonraker.moonraker import Moonraker
17 | from core.instance_manager.base_instance import BaseInstance
18 | from core.logger import Logger
19 | from extensions.octoeverywhere import (
20 |     OE_CFG_NAME,
21 |     OE_DIR,
22 |     OE_ENV_DIR,
23 |     OE_INSTALL_SCRIPT,
24 |     OE_LOG_NAME,
25 |     OE_SYS_CFG_NAME,
26 |     OE_UPDATE_SCRIPT,
27 | )
28 | from utils.sys_utils import get_service_file_path
29 | 
30 | 
31 | @dataclass
32 | class Octoeverywhere:
33 |     suffix: str
34 |     base: BaseInstance = field(init=False, repr=False)
35 |     service_file_path: Path = field(init=False)
36 |     log_file_name = OE_LOG_NAME
37 |     dir: Path = OE_DIR
38 |     env_dir: Path = OE_ENV_DIR
39 |     data_dir: Path = field(init=False)
40 |     store_dir: Path = field(init=False)
41 |     cfg_file: Path = field(init=False)
42 |     sys_cfg_file: Path = field(init=False)
43 | 
44 |     def __post_init__(self):
45 |         self.base: BaseInstance = BaseInstance(Moonraker, self.suffix)
46 |         self.base.log_file_name = self.log_file_name
47 | 
48 |         self.service_file_path: Path = get_service_file_path(
49 |             Octoeverywhere, self.suffix
50 |         )
51 |         self.store_dir = self.base.data_dir.joinpath("store")
52 |         self.cfg_file = self.base.cfg_dir.joinpath(OE_CFG_NAME)
53 |         self.sys_cfg_file = self.base.cfg_dir.joinpath(OE_SYS_CFG_NAME)
54 |         self.data_dir = self.base.data_dir
55 |         self.sys_cfg_file = self.base.cfg_dir.joinpath(OE_SYS_CFG_NAME)
56 | 
57 |     def create(self) -> None:
58 |         Logger.print_status("Creating OctoEverywhere for Klipper Instance ...")
59 | 
60 |         try:
61 |             cmd = f"{OE_INSTALL_SCRIPT} {self.base.cfg_dir}/{MOONRAKER_CFG_NAME}"
62 |             run(cmd, check=True, shell=True)
63 | 
64 |         except CalledProcessError as e:
65 |             Logger.print_error(f"Error creating instance: {e}")
66 |             raise
67 | 
68 |     @staticmethod
69 |     def update() -> None:
70 |         try:
71 |             run(OE_UPDATE_SCRIPT.as_posix(), check=True, shell=True, cwd=OE_DIR)
72 | 
73 |         except CalledProcessError as e:
74 |             Logger.print_error(f"Error updating OctoEverywhere for Klipper: {e}")
75 |             raise
76 | 


--------------------------------------------------------------------------------
/kiauh/extensions/pretty_gcode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/extensions/pretty_gcode/__init__.py


--------------------------------------------------------------------------------
/kiauh/extensions/pretty_gcode/assets/pgcode.local.conf:
--------------------------------------------------------------------------------
 1 | # PrettyGCode website configuration
 2 | # copy this file to /etc/nginx/sites-available/pgcode.local.conf
 3 | # then to enable:
 4 | # sudo ln -s /etc/nginx/sites-available/pgcode.local.conf  /etc/nginx/sites-enabled/pgcode.local.conf
 5 | # then restart ngninx:
 6 | # sudo systemctl reload nginx
 7 | server {
 8 |      listen %PORT%;
 9 |      listen [::]:%PORT%;
10 |      server_name pgcode.local;
11 | 
12 |      root %ROOT_DIR%;
13 | 
14 |      index pgcode.html;
15 | 
16 |      location / {
17 |           try_files $uri $uri/ =404;
18 |      }
19 | }
20 | 


--------------------------------------------------------------------------------
/kiauh/extensions/pretty_gcode/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 8,
 4 |     "module": "pretty_gcode_extension",
 5 |     "maintained_by": "Kragrathea",
 6 |     "display_name": "PrettyGCode for Klipper",
 7 |     "description": ["3D G-Code viewer for Klipper"],
 8 |     "updates": true
 9 |   }
10 | }
11 | 


--------------------------------------------------------------------------------
/kiauh/extensions/simply_print/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/extensions/simply_print/__init__.py


--------------------------------------------------------------------------------
/kiauh/extensions/simply_print/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "metadata": {
 3 |         "index": 10,
 4 |         "module": "simply_print_extension",
 5 |         "maintained_by": "dw-0",
 6 |         "display_name": "SimplyPrint",
 7 |         "description": [
 8 |             "3D Printer Cloud Management Software.",
 9 |             "\n\n",
10 |             "3D printing doesn't have to be a complicated, analog, SD card-filled experience; step into the future of modern 3D printing"
11 |         ]
12 |     }
13 | }
14 | 


--------------------------------------------------------------------------------
/kiauh/extensions/spoolman/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from pathlib import Path
10 | 
11 | MODULE_PATH = Path(__file__).resolve().parent
12 | SPOOLMAN_DOCKER_IMAGE = "ghcr.io/donkie/spoolman:latest"
13 | SPOOLMAN_DIR = Path.home().joinpath("spoolman")
14 | SPOOLMAN_DATA_DIR = SPOOLMAN_DIR.joinpath("data")
15 | SPOOLMAN_COMPOSE_FILE = SPOOLMAN_DIR.joinpath("docker-compose.yml")
16 | SPOOLMAN_DEFAULT_PORT = 7912
17 | 


--------------------------------------------------------------------------------
/kiauh/extensions/spoolman/assets/docker-compose.yml:
--------------------------------------------------------------------------------
 1 | services:
 2 |   spoolman:
 3 |     image: ghcr.io/donkie/spoolman:latest
 4 |     restart: unless-stopped
 5 |     volumes:
 6 |       # Mount the host machine's ./data directory into the container's /home/app/.local/share/spoolman directory
 7 |       - type: bind
 8 |         source: ./data # This is where the data will be stored locally. Could also be set to for example `source: /home/pi/printer_data/spoolman`.
 9 |         target: /home/app/.local/share/spoolman # Do NOT modify this line
10 |     ports:
11 |       # Map the host machine's port 7912 to the container's port 8000
12 |       - "7912:8000"
13 |     environment:
14 |       - TZ=Europe/Stockholm # Optional, defaults to UTC
15 | 


--------------------------------------------------------------------------------
/kiauh/extensions/spoolman/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 11,
 4 |     "module": "spoolman_extension",
 5 |     "maintained_by": "dw-0",
 6 |     "display_name": "Spoolman (Docker)",
 7 |     "description": [
 8 |       "Filament manager for 3D printing",
 9 |       "- Track your filament inventory",
10 |       "- Monitor filament usage",
11 |       "- Manage vendors, materials, and spools",
12 |       "- Integrates with Moonraker",
13 |       "\n\n",
14 |       "Note: This extension installs Spoolman using Docker. Docker must be installed on your system before installing Spoolman."
15 |     ],
16 |     "updates": true
17 |   }
18 | }
19 | 


--------------------------------------------------------------------------------
/kiauh/extensions/telegram_bot/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from pathlib import Path
10 | 
11 | MODULE_PATH = Path(__file__).resolve().parent
12 | 
13 | # repo
14 | TG_BOT_REPO = "https://github.com/nlef/moonraker-telegram-bot.git"
15 | 
16 | # names
17 | TG_BOT_CFG_NAME = "telegram.conf"
18 | TG_BOT_LOG_NAME = "telegram.log"
19 | TG_BOT_SERVICE_NAME = "moonraker-telegram-bot.service"
20 | TG_BOT_ENV_FILE_NAME = "moonraker-telegram-bot.env"
21 | 
22 | # directories
23 | TG_BOT_DIR = Path.home().joinpath("moonraker-telegram-bot")
24 | TG_BOT_ENV = Path.home().joinpath("moonraker-telegram-bot-env")
25 | 
26 | # files
27 | TG_BOT_SERVICE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{TG_BOT_SERVICE_NAME}")
28 | TG_BOT_ENV_FILE_TEMPLATE = MODULE_PATH.joinpath(f"assets/{TG_BOT_ENV_FILE_NAME}")
29 | TG_BOT_REQ_FILE = TG_BOT_DIR.joinpath("scripts/requirements.txt")
30 | 


--------------------------------------------------------------------------------
/kiauh/extensions/telegram_bot/assets/moonraker-telegram-bot.env:
--------------------------------------------------------------------------------
1 | TELEGRAM_BOT_ARGS="%TELEGRAM_BOT_DIR%/bot/main.py -c %CFG% -l %LOG%"


--------------------------------------------------------------------------------
/kiauh/extensions/telegram_bot/assets/moonraker-telegram-bot.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=Moonraker Telegram Bot SV1
 3 | Documentation=https://github.com/nlef/moonraker-telegram-bot/wiki
 4 | After=network-online.target
 5 | 
 6 | [Install]
 7 | WantedBy=multi-user.target
 8 | 
 9 | [Service]
10 | Type=simple
11 | User=%USER%
12 | WorkingDirectory=%TELEGRAM_BOT_DIR%
13 | EnvironmentFile=%ENV_FILE%
14 | ExecStart=%ENV%/bin/python $TELEGRAM_BOT_ARGS
15 | Restart=always
16 | RestartSec=10
17 | 


--------------------------------------------------------------------------------
/kiauh/extensions/telegram_bot/metadata.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "metadata": {
 3 |     "index": 5,
 4 |     "module": "moonraker_telegram_bot_extension",
 5 |     "maintained_by": "nlef",
 6 |     "display_name": "Moonraker Telegram Bot",
 7 |     "description": ["Control your printer with the Telegram messenger app."],
 8 |     "project_url": "https://github.com/nlef/moonraker-telegram-bot",
 9 |     "updates": true
10 |   }
11 | }
12 | 


--------------------------------------------------------------------------------
/kiauh/main.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | import io
10 | import sys
11 | 
12 | from core.logger import Logger
13 | from core.menus.main_menu import MainMenu
14 | from core.settings.kiauh_settings import KiauhSettings
15 | 
16 | 
17 | def ensure_encoding() -> None:
18 |     if sys.stdout.encoding == "UTF-8" or not isinstance(sys.stdout, io.TextIOWrapper):
19 |         return
20 |     sys.stdout.reconfigure(encoding="utf-8")
21 | 
22 | 
23 | def main() -> None:
24 |     try:
25 |         KiauhSettings()
26 |         ensure_encoding()
27 |         MainMenu().run()
28 |     except KeyboardInterrupt:
29 |         Logger.print_ok("\nHappy printing!\n", prefix=False)
30 | 


--------------------------------------------------------------------------------
/kiauh/procedures/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/kiauh/procedures/__init__.py


--------------------------------------------------------------------------------
/kiauh/utils/__init__.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from pathlib import Path
11 | 
12 | MODULE_PATH = Path(__file__).resolve().parent
13 | 


--------------------------------------------------------------------------------
/kiauh/utils/config_utils.py:
--------------------------------------------------------------------------------
  1 | # ======================================================================= #
  2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
  3 | #                                                                         #
  4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
  5 | #  https://github.com/dw-0/kiauh                                          #
  6 | #                                                                         #
  7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
  8 | # ======================================================================= #
  9 | from __future__ import annotations
 10 | 
 11 | import shutil
 12 | import tempfile
 13 | from pathlib import Path
 14 | from typing import List, Tuple
 15 | 
 16 | from core.logger import Logger
 17 | from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
 18 |     SimpleConfigParser,
 19 | )
 20 | from utils.instance_type import InstanceType
 21 | 
 22 | ConfigOption = Tuple[str, str]
 23 | 
 24 | 
 25 | def add_config_section(
 26 |     section: str,
 27 |     instances: List[InstanceType],
 28 |     options: List[ConfigOption] | None = None,
 29 | ) -> None:
 30 |     if not instances:
 31 |         return
 32 | 
 33 |     for instance in instances:
 34 |         cfg_file = instance.cfg_file
 35 |         Logger.print_status(f"Add section '[{section}]' to '{cfg_file}' ...")
 36 | 
 37 |         if not Path(cfg_file).exists():
 38 |             Logger.print_warn(f"'{cfg_file}' not found!")
 39 |             continue
 40 | 
 41 |         scp = SimpleConfigParser()
 42 |         scp.read_file(cfg_file)
 43 |         if scp.has_section(section):
 44 |             Logger.print_info("Section already exist. Skipped ...")
 45 |             continue
 46 | 
 47 |         scp.add_section(section)
 48 | 
 49 |         if options is not None:
 50 |             for option in reversed(options):
 51 |                 scp.set_option(section, option[0], option[1])
 52 | 
 53 |         scp.write_file(cfg_file)
 54 | 
 55 |         Logger.print_ok("OK!")
 56 | 
 57 | 
 58 | def add_config_section_at_top(section: str, instances: List[InstanceType]) -> None:
 59 |     # TODO: this could be implemented natively in SimpleConfigParser
 60 |     for instance in instances:
 61 |         tmp_cfg = tempfile.NamedTemporaryFile(mode="w", delete=False)
 62 |         tmp_cfg_path = Path(tmp_cfg.name)
 63 |         scp = SimpleConfigParser()
 64 |         scp.read_file(tmp_cfg_path)
 65 |         scp.add_section(section)
 66 |         scp.write_file(tmp_cfg_path)
 67 |         tmp_cfg.close()
 68 | 
 69 |         cfg_file = instance.cfg_file
 70 |         with open(cfg_file, "r") as org:
 71 |             org_content = org.readlines()
 72 |         with open(tmp_cfg_path, "a") as tmp:
 73 |             tmp.writelines(org_content)
 74 | 
 75 |         cfg_file.unlink()
 76 |         shutil.move(tmp_cfg_path, cfg_file)
 77 | 
 78 |         Logger.print_ok("OK!")
 79 | 
 80 | 
 81 | def remove_config_section(
 82 |     section: str, instances: List[InstanceType]
 83 | ) -> List[InstanceType]:
 84 |     removed_from: List[instances] = []
 85 |     for instance in instances:
 86 |         cfg_file = instance.cfg_file
 87 |         Logger.print_status(f"Remove section '[{section}]' from '{cfg_file}' ...")
 88 | 
 89 |         if not Path(cfg_file).exists():
 90 |             Logger.print_warn(f"'{cfg_file}' not found!")
 91 |             continue
 92 | 
 93 |         scp = SimpleConfigParser()
 94 |         scp.read_file(cfg_file)
 95 |         if not scp.has_section(section):
 96 |             Logger.print_info("Section does not exist. Skipped ...")
 97 |             continue
 98 | 
 99 |         scp.remove_section(section)
100 |         scp.write_file(cfg_file)
101 | 
102 |         removed_from.append(instance)
103 |         Logger.print_ok("OK!")
104 | 
105 |     return removed_from
106 | 


--------------------------------------------------------------------------------
/kiauh/utils/instance_type.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | 
10 | from typing import TypeVar
11 | 
12 | from components.klipper.klipper import Klipper
13 | from components.moonraker.moonraker import Moonraker
14 | from extensions.obico.moonraker_obico import MoonrakerObico
15 | from extensions.octoeverywhere.octoeverywhere import Octoeverywhere
16 | from extensions.octoapp.octoapp import Octoapp
17 | from extensions.telegram_bot.moonraker_telegram_bot import MoonrakerTelegramBot
18 | 
19 | InstanceType = TypeVar(
20 |     "InstanceType",
21 |     Klipper,
22 |     Moonraker,
23 |     MoonrakerTelegramBot,
24 |     MoonrakerObico,
25 |     Octoeverywhere,
26 |     Octoapp,
27 | )
28 | 


--------------------------------------------------------------------------------
/kiauh/utils/instance_utils.py:
--------------------------------------------------------------------------------
 1 | # ======================================================================= #
 2 | #  Copyright (C) 2020 - 2025 Dominik Willner <th33xitus@gmail.com>        #
 3 | #                                                                         #
 4 | #  This file is part of KIAUH - Klipper Installation And Update Helper    #
 5 | #  https://github.com/dw-0/kiauh                                          #
 6 | #                                                                         #
 7 | #  This file may be distributed under the terms of the GNU GPLv3 license  #
 8 | # ======================================================================= #
 9 | from __future__ import annotations
10 | 
11 | import re
12 | from pathlib import Path
13 | from typing import List
14 | 
15 | from core.constants import SYSTEMD
16 | from core.instance_manager.base_instance import SUFFIX_BLACKLIST
17 | from utils.instance_type import InstanceType
18 | 
19 | 
20 | def get_instances(
21 |     instance_type: type, suffix_blacklist: List[str] = SUFFIX_BLACKLIST
22 | ) -> List[InstanceType]:
23 |     from utils.common import convert_camelcase_to_kebabcase
24 | 
25 |     if not isinstance(instance_type, type):
26 |         raise ValueError("instance_type must be a class")
27 | 
28 |     name = convert_camelcase_to_kebabcase(instance_type.__name__)
29 |     pattern = re.compile(f"^{name}(-[0-9a-zA-Z]+)?.service
quot;)
30 | 
31 |     service_list = [
32 |         Path(SYSTEMD, service)
33 |         for service in SYSTEMD.iterdir()
34 |         if pattern.search(service.name)
35 |         and not any(s in service.name for s in suffix_blacklist)
36 |     ]
37 | 
38 |     instance_list = [
39 |         instance_type(get_instance_suffix(name, service)) for service in service_list
40 |     ]
41 | 
42 |     def _sort_instance_list(suffix: int | str | None):
43 |         if suffix is None:
44 |             return
45 |         elif isinstance(suffix, str) and suffix.isdigit():
46 |             return f"{int(suffix):04}"
47 |         else:
48 |             return suffix
49 | 
50 |     return sorted(instance_list, key=lambda x: _sort_instance_list(x.suffix))
51 | 
52 | 
53 | def get_instance_suffix(name: str, file_path: Path) -> str:
54 |     # to get the suffix of the instance, we remove the name of the instance from
55 |     # the file name, if the remaining part an empty string we return it
56 |     # otherwise there is and hyphen left, and we return the part after the hyphen
57 |     suffix = file_path.stem[len(name) :]
58 |     return suffix[1:] if suffix else ""
59 | 


--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
 1 | [project]
 2 | requires-python = ">=3.8"
 3 | 
 4 | [project.optional-dependencies]
 5 | dev=["ruff", "pyright"]
 6 | 
 7 | [tool.ruff]
 8 | required-version = ">=0.9.10"
 9 | respect-gitignore = true
10 | exclude = [".git",".github", "./docs", "kiauh/core/submodules"]
11 | line-length = 88
12 | indent-width = 4
13 | output-format = "full"
14 | target-version = "py38"
15 | 
16 | [tool.ruff.format]
17 | indent-style = "space"
18 | line-ending = "lf"
19 | quote-style = "double"
20 | 
21 | [tool.ruff.lint]
22 | extend-select = ["I"]
23 | 


--------------------------------------------------------------------------------
/pyrightconfig.json:
--------------------------------------------------------------------------------
1 | {
2 |   "pythonVersion": "3.8",
3 |   "pythonPlatform": "Linux",
4 |   "typeCheckingMode": "standard",
5 |   "venvPath": "./.kiauh-env"
6 | }
7 | 


--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | ruff (>=0.9.10)
2 | pyright
3 | 


--------------------------------------------------------------------------------
/resources/autocommit.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #####################################################################
 4 | ### Please set the paths accordingly. In case you don't have all  ###
 5 | ### the listed folders, just keep that line commented out.        ###
 6 | #####################################################################
 7 | ### Path to your config folder you want to back up
 8 | #config_folder=~/klipper_config
 9 | 
10 | ### Path to your Klipper folder, by default that is '~/klipper'
11 | #klipper_folder=~/klipper
12 | 
13 | ### Path to your Moonraker folder, by default that is '~/moonraker'
14 | #moonraker_folder=~/moonraker
15 | 
16 | ### Path to your Mainsail folder, by default that is '~/mainsail'
17 | #mainsail_folder=~/mainsail
18 | 
19 | ### Path to your Fluidd folder, by default that is '~/fluidd'
20 | #fluidd_folder=~/fluidd
21 | 
22 | #####################################################################
23 | #####################################################################
24 | 
25 | 
26 | #####################################################################
27 | ################ !!! DO NOT EDIT BELOW THIS LINE !!! ################
28 | #####################################################################
29 | grab_version() {
30 |   local klipper_commit moonraker_commit
31 |   local mainsail_ver fluidd_ver
32 | 
33 |   if [[ -n ${klipper_folder} ]]; then
34 |     cd "${klipper_folder}"
35 |     klipper_commit=$(git rev-parse --short=7 HEAD)
36 |     m1="Klipper on commit: ${klipper_commit}"
37 |   fi
38 |   if [[ -n ${moonraker_folder} ]]; then
39 |     cd "${moonraker_folder}"
40 |     moonraker_commit=$(git rev-parse --short=7 HEAD)
41 |     m2="Moonraker on commit: ${moonraker_commit}"
42 |   fi
43 |   if [[ -n ${mainsail_folder} ]]; then
44 |     mainsail_ver=$(head -n 1 "${mainsail_folder}/.version")
45 |     m3="Mainsail version: ${mainsail_ver}"
46 |   fi
47 |   if [[ -n ${fluidd_folder} ]]; then
48 |     fluidd_ver=$(head -n 1 "${fluidd_folder}/.version")
49 |     m4="Fluidd version: ${fluidd_ver}"
50 |   fi
51 | }
52 | 
53 | push_config() {
54 |   local current_date
55 |   
56 |   cd "${config_folder}" || exit 1
57 |   git pull
58 |   git add .
59 |   current_date=$(date +"%Y-%m-%d %T")
60 |   git commit -m "Autocommit from ${current_date}" -m "${m1}" -m "${m2}" -m "${m3}" -m "${m4}"
61 |   git push
62 | }
63 | 
64 | grab_version
65 | push_config
66 | 


--------------------------------------------------------------------------------
/resources/common_vars.conf:
--------------------------------------------------------------------------------
1 | # /etc/nginx/conf.d/common_vars.conf
2 | 
3 | map $http_upgrade $connection_upgrade {
4 |     default upgrade;
5 |     '' close;
6 | }


--------------------------------------------------------------------------------
/resources/example.printer.cfg:
--------------------------------------------------------------------------------
 1 | [mcu]
 2 | serial: /dev/serial/by-id/<your-mcu-id>
 3 | 
 4 | [virtual_sdcard]
 5 | path: %GCODES_DIR%
 6 | on_error_gcode: CANCEL_PRINT
 7 | 
 8 | [printer]
 9 | kinematics: none
10 | max_velocity: 1000
11 | max_accel: 1000
12 | 


--------------------------------------------------------------------------------
/resources/fluidd:
--------------------------------------------------------------------------------
 1 | # /etc/nginx/sites-available/fluidd
 2 | 
 3 | server {
 4 |     listen 80;
 5 | 
 6 |     access_log /var/log/nginx/fluidd-access.log;
 7 |     error_log /var/log/nginx/fluidd-error.log;
 8 | 
 9 |     # disable this section on smaller hardware like a pi zero
10 |     gzip on;
11 |     gzip_vary on;
12 |     gzip_proxied any;
13 |     gzip_proxied expired no-cache no-store private auth;
14 |     gzip_comp_level 4;
15 |     gzip_buffers 16 8k;
16 |     gzip_http_version 1.1;
17 |     gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;
18 | 
19 |     # web_path from fluidd static files
20 |     root /home/pi/fluidd;
21 | 
22 |     index index.html;
23 |     server_name _;
24 | 
25 |     # disable max upload size checks
26 |     client_max_body_size 0;
27 | 
28 |     # disable proxy request buffering
29 |     proxy_request_buffering off;
30 | 
31 |     location / {
32 |         try_files $uri $uri/ /index.html;
33 |     }
34 | 
35 |     location = /index.html {
36 |         add_header Cache-Control "no-store, no-cache, must-revalidate";
37 |     }
38 | 
39 |     location /websocket {
40 |         proxy_pass http://apiserver/websocket;
41 |         proxy_http_version 1.1;
42 |         proxy_set_header Upgrade $http_upgrade;
43 |         proxy_set_header Connection $connection_upgrade;
44 |         proxy_set_header Host $http_host;
45 |         proxy_set_header X-Real-IP $remote_addr;
46 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
47 |         proxy_read_timeout 86400;
48 |     }
49 | 
50 |     location ~ ^/(printer|api|access|machine|server)/ {
51 |         proxy_pass http://apiserver$request_uri;
52 |         proxy_http_version 1.1;
53 |         proxy_set_header Upgrade $http_upgrade;
54 |         proxy_set_header Host $http_host;
55 |         proxy_set_header X-Real-IP $remote_addr;
56 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57 |         proxy_set_header X-Scheme $scheme;
58 |         proxy_read_timeout 600;
59 |     }
60 | 
61 |     location /webcam/ {
62 |         postpone_output 0;
63 |         proxy_buffering off;
64 |         proxy_ignore_headers X-Accel-Buffering;
65 |         access_log off;
66 |         error_log off;
67 |         proxy_pass http://mjpgstreamer1/;
68 |     }
69 | 
70 |     location /webcam2/ {
71 |         postpone_output 0;
72 |         proxy_buffering off;
73 |         proxy_ignore_headers X-Accel-Buffering;
74 |         access_log off;
75 |         error_log off;
76 |         proxy_pass http://mjpgstreamer2/;
77 |     }
78 | 
79 |     location /webcam3/ {
80 |         postpone_output 0;
81 |         proxy_buffering off;
82 |         proxy_ignore_headers X-Accel-Buffering;
83 |         access_log off;
84 |         error_log off;
85 |         proxy_pass http://mjpgstreamer3/;
86 |     }
87 | 
88 |     location /webcam4/ {
89 |         postpone_output 0;
90 |         proxy_buffering off;
91 |         proxy_ignore_headers X-Accel-Buffering;
92 |         access_log off;
93 |         error_log off;
94 |         proxy_pass http://mjpgstreamer4/;
95 |     }
96 | }
97 | 


--------------------------------------------------------------------------------
/resources/gcode_shell_command.py:
--------------------------------------------------------------------------------
 1 | # Run a shell command via gcode
 2 | #
 3 | # Copyright (C) 2019  Eric Callahan <arksine.code@gmail.com>
 4 | #
 5 | # This file may be distributed under the terms of the GNU GPLv3 license.
 6 | import os
 7 | import shlex
 8 | import subprocess
 9 | import logging
10 | 
11 | 
12 | class ShellCommand:
13 |     def __init__(self, config):
14 |         self.name = config.get_name().split()[-1]
15 |         self.printer = config.get_printer()
16 |         self.gcode = self.printer.lookup_object("gcode")
17 |         cmd = config.get("command")
18 |         cmd = os.path.expanduser(cmd)
19 |         self.command = shlex.split(cmd)
20 |         self.timeout = config.getfloat("timeout", 2.0, above=0.0)
21 |         self.verbose = config.getboolean("verbose", True)
22 |         self.proc_fd = None
23 |         self.partial_output = ""
24 |         self.gcode.register_mux_command(
25 |             "RUN_SHELL_COMMAND",
26 |             "CMD",
27 |             self.name,
28 |             self.cmd_RUN_SHELL_COMMAND,
29 |             desc=self.cmd_RUN_SHELL_COMMAND_help,
30 |         )
31 | 
32 |     def _process_output(self, eventime):
33 |         if self.proc_fd is None:
34 |             return
35 |         try:
36 |             data = os.read(self.proc_fd, 4096)
37 |         except Exception:
38 |             pass
39 |         data = self.partial_output + data.decode()
40 |         if "\n" not in data:
41 |             self.partial_output = data
42 |             return
43 |         elif data[-1] != "\n":
44 |             split = data.rfind("\n") + 1
45 |             self.partial_output = data[split:]
46 |             data = data[:split]
47 |         else:
48 |             self.partial_output = ""
49 |         self.gcode.respond_info(data)
50 | 
51 |     cmd_RUN_SHELL_COMMAND_help = "Run a linux shell command"
52 | 
53 |     def cmd_RUN_SHELL_COMMAND(self, params):
54 |         gcode_params = params.get("PARAMS", "")
55 |         gcode_params = shlex.split(gcode_params)
56 |         reactor = self.printer.get_reactor()
57 |         try:
58 |             proc = subprocess.Popen(
59 |                 self.command + gcode_params,
60 |                 stdout=subprocess.PIPE,
61 |                 stderr=subprocess.STDOUT,
62 |             )
63 |         except Exception:
64 |             logging.exception("shell_command: Command {%s} failed" % (self.name))
65 |             raise self.gcode.error("Error running command {%s}" % (self.name))
66 |         if self.verbose:
67 |             self.proc_fd = proc.stdout.fileno()
68 |             self.gcode.respond_info("Running Command {%s}...:" % (self.name))
69 |             hdl = reactor.register_fd(self.proc_fd, self._process_output)
70 |         eventtime = reactor.monotonic()
71 |         endtime = eventtime + self.timeout
72 |         complete = False
73 |         while eventtime < endtime:
74 |             eventtime = reactor.pause(eventtime + 0.05)
75 |             if proc.poll() is not None:
76 |                 complete = True
77 |                 break
78 |         if not complete:
79 |             proc.terminate()
80 |         if self.verbose:
81 |             if self.partial_output:
82 |                 self.gcode.respond_info(self.partial_output)
83 |                 self.partial_output = ""
84 |             if complete:
85 |                 msg = "Command {%s} finished\n" % (self.name)
86 |             else:
87 |                 msg = "Command {%s} timed out" % (self.name)
88 |             self.gcode.respond_info(msg)
89 |             reactor.unregister_fd(hdl)
90 |             self.proc_fd = None
91 | 
92 | 
93 | def load_config_prefix(config):
94 |     return ShellCommand(config)
95 | 


--------------------------------------------------------------------------------
/resources/klipper.env:
--------------------------------------------------------------------------------
1 | KLIPPER_ARGS="%KLIPPER_DIR%/klippy/klippy.py %CFG% -I %PRINTER% -l %LOG% -a %UDS%"


--------------------------------------------------------------------------------
/resources/klipper.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=Klipper 3D Printer Firmware SV1
 3 | Documentation=https://www.klipper3d.org/
 4 | After=network-online.target
 5 | Wants=udev.target
 6 | 
 7 | [Install]
 8 | WantedBy=multi-user.target
 9 | 
10 | [Service]
11 | Type=simple
12 | User=%USER%
13 | RemainAfterExit=yes
14 | WorkingDirectory=%KLIPPER_DIR%
15 | EnvironmentFile=%ENV_FILE%
16 | ExecStart=%ENV%/bin/python $KLIPPER_ARGS
17 | Restart=always
18 | RestartSec=10
19 | 


--------------------------------------------------------------------------------
/resources/mainsail:
--------------------------------------------------------------------------------
 1 | # /etc/nginx/sites-available/mainsail
 2 | 
 3 | server {
 4 |     listen 80;
 5 | 
 6 |     access_log /var/log/nginx/mainsail-access.log;
 7 |     error_log /var/log/nginx/mainsail-error.log;
 8 | 
 9 |     # disable this section on smaller hardware like a pi zero
10 |     gzip on;
11 |     gzip_vary on;
12 |     gzip_proxied any;
13 |     gzip_proxied expired no-cache no-store private auth;
14 |     gzip_comp_level 4;
15 |     gzip_buffers 16 8k;
16 |     gzip_http_version 1.1;
17 |     gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml;
18 | 
19 |     # web_path from mainsail static files
20 |     root /home/pi/mainsail;
21 | 
22 |     index index.html;
23 |     server_name _;
24 | 
25 |     # disable max upload size checks
26 |     client_max_body_size 0;
27 | 
28 |     # disable proxy request buffering
29 |     proxy_request_buffering off;
30 | 
31 |     location / {
32 |         try_files $uri $uri/ /index.html;
33 |     }
34 | 
35 |     location = /index.html {
36 |         add_header Cache-Control "no-store, no-cache, must-revalidate";
37 |     }
38 | 
39 |     location /websocket {
40 |         proxy_pass http://apiserver/websocket;
41 |         proxy_http_version 1.1;
42 |         proxy_set_header Upgrade $http_upgrade;
43 |         proxy_set_header Connection $connection_upgrade;
44 |         proxy_set_header Host $http_host;
45 |         proxy_set_header X-Real-IP $remote_addr;
46 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
47 |         proxy_read_timeout 86400;
48 |     }
49 | 
50 |     location ~ ^/(printer|api|access|machine|server)/ {
51 |         proxy_pass http://apiserver$request_uri;
52 |         proxy_http_version 1.1;
53 |         proxy_set_header Upgrade $http_upgrade;
54 |         proxy_set_header Host $http_host;
55 |         proxy_set_header X-Real-IP $remote_addr;
56 |         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57 |         proxy_set_header X-Scheme $scheme;
58 |         proxy_read_timeout 600;
59 |     }
60 | 
61 |     location /webcam/ {
62 |         postpone_output 0;
63 |         proxy_buffering off;
64 |         proxy_ignore_headers X-Accel-Buffering;
65 |         access_log off;
66 |         error_log off;
67 |         proxy_pass http://mjpgstreamer1/;
68 |     }
69 | 
70 |     location /webcam2/ {
71 |         postpone_output 0;
72 |         proxy_buffering off;
73 |         proxy_ignore_headers X-Accel-Buffering;
74 |         access_log off;
75 |         error_log off;
76 |         proxy_pass http://mjpgstreamer2/;
77 |     }
78 | 
79 |     location /webcam3/ {
80 |         postpone_output 0;
81 |         proxy_buffering off;
82 |         proxy_ignore_headers X-Accel-Buffering;
83 |         access_log off;
84 |         error_log off;
85 |         proxy_pass http://mjpgstreamer3/;
86 |     }
87 | 
88 |     location /webcam4/ {
89 |         postpone_output 0;
90 |         proxy_buffering off;
91 |         proxy_ignore_headers X-Accel-Buffering;
92 |         access_log off;
93 |         error_log off;
94 |         proxy_pass http://mjpgstreamer4/;
95 |     }
96 | }
97 | 


--------------------------------------------------------------------------------
/resources/mjpg-streamer/webcam.txt:
--------------------------------------------------------------------------------
 1 | ### Windows users: To edit this file use Notepad++, VSCode, Atom or SublimeText.
 2 | ### Do not use Notepad or WordPad.
 3 | 
 4 | ### MacOSX users: If you use Textedit to edit this file make sure to use 
 5 | ### "plain text format" and "disable smart quotes" in "Textedit > Preferences"
 6 | 
 7 | ### Configure which camera to use
 8 | #
 9 | # Available options are:
10 | # - auto: tries first usb webcam, if that's not available tries raspi cam
11 | # - usb: only tries usb webcam
12 | # - raspi: only tries raspi cam
13 | #
14 | # Defaults to auto
15 | #
16 | #camera="auto"
17 | 
18 | ### Additional options to supply to MJPG Streamer for the USB camera
19 | #
20 | # See https://faq.octoprint.org/mjpg-streamer-config for available options
21 | #
22 | # Defaults to a resolution of 640x480 px and a framerate of 10 fps
23 | #
24 | #camera_usb_options="-r 640x480 -f 10"
25 | 
26 | ### Additional webcam devices known to cause problems with -f
27 | #
28 | # Apparently there a some devices out there that with the current 
29 | # mjpg_streamer release do not support the -f parameter (for specifying 
30 | # the capturing framerate) and will just refuse to output an image if it 
31 | # is supplied.
32 | #
33 | # The webcam daemon will detect those devices by their USB Vendor and Product
34 | # ID and remove the -f parameter from the options provided to mjpg_streamer.
35 | #
36 | # By default, this is done for the following devices:
37 | #   Logitech C170 (046d:082b)
38 | #   GEMBIRD (1908:2310)
39 | #   Genius F100 (0458:708c)
40 | #   Cubeternet GL-UPC822 UVC WebCam (1e4e:0102)
41 | #
42 | # Using the following option it is possible to add additional devices. If
43 | # your webcam happens to show above symptoms, try determining your cam's
44 | # vendor and product id via lsusb, activating the line below by removing # and 
45 | # adding it, e.g. for two broken cameras "aabb:ccdd" and "aabb:eeff"
46 | #
47 | #   additional_brokenfps_usb_devices=("aabb:ccdd" "aabb:eeff")
48 | #
49 | #
50 | #additional_brokenfps_usb_devices=()
51 | 
52 | ### Additional options to supply to MJPG Streamer for the RasPi Cam
53 | #
54 | # See https://faq.octoprint.org/mjpg-streamer-config for available options
55 | #
56 | # Defaults to 10fps
57 | #
58 | #camera_raspi_options="-fps 10"
59 | 
60 | ### Configuration of camera HTTP output
61 | #
62 | # Usually you should NOT need to change this at all! Only touch if you
63 | # know what you are doing and what the parameters mean.
64 | #
65 | # Below settings are used in the mjpg-streamer call like this:
66 | #
67 | #   -o "output_http.so -w $camera_http_webroot $camera_http_options"
68 | #
69 | # Current working directory is the mjpg-streamer base directory.
70 | #
71 | #camera_http_webroot="./www-mainsail"
72 | #camera_http_options="-n"
73 | 
74 | ### EXPERIMENTAL
75 | # Support for different streamer types.
76 | #
77 | # Available options:
78 | #   mjpeg [default] - stable MJPG-streamer
79 | #camera_streamer=mjpeg
80 | 


--------------------------------------------------------------------------------
/resources/mjpg-streamer/webcamd.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=Starts mjpg-streamer on startup
 3 | After=network.target
 4 | 
 5 | [Install]
 6 | WantedBy=multi-user.target
 7 | 
 8 | [Service]
 9 | Type=forking
10 | User=%USER%
11 | WorkingDirectory=/usr/local/bin
12 | StandardOutput=append:/var/log/webcamd.log
13 | StandardError=append:/var/log/webcamd.log
14 | ExecStart=/usr/local/bin/webcamd
15 | Restart=always


--------------------------------------------------------------------------------
/resources/moonraker-telegram-bot.env:
--------------------------------------------------------------------------------
1 | TELEGRAM_BOT_ARGS="%TELEGRAM_BOT_DIR%/bot/main.py -c %CFG% -l %LOG%"


--------------------------------------------------------------------------------
/resources/moonraker-telegram-bot.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=Moonraker Telegram Bot SV1 %INST%
 3 | Documentation=https://github.com/nlef/moonraker-telegram-bot/wiki
 4 | After=network-online.target
 5 | 
 6 | [Install]
 7 | WantedBy=multi-user.target
 8 | 
 9 | [Service]
10 | Type=simple
11 | User=%USER%
12 | WorkingDirectory=%TELEGRAM_BOT_DIR%
13 | EnvironmentFile=%ENV_FILE%
14 | ExecStart=%ENV%/bin/python $TELEGRAM_BOT_ARGS
15 | Restart=always
16 | RestartSec=10
17 | 


--------------------------------------------------------------------------------
/resources/moonraker.conf:
--------------------------------------------------------------------------------
 1 | [server]
 2 | host: 0.0.0.0
 3 | port: %PORT%
 4 | klippy_uds_address: %UDS%
 5 | 
 6 | [authorization]
 7 | trusted_clients:
 8 |     %LAN%
 9 |     10.0.0.0/8
10 |     127.0.0.0/8
11 |     169.254.0.0/16
12 |     172.16.0.0/12
13 |     192.168.0.0/16
14 |     FE80::/10
15 |     ::1/128
16 | cors_domains:
17 |     *.lan
18 |     *.local
19 |     *.internal
20 |     *://localhost
21 |     *://localhost:*
22 |     *://my.mainsail.xyz
23 |     *://app.fluidd.xyz
24 | 
25 | [octoprint_compat]
26 | 
27 | [history]
28 | 
29 | [update_manager]
30 | channel: dev
31 | refresh_interval: 168
32 | 


--------------------------------------------------------------------------------
/resources/moonraker.env:
--------------------------------------------------------------------------------
1 | MOONRAKER_ARGS="%MOONRAKER_DIR%/moonraker/moonraker.py -d %PRINTER_DATA%"


--------------------------------------------------------------------------------
/resources/moonraker.service:
--------------------------------------------------------------------------------
 1 | [Unit]
 2 | Description=API Server for Klipper SV1 %INST%
 3 | Documentation=https://moonraker.readthedocs.io/
 4 | Requires=network-online.target
 5 | After=network-online.target
 6 | 
 7 | [Install]
 8 | WantedBy=multi-user.target
 9 | 
10 | [Service]
11 | Type=simple
12 | User=%USER%
13 | SupplementaryGroups=moonraker-admin
14 | RemainAfterExit=yes
15 | WorkingDirectory=%MOONRAKER_DIR%
16 | EnvironmentFile=%ENV_FILE%
17 | ExecStart=%ENV%/bin/python $MOONRAKER_ARGS
18 | Restart=always
19 | RestartSec=10


--------------------------------------------------------------------------------
/resources/screenshots/kiauh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/resources/screenshots/kiauh.png


--------------------------------------------------------------------------------
/resources/screenshots/rpi_imager1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/resources/screenshots/rpi_imager1.png


--------------------------------------------------------------------------------
/resources/screenshots/rpi_imager2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dw-0/kiauh/33113e72e9a45ac1a4020d409b5932e60e4c77eb/resources/screenshots/rpi_imager2.png


--------------------------------------------------------------------------------
/resources/shell_command.cfg:
--------------------------------------------------------------------------------
1 | [gcode_shell_command hello_world]
2 | command: echo hello world
3 | timeout: 2.
4 | verbose: True
5 | [gcode_macro HELLO_WORLD]
6 | gcode:
7 |     RUN_SHELL_COMMAND CMD=hello_world


--------------------------------------------------------------------------------
/resources/upstreams.conf:
--------------------------------------------------------------------------------
 1 | # /etc/nginx/conf.d/upstreams.conf
 2 | upstream apiserver {
 3 |     ip_hash;
 4 |     server 127.0.0.1:7125;
 5 | }
 6 | 
 7 | upstream mjpgstreamer1 {
 8 |     ip_hash;
 9 |     server 127.0.0.1:8080;
10 | }
11 | 
12 | upstream mjpgstreamer2 {
13 |     ip_hash;
14 |     server 127.0.0.1:8081;
15 | }
16 | 
17 | upstream mjpgstreamer3 {
18 |     ip_hash;
19 |     server 127.0.0.1:8082;
20 | }
21 | 
22 | upstream mjpgstreamer4 {
23 |     ip_hash;
24 |     server 127.0.0.1:8083;
25 | }


--------------------------------------------------------------------------------
/scripts/globals.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | # shellcheck disable=SC2034
13 | set -e
14 | 
15 | function set_globals() {
16 |   #=================== SYSTEM ===================#
17 |   SYSTEMD="/etc/systemd/system"
18 |   INITD="/etc/init.d"
19 |   ETCDEF="/etc/default"
20 | 
21 |   #=================== KIAUH ====================#
22 |   green=$(echo -en "\e[92m")
23 |   yellow=$(echo -en "\e[93m")
24 |   magenta=$(echo -en "\e[35m")
25 |   red=$(echo -en "\e[91m")
26 |   cyan=$(echo -en "\e[96m")
27 |   white=$(echo -en "\e[39m")
28 |   INI_FILE="${HOME}/.kiauh.ini"
29 |   LOGFILE="/tmp/kiauh.log"
30 |   RESOURCES="${KIAUH_SRCDIR}/resources"
31 |   BACKUP_DIR="${HOME}/kiauh-backups"
32 | 
33 |   #================== KLIPPER ===================#
34 |   KLIPPY_ENV="${HOME}/klippy-env"
35 |   KLIPPER_DIR="${HOME}/klipper"
36 |   KLIPPER_REPO="https://github.com/Klipper3d/klipper.git"
37 | 
38 |   #================= MOONRAKER ==================#
39 |   MOONRAKER_ENV="${HOME}/moonraker-env"
40 |   MOONRAKER_DIR="${HOME}/moonraker"
41 |   MOONRAKER_REPO="https://github.com/Arksine/moonraker.git"
42 | 
43 |   #================= MAINSAIL ===================#
44 |   MAINSAIL_DIR="${HOME}/mainsail"
45 | 
46 |   #================== FLUIDD ====================#
47 |   FLUIDD_DIR="${HOME}/fluidd"
48 | 
49 |   #=============== KLIPPERSCREEN ================#
50 |   KLIPPERSCREEN_ENV="${HOME}/.KlipperScreen-env"
51 |   KLIPPERSCREEN_DIR="${HOME}/KlipperScreen"
52 |   KLIPPERSCREEN_REPO="https://github.com/jordanruthe/KlipperScreen.git"
53 | 
54 |   #========== MOONRAKER-TELEGRAM-BOT ============#
55 |   TELEGRAM_BOT_ENV="${HOME}/moonraker-telegram-bot-env"
56 |   TELEGRAM_BOT_DIR="${HOME}/moonraker-telegram-bot"
57 |   TELEGRAM_BOT_REPO="https://github.com/nlef/moonraker-telegram-bot.git"
58 | 
59 |   #=============== PRETTY-GCODE =================#
60 |   PGC_DIR="${HOME}/pgcode"
61 |   PGC_REPO="https://github.com/Kragrathea/pgcode"
62 | 
63 |   #================== NGINX =====================#
64 |   NGINX_SA="/etc/nginx/sites-available"
65 |   NGINX_SE="/etc/nginx/sites-enabled"
66 |   NGINX_CONFD="/etc/nginx/conf.d"
67 | 
68 |   #=============== MOONRAKER-OBICO ================#
69 |   MOONRAKER_OBICO_DIR="${HOME}/moonraker-obico"
70 |   MOONRAKER_OBICO_REPO="https://github.com/TheSpaghettiDetective/moonraker-obico.git"
71 | 
72 |   #=============== OCTOEVERYWHERE ================#
73 |   OCTOEVERYWHERE_ENV="${HOME}/octoeverywhere-env"
74 |   OCTOEVERYWHERE_DIR="${HOME}/octoeverywhere"
75 |   OCTOEVERYWHERE_REPO="https://github.com/QuinnDamerell/OctoPrint-OctoEverywhere.git"
76 | 
77 |   #=============== Crowsnest ================#
78 |   CROWSNEST_DIR="${HOME}/crowsnest"
79 |   CROWSNEST_REPO="https://github.com/mainsail-crew/crowsnest.git"
80 | 
81 |   #=============== Mobileraker ================#
82 |   MOBILERAKER_ENV="${HOME}/mobileraker-env"
83 |   MOBILERAKER_DIR="${HOME}/mobileraker_companion"
84 |   MOBILERAKER_REPO="https://github.com/Clon1998/mobileraker_companion.git"
85 | 
86 |   #=============== OCTOAPP ================#
87 |   OCTOAPP_ENV="${HOME}/octoapp-env"
88 |   OCTOAPP_DIR="${HOME}/octoapp"
89 |   OCTOAPP_REPO="https://github.com/crysxd/OctoApp-Plugin.git"
90 | 
91 |   #=============== Spoolman ================#
92 |   SPOOLMAN_DIR="${HOME}/Spoolman"
93 |   SPOOLMAN_DB_DIR="${HOME}/.local/share/spoolman"
94 |   SPOOLMAN_REPO="https://api.github.com/repos/Donkie/Spoolman/releases/latest"
95 | }
96 | 


--------------------------------------------------------------------------------
/scripts/rollback.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | set -e
13 | 
14 | function rollback_menu() {
15 |   top_border
16 |   echo -e "|     $(title_msg "~~~~~~~~~~~~~ [ Rollback Menu ] ~~~~~~~~~~~~~")     |"
17 |   hr
18 |   echo -e "| If serious errors occured after updating Klipper or   |"
19 |   echo -e "| Moonraker, you can use this menu to try and reset the |"
20 |   echo -e "| repository to an earlier state.                       |"
21 |   hr
22 |   echo -e "| 1) Rollback Klipper                                   |"
23 |   echo -e "| 2) Rollback Moonraker                                 |"
24 |   back_footer
25 | 
26 |   local action
27 |   while true; do
28 |     read -p "${cyan}###### Perform action:${white} " action
29 |     case "${action}" in
30 |       1)
31 |         select_msg "Klipper"
32 |         rollback_component "klipper"
33 |         break;;
34 |       2)
35 |         select_msg "Moonraker"
36 |         rollback_component "moonraker"
37 |         break;;
38 |       B|b)
39 |         clear; advanced_menu; break;;
40 |       *)
41 |         error_msg "Invalid command!";;
42 |     esac
43 |   done
44 | }
45 | 
46 | function rollback_component() {
47 |   local component=${1}
48 | 
49 |   if [[ ! -d "${HOME}/${component}" ]]; then
50 |     print_error "Rollback not possible! Missing installation?"
51 |     return
52 |   fi
53 | 
54 |   echo
55 |   top_border
56 |   echo -e "| Please select how many commits you want to revert.    |"
57 |   echo -e "| Consider using the information provided by the GitHub |"
58 |   echo -e "| commit history to decide how many commits to revert.  |"
59 |   blank_line
60 |   echo -e "| ${red}Warning:${white}                                              |"
61 |   echo -e "| ${red}Do not proceed if you are currently in the progress${white}   |"
62 |   echo -e "| ${red}of printing! Proceeding WILL terminate that print!${white}    |"
63 |   back_footer
64 | 
65 |   local count
66 |   while true; do
67 |     read -p "${cyan}###### Revert this amount of commits:${white} " count
68 |     if [[ -n ${count} ]] && (( count > 0 )); then
69 |       status_msg "Revert ${component^} by ${count} commits ..."
70 |       cd "${HOME}/${component}"
71 |       if git reset --hard HEAD~"${count}"; then
72 |         do_action_service "restart" "${component}"
73 |         print_confirm "${component^} was successfully reset!"
74 |       else
75 |         print_error "Reverting ${component^} failed! Please see the console output above."
76 |       fi
77 |       break
78 |     elif [[ ${count} == "B" || ${count} == "b" ]]; then
79 |       clear && print_header && break
80 |     else
81 |       error_msg "Invalid command!"
82 |     fi
83 |   done
84 |   rollback_menu
85 | }
86 | 


--------------------------------------------------------------------------------
/scripts/ui/advanced_menu.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | set -e
13 | 
14 | function advanced_ui() {
15 |   top_border
16 |   echo -e "|     ${yellow}~~~~~~~~~~~~~ [ Advanced Menu ] ~~~~~~~~~~~~~${white}     |"
17 |   hr
18 |   echo -e "| Klipper & API:          | Mainsail:                   |"
19 |   echo -e "|  1) [Rollback]          |  6) [Theme installer]       |"
20 |   echo -e "|                         |                             |"
21 |   echo -e "| Firmware:               | System:                     |"
22 |   echo -e "|  2) [Build only]        |  7) [Change hostname]       |"
23 |   echo -e "|  3) [Flash only]        |                             |"
24 |   echo -e "|  4) [Build + Flash]     | Extras:                     |"
25 |   echo -e "|  5) [Get MCU ID]        |  8) [G-Code Shell Command]  |"
26 |   back_footer
27 | }
28 | 
29 | function advanced_menu() {
30 |   do_action "" "advanced_ui"
31 | 
32 |   local action
33 |   while true; do
34 |     read -p "${cyan}####### Perform action:${white} " action
35 |     case "${action}" in
36 |       1)
37 |         do_action "rollback_menu" "advanced_menu";;
38 |       2)
39 |         do_action "build_fw" "advanced_ui";;
40 |       3)
41 |         clear && print_header
42 |         do_action "init_flash_process" "advanced_ui";;
43 |       4)
44 |         clear && print_header
45 |         status_msg "Please wait..."
46 |         build_fw && init_flash_process
47 |         advanced_ui;;
48 |       5)
49 |         clear && print_header
50 |         select_mcu_connection
51 |         print_detected_mcu_to_screen
52 |         advanced_ui;;
53 |       6)
54 |         do_action "ms_theme_installer_menu";;
55 |       7)
56 |         clear
57 |         print_header
58 |         set_custom_hostname
59 |         advanced_ui;;
60 |       8)
61 |         do_action "setup_gcode_shell_command" "advanced_ui";;
62 |       B|b)
63 |         clear; main_menu; break;;
64 |       *)
65 |         deny_action "advanced_ui";;
66 |     esac
67 |   done
68 |   advanced_menu
69 | }
70 | 


--------------------------------------------------------------------------------
/scripts/ui/backup_menu.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | set -e
13 | 
14 | function backup_ui() {
15 |   top_border
16 |   echo -e "|     $(title_msg "~~~~~~~~~~~~~~ [ Backup Menu ] ~~~~~~~~~~~~~~")     |"
17 |   hr
18 |   echo -e "| ${yellow}INFO: Backups are located in '~/kiauh-backups'${white}        |"
19 |   hr
20 |   echo -e "| Klipper & API:             | Touchscreen GUI:         |"
21 |   echo -e "|  1) [Klipper]              |  7) [KlipperScreen]      |"
22 |   echo -e "|  2) [Moonraker]            |                          |"
23 |   echo -e "|  3) [Config Folder]        | 3rd Party Webinterface:  |"
24 |   echo -e "|  4) [Moonraker Database]   |  8) [OctoPrint]          |"
25 |   echo -e "|                            |                          |"
26 |   echo -e "| Klipper Webinterface:      | Other:                   |"
27 |   echo -e "|  5) [Mainsail]             |  9) [Telegram Bot]       |"
28 |   echo -e "|  6) [Fluidd]               | 10) [OctoEverywhere]     |"
29 |   echo -e "|                            | 11) [Spoolman]           |"
30 |   back_footer
31 | }
32 | 
33 | function backup_menu() {
34 |   do_action "" "backup_ui"
35 | 
36 |   local action
37 |   while true; do
38 |     read -p "${cyan}####### Perform action:${white} " action
39 |     case "${action}" in
40 |       1)
41 |         do_action "backup_klipper" "backup_ui";;
42 |       2)
43 |         do_action "backup_moonraker" "backup_ui";;
44 |       3)
45 |         do_action "backup_config_dir" "backup_ui";;
46 |       4)
47 |         do_action "backup_moonraker_database" "backup_ui";;
48 |       5)
49 |         do_action "backup_mainsail" "backup_ui";;
50 |       6)
51 |         do_action "backup_fluidd" "backup_ui";;
52 |       7)
53 |         do_action "backup_klipperscreen" "backup_ui";;
54 |       8)
55 |         do_action "backup_octoprint" "backup_ui";;
56 |       9)
57 |         do_action "backup_telegram_bot" "backup_ui";;
58 |       10)
59 |         do_action "backup_octoeverywhere" "backup_ui";;
60 |       11)
61 |         do_action "backup_spoolman" "backup_ui";;
62 |       B|b)
63 |         clear; main_menu; break;;
64 |       *)
65 |         deny_action "backup_ui";;
66 |     esac
67 |   done
68 |   backup_menu
69 | }
70 | 


--------------------------------------------------------------------------------
/scripts/ui/general_ui.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | set -e
13 | 
14 | #ui total width = 57 chars
15 | function top_border() {
16 |   echo -e "/=======================================================\\"
17 | }
18 | 
19 | function bottom_border() {
20 |   echo -e "\=======================================================/"
21 | }
22 | 
23 | function blank_line() {
24 |   echo -e "|                                                       |"
25 | }
26 | 
27 | function hr() {
28 |   echo -e "|-------------------------------------------------------|"
29 | }
30 | 
31 | function quit_footer() {
32 |   hr
33 |   echo -e "|                        ${red}Q) Quit${white}                        |"
34 |   bottom_border
35 | }
36 | 
37 | function back_footer() {
38 |   hr
39 |   echo -e "|                       ${green}B) « Back${white}                       |"
40 |   bottom_border
41 | }
42 | 
43 | function back_help_footer() {
44 |   hr
45 |   echo -e "|         ${green}B) « Back${white}         |        ${yellow}H) Help [?]${white}        |"
46 |   bottom_border
47 | }
48 | 
49 | function print_header() {
50 |   top_border
51 |   echo -e "|     $(title_msg "~~~~~~~~~~~~~~~~~ [ KIAUH ] ~~~~~~~~~~~~~~~~~")     |"
52 |   echo -e "|     $(title_msg "   Klipper Installation And Update Helper    ")     |"
53 |   echo -e "|     $(title_msg "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")     |"
54 |   bottom_border
55 | }
56 | 
57 | function do_action() {
58 |   clear && print_header
59 |   ### $1 is the action the user wants to fire
60 |   $1
61 | #  print_msg && clear_msg
62 |   ### $2 is the menu the user usually gets directed back to after an action is completed
63 |   $2
64 | }
65 | 
66 | function deny_action() {
67 |   clear && print_header
68 |   print_error "Invalid command!"
69 |   $1
70 | }
71 | 


--------------------------------------------------------------------------------
/scripts/ui/install_menu.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | set -e
13 | 
14 | function install_ui() {
15 |   top_border
16 |   echo -e "|     ${green}~~~~~~~~~~~ [ Installation Menu ] ~~~~~~~~~~~${white}     |"
17 |   hr
18 |   echo -e "|  You need this menu usually only for installing       |"
19 |   echo -e "|  all necessary dependencies for the various           |"
20 |   echo -e "|  functions on a completely fresh system.              |"
21 |   hr
22 |   echo -e "| Firmware & API:          | Other:                     |"
23 |   echo -e "|  1) [Klipper]            |  7) [PrettyGCode]          |"
24 |   echo -e "|  2) [Moonraker]          |  8) [Telegram Bot]         |"
25 |   echo -e "|                          |  9) $(obico_install_title) |"
26 |   echo -e "| Klipper Webinterface:    | 10) [OctoEverywhere]       |"
27 |   echo -e "|  3) [Mainsail]           | 11) [Mobileraker]          |"
28 |   echo -e "|  4) [Fluidd]             | 12) [OctoApp for Klipper]  |"
29 |   echo -e "|                          | 13) [Spoolman]             |"
30 |   echo -e "| Touchscreen GUI:         |                            |"
31 |   echo -e "|  5) [KlipperScreen]      | Webcam Streamer:           |"
32 |   echo -e "|                          | 14) [Crowsnest]            |"
33 |   echo -e "| 3rd Party Webinterface:  |                            |"
34 |   echo -e "|  6) [OctoPrint]          |                            |"
35 |   back_footer
36 | }
37 | 
38 | function install_menu() {
39 |   clear -x && sudo true && clear -x # (re)cache sudo credentials so password prompt doesn't bork ui
40 |   print_header
41 |   install_ui
42 | 
43 |   ### save all installed webinterface ports to the ini file
44 |   fetch_webui_ports
45 | 
46 |   ### save all klipper multi-instance names to the ini file
47 |   set_multi_instance_names
48 | 
49 |   local action
50 |   while true; do
51 |     read -p "${cyan}####### Perform action:${white} " action
52 |     case "${action}" in
53 |       1)
54 |         do_action "start_klipper_setup" "install_ui";;
55 |       2)
56 |         do_action "moonraker_setup_dialog" "install_ui";;
57 |       3)
58 |         do_action "install_mainsail" "install_ui";;
59 |       4)
60 |         do_action "install_fluidd" "install_ui";;
61 |       5)
62 |         do_action "install_klipperscreen" "install_ui";;
63 |       6)
64 |         do_action "octoprint_setup_dialog" "install_ui";;
65 |       7)
66 |         do_action "install_pgc_for_klipper" "install_ui";;
67 |       8)
68 |         do_action "telegram_bot_setup_dialog" "install_ui";;
69 |       9)
70 |         do_action "moonraker_obico_setup_dialog" "install_ui";;
71 |       10)
72 |         do_action "octoeverywhere_setup_dialog" "install_ui";;
73 |       11)
74 |         do_action "install_mobileraker" "install_ui";;
75 |       12)
76 |         do_action "octoapp_setup_dialog" "install_ui";;
77 |       13)
78 |         do_action "install_spoolman" "install_ui";;
79 |       14)
80 |         do_action "install_crowsnest" "install_ui";;
81 |       B|b)
82 |         clear; main_menu; break;;
83 |       *)
84 |         deny_action "install_ui";;
85 |     esac
86 |   done
87 |   install_menu
88 | }
89 | 


--------------------------------------------------------------------------------
/scripts/ui/remove_menu.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | 
 3 | #=======================================================================#
 4 | # Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com>       #
 5 | #                                                                       #
 6 | # This file is part of KIAUH - Klipper Installation And Update Helper   #
 7 | # https://github.com/dw-0/kiauh                                         #
 8 | #                                                                       #
 9 | # This file may be distributed under the terms of the GNU GPLv3 license #
10 | #=======================================================================#
11 | 
12 | set -e
13 | 
14 | function remove_ui() {
15 |   top_border
16 |   echo -e "|     ${red}~~~~~~~~~~~~~~ [ Remove Menu ] ~~~~~~~~~~~~~~${white}     |"
17 |   hr
18 |   echo -e "| ${yellow}INFO: Configurations and/or any backups will be kept!${white} |"
19 |   hr
20 |   echo -e "| Firmware & API:           | Webcam Streamer:          |"
21 |   echo -e "|  1) [Klipper]             |  9) [Crowsnest]           |"
22 |   echo -e "|  2) [Moonraker]           | 10) [MJPG-Streamer]       |"
23 |   echo -e "|                           |                           |"
24 |   echo -e "| Klipper Webinterface:     | Other:                    |"
25 |   echo -e "|  3) [Mainsail]            | 11) [PrettyGCode]         |"
26 |   echo -e "|  4) [Mainsail-Config]     | 12) [Telegram Bot]        |"
27 |   echo -e "|  5) [Fluidd]              | 13) [Obico for Klipper]   |"
28 |   echo -e "|  6) [Fluidd-Config]       | 14) [OctoEverywhere]      |"
29 |   echo -e "|                           | 15) [Mobileraker]         |"
30 |   echo -e "| Touchscreen GUI:          | 16) [NGINX]               |"
31 |   echo -e "|  7) [KlipperScreen]       | 17) [OctoApp]             |"
32 |   echo -e "|                           | 18) [Spoolman]            |"
33 |   echo -e "| 3rd Party Webinterface:   |                           |"
34 |   echo -e "|  8) [OctoPrint]           |                           |"
35 |   back_footer
36 | }
37 | 
38 | function remove_menu() {
39 |   do_action "" "remove_ui"
40 | 
41 |   local action
42 |   while true; do
43 |     read -p "${cyan}####### Perform action:${white} " action
44 |     case "${action}" in
45 |       1)
46 |         do_action "remove_klipper" "remove_ui";;
47 |       2)
48 |         do_action "remove_moonraker" "remove_ui";;
49 |       3)
50 |         do_action "remove_mainsail" "remove_ui";;
51 |       4)
52 |         do_action "remove_mainsail_config" "remove_ui";;
53 |       5)
54 |         do_action "remove_fluidd" "remove_ui";;
55 |       6)
56 |         do_action "remove_fluidd_config" "remove_ui";;
57 |       7)
58 |         do_action "remove_klipperscreen" "remove_ui";;
59 |       8)
60 |         do_action "remove_octoprint" "remove_ui";;
61 |       9)
62 |         do_action "remove_crowsnest" "remove_ui";;
63 |       10)
64 |         do_action "remove_mjpg-streamer" "remove_ui";;
65 |       11)
66 |         do_action "remove_prettygcode" "remove_ui";;
67 |       12)
68 |         do_action "remove_telegram_bot" "remove_ui";;
69 |       13)
70 |         do_action "remove_moonraker_obico" "remove_ui";;
71 |       14)
72 |         do_action "remove_octoeverywhere" "remove_ui";;
73 |       15)
74 |         do_action "remove_mobileraker" "remove_ui";;
75 |       16)
76 |         do_action "remove_nginx" "remove_ui";;
77 |       17)
78 |         do_action "remove_octoapp" "remove_ui";;
79 |       18)
80 |         do_action "remove_spoolman" "remove_ui";;
81 |       B|b)
82 |         clear; main_menu; break;;
83 |       *)
84 |         deny_action "remove_ui";;
85 |     esac
86 |   done
87 |   remove_menu
88 | }
89 | 


--------------------------------------------------------------------------------