diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..4ff871410
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# Funding
+patreon: meteyou
+ko_fi: mainsail
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 000000000..c7032b913
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,42 @@
+name: Bug report
+description: Create a report to help us improve
+labels: ["bug", "triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ This issue form is for reporting bugs only!
+ If you have a feature request, please use [feature_request](/new?template=feature_request.yml)
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: What happened
+ description: >-
+ A clear and concise description of what the bug is.
+ validations:
+ required: true
+ - type: textarea
+ id: expected-behavior
+ attributes:
+ label: What did you expect to happen
+ description: >-
+ A clear and concise description of what you expected to happen.
+ validations:
+ required: true
+ - type: textarea
+ id: repro-steps
+ attributes:
+ label: How to reproduce
+ description: >-
+ Minimal and precise steps to reproduce this bug.
+ validations:
+ required: true
+ - type: textarea
+ id: additional-info
+ attributes:
+ label: Additional information
+ description: |
+ If you have any additional information for us, use the field below.
+
+ Please note, you can attach logs, screenshots or screen recordings here, by
+ dragging and dropping files in the field below.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..d75ce0ea9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Mainsail Discord
+ url: https://discord.gg/skWTwTD
+ about: Quickest way to get in contact
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 000000000..19e3a4e87
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,40 @@
+name: Feature request
+description: Suggest an idea for this project
+labels: ["feature request"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ This issue form is for feature requests only!
+ If you've found a bug, please use [bug_report](/new?template=bug_report.yml)
+ - type: textarea
+ id: problem-description
+ attributes:
+ label: Is your feature request related to a problem? Please describe
+ description: >-
+ A clear and concise description of what the problem is.
+ validations:
+ required: true
+ - type: textarea
+ id: solution-description
+ attributes:
+ label: Describe the solution you'd like
+ description: >-
+ A clear and concise description of what you want to happen.
+ validations:
+ required: true
+ - type: textarea
+ id: possible-alternatives
+ attributes:
+ label: Describe alternatives you've considered
+ description: >-
+ A clear and concise description of any alternative solutions or features you've considered.
+ - type: textarea
+ id: additional-info
+ attributes:
+ label: Additional information
+ description: |
+ If you have any additional information for us, use the field below.
+
+ Please note, you can attach logs, screenshots or screen recordings here, by
+ dragging and dropping files in the field below.
diff --git a/.github/sdcard-logo.png b/.github/sdcard-logo.png
new file mode 100644
index 000000000..0934c25d2
Binary files /dev/null and b/.github/sdcard-logo.png differ
diff --git a/README.md b/README.md
index 218593511..bedcb0b83 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,32 @@
![downloads](https://img.shields.io/github/downloads/mainsail-crew/MainsailOS/total)
-[![discord](https://img.shields.io/discord/758059413700345988?color=%235865F2&label=discord&logo=discord&logoColor=white&style=flat)](https://discord.gg/skWTwTD)
+[![discord](https://img.shields.io/discord/758059413700345988?color=%235865F2&label=discord&logo=discord&logoColor=white&style=flat)](https://discord.gg/mainsail)
+
+
+
+
# MainsailOS
-![Mainsail Logo](https://github.com/meteyou/mainsail/raw/master/docs/assets/img/logo.png?raw=true)
+A [Raspberry Pi OS](https://www.raspberrypi.org/software/) based distribution for 3D Printers. \
+It includes everything to get started with Klipper Firmware and Mainsail.
+
+Learn more about:
-A [Raspberry Pi OS](https://www.raspberrypi.org/software/) based distribution for 3d Printers. It includes everything to get started with Klipper Firmware and Mainsail.
+- [Klipper Firmware](https://www.klipper3d.org/)
+- [Moonraker](https://moonraker.readthedocs.io/en/latest/)
+- [Mainsail](https://docs.mainsail.xyz/)
-Learn More about...
-Klipper Firmware: https://www.klipper3d.org/
-Moonraker: https://moonraker.readthedocs.io/en/latest/
-Mainsail: https://docs.mainsail.xyz/
+## How to install MainsailOS ?
-## Installing
+You can find detailed instructions in our [documentation](https://docs.mainsail.xyz/setup/mainsail-os).
-See the installation instructions on the Mainsail documentation page:
-https://docs.mainsail.xyz/setup/mainsail-os
+We recommend the installation via [Raspberry Pi Imager](https://docs.mainsail.xyz/setup/mainsailos/pi-imager).
## Community
-For additional help, join us in the [Mainsail Discord](https://discord.gg/skWTwTD).
+For additional help, join us on [Discord](https://discord.gg/mainsail).
-[![discord](https://img.shields.io/discord/758059413700345988?color=%235865F2&label=discord&logo=discord&logoColor=white&style=flat)](https://discord.gg/skWTwTD)
+[![discord](https://img.shields.io/discord/758059413700345988?color=%235865F2&label=discord&logo=discord&logoColor=white&style=flat)](https://discord.gg/mainsail)
## What is included?
@@ -30,30 +35,30 @@ Here a list of included and preinstalled Software:
- [Klipper (3D Printer Firmware)](https://github.com/KevinOConnor/klipper)
- [Moonraker (API Web Server for Klipper)](https://github.com/Arksine/moonraker)
- [Mainsail (Web interface for Klipper/Moonraker)](https://github.com/meteyou/mainsail)
-- [Mjpeg-streamer (Webcam streaming)](https://github.com/jacksonliam/mjpg-streamer)
+- [Crowsnest (Webcam streaming)](https://github.com/mainsail-crew/crowsnest)
+- [Sonar (Keepalive daemon)](https://github.com/mainsail-crew/sonar)
- [Nginx (Webserver & Proxy)](https://nginx.org/en/)
-- [FFMPEG (upcoming features)](https://www.ffmpeg.org/)
+
+## also includes
+
+- Enabled Serial Connection by default. \
+ Using Hardware UART (PL011) for Boards like BTT SKR Mini E3 V3
+- Preinstalled Dependencies for Klipper's Input Shaper. \
+ You only need to build the [klipper_mcu](https://www.klipper3d.org/RPi_microcontroller.html) and installing the service. \
+ See [Klipper documentation](https://www.klipper3d.org/Measuring_Resonances.html) for more information.
+- Preinstalled python3-serial package, needed for [CanBoot](https://github.com/Arksine/CanBoot)
## Screenshots
-![screenshot-dashboard](https://raw.githubusercontent.com/mainsail-crew/docs/master/assets/img/screenshot.png)
+![screenshot-dashboard](https://github.com/mainsail-crew/docs/raw/master/assets/img/screenshot.png)
# Build your own / Developing
To prevent you have to deal with an entire build chain setup, \
simply fork this repository.
-Enable the workflows in your fork and you are good to go.
-On every push you make, it will build an Image and upload it as artifact.
-
-If you want or need to build locally please visit [CustomPiOS](https://github.com/guysoft/CustomPiOS)
-Especcially ["Build a Distro From within Raspbian / Debian / Ubuntu / CustomPiOS Distros"](https://github.com/guysoft/CustomPiOS#build-a-distro-from-within-raspbian--debian--ubuntu--custompios-distros)
-
----
-
-## Credits
+Enable the workflows in your fork and you are good to go. \
+On each push you make, an image is build and uploaded as an artifact.
-We want to give a shoutout to [jottr](https://github.com/jottr) for mentioning a naming convention problem.\
-In the past we used 'raspbian' at some points for names.\
-But this isn't technicaly true.\
-MainsailOS is based of Raspberry Pi OS, not raspbian.
+If you want or need to build locally please visit [CustomPiOS](https://github.com/guysoft/CustomPiOS). \
+Especially ["Build a Distro From within Raspbian / Debian / Ubuntu / CustomPiOS Distros"](https://github.com/guysoft/CustomPiOS#build-a-distro-from-within-raspbian--debian--ubuntu--custompios-distros)
diff --git a/src/config b/src/config
index 47c414659..ea9e7bc4b 100644
--- a/src/config
+++ b/src/config
@@ -2,8 +2,8 @@
# Shebang for better detection of file type
export DIST_NAME=MainsailOS
-export DIST_VERSION=0.6.1
+export DIST_VERSION=0.7.0
export BASE_IMAGE_ENLARGEROOT=2500
-export MODULES="base,pkgupgrade,mainsailos(network,piconfig,klipper,is_req_preinstall,moonraker,mainsail,mjpgstreamer,serialcomm,password-for-sudo)"
+export MODULES="base,pkgupgrade,mainsailos(network,piconfig,klipper,is_req_preinstall,moonraker,timelapse,mainsail,crowsnest,sonar,password-for-sudo),postrename"
export BASE_RELEASE_COMPRESS=yes
export BASE_IMAGE_RESIZEROOT=600
diff --git a/src/modules/crowsnest/config b/src/modules/crowsnest/config
new file mode 100644
index 000000000..d12c02d7e
--- /dev/null
+++ b/src/modules/crowsnest/config
@@ -0,0 +1,28 @@
+#!/bin/bash
+#### crowsnest - A webcam Service for multiple Cams and Stream Services.
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2021
+#### https://github.com/mainsail-crew/crowsnest
+####
+#### This File is distributed under GPLv3
+####
+# shellcheck disable=all
+
+# crowsnest
+[ -n "$CROWSNEST_CROWSNEST_REPO_SHIP" ] || CROWSNEST_CROWSNEST_REPO_SHIP=https://github.com/mainsail-crew/crowsnest.git
+[ -n "$CROWSNEST_CROWSNEST_REPO_BRANCH" ] || CROWSNEST_CROWSNEST_REPO_BRANCH=master
+[ -n "$CROWSNEST_CROWSNEST_DEPS" ] || CROWSNEST_CROWSNEST_DEPS="git crudini bsdutils findutils v4l-utils ffmpeg"
+[ -n "$CROWSNEST_DEFAULT_CONF" ] || CROWSNEST_DEFAULT_CONF="mainsail_default.conf"
+[ -n "$CROWSNEST_DEFAULT_CONF_DIR" ] || CROWSNEST_DEFAULT_CONF_DIR="/home/${BASE_USER}/klipper_config"
+# Force Raspicam fix bool (1:yes / 0:no)
+[ -n "$CROWSNEST_FORCE_RASPICAMFIX" ] || CROWSNEST_FORCE_RASPICAMFIX=1
+# Add Crowsnest to moonraker.conf (update manager) bool (1:yes / 0:no)
+[ -n "$CROWSNEST_ADD_CROWSNEST_MOONRAKER" ] || CROWSNEST_ADD_CROWSNEST_MOONRAKER=1
+
+
+# ustreamer
+[ -n "$CROWSNEST_USTREAMER_DEPS" ] || CROWSNEST_USTREAMER_DEPS="git build-essential libevent-dev libjpeg-dev \
+libbsd-dev libraspberrypi-dev libgpiod-dev"
+[ -n "$CROWSNEST_USTREAMER_WITH_OMX" ] || CROWSNEST_USTREAMER_WITH_OMX="y"
+[ -n "$CROWSNEST_USTREAMER_WITH_GPIO" ] || CROWSNEST_USTREAMER_WITH_GPIO="n"
diff --git a/src/modules/crowsnest/start_chroot_script b/src/modules/crowsnest/start_chroot_script
new file mode 100644
index 000000000..9f75b2c6d
--- /dev/null
+++ b/src/modules/crowsnest/start_chroot_script
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+#### crowsnest - A webcam Service for multiple Cams and Stream Services.
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2021
+#### https://github.com/mainsail-crew/crowsnest
+####
+#### This File is distributed under GPLv3
+####
+
+# shellcheck disable=all
+
+# Error handling
+set -Ee
+
+source /common.sh
+install_cleanup_trap
+
+echo_green "Installing crowsnest and enable webcam Service ..."
+# install dependencies
+# force apt update
+apt update
+# It could use inbuilt dependencie check, but should speed up if preinstalled.
+# shellcheck disable=SC2086
+check_install_pkgs ${CROWSNEST_CROWSNEST_DEPS} ${CROWSNEST_USTREAMER_DEPS}
+# Move to $HOME dir
+pushd "/home/${BASE_USER}" &> /dev/null || exit 1
+ # make sure config folder exist
+ if [ ! -d "${CROWSNEST_DEFAULT_CONF_DIR}" ]; then
+ sudo -u "${BASE_USER}" mkdir -p "${CROWSNEST_DEFAULT_CONF_DIR}"
+ fi
+ # clone Repo
+ echo_green "Clone crowsnest repository ..."
+ gitclone CROWSNEST_CROWSNEST_REPO crowsnest
+ # install crowsnest - use crowsnest's make unattended
+ pushd "/home/${BASE_USER}/crowsnest" &> /dev/null || exit 1
+ echo_green "Launch crowsnest install routine ..."
+ pushd "/home/${BASE_USER}/crowsnest" &> /dev/null || exit 1
+ sudo -u "${BASE_USER}" make unattended
+ # Apply Raspicam fix if enabled.
+ if [ "${CROWSNEST_FORCE_RASPICAMFIX}" == "1" ]; then
+ echo -en "Applying Raspicam Fix ... \r"
+ sudo sh -c 'echo "bcm2835-v4l2" >> /etc/modules'
+ sudo cp file_templates/bcm2835-v4l2.conf /etc/modprobe.d/
+ echo -e "Applying Raspicam Fix ... [OK]"
+ fi
+ popd &> /dev/null || exit 1
+popd &> /dev/null || exit 1
diff --git a/src/modules/is_req_preinstall/config b/src/modules/is_req_preinstall/config
index 8c44ef35d..ab8e7aa6e 100644
--- a/src/modules/is_req_preinstall/config
+++ b/src/modules/is_req_preinstall/config
@@ -1,4 +1,6 @@
+#!/bin/bash
+# shellcheck disable=all
[ -n "$IS_REQ_PREINSTALL_VENV_DIR" ] || IS_REQ_PREINSTALL_VENV_DIR=/home/${BASE_USER}/klippy-env
[ -n "$IS_REQ_PREINSTALL_DEPS" ] || IS_REQ_PREINSTALL_DEPS="python3-numpy python3-matplotlib"
-[ -n "$IS_REQ_PREINSTALL_PIP" ] || IS_REQ_PREINSTALL_PIP="numpy"
-[ -n "$IS_REQ_PREINSTALL_CFG_FILE" ] || IS_REQ_PREINSTALL_CFG_FILE="/boot/config.txt"
\ No newline at end of file
+[ -n "$IS_REQ_PREINSTALL_PIP" ] || IS_REQ_PREINSTALL_PIP="numpy<=1.21.4"
+[ -n "$IS_REQ_PREINSTALL_CFG_FILE" ] || IS_REQ_PREINSTALL_CFG_FILE="/boot/config.txt"
diff --git a/src/modules/is_req_preinstall/start_chroot_script b/src/modules/is_req_preinstall/start_chroot_script
index a5a6effc9..fde8d9285 100644
--- a/src/modules/is_req_preinstall/start_chroot_script
+++ b/src/modules/is_req_preinstall/start_chroot_script
@@ -1,59 +1,61 @@
#!/usr/bin/env bash
-#
-# Copyright 2021 by Stephan Wendel aka KwadFan
-#
-# This file may distributed under GPLv3
-########
-
-### For easier maintainability look to klipper/config.
+#### MainsailOS Build Chain
+####
+#### Klipper Install Module
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2021 - 2022
+#### https://github.com/mainsail-crew/MainsailOS
+####
+#### This File is distributed under GPLv3
+####
+
+# shellcheck enable=require-variable-braces
## Source error handling, leave this in place
-set -xe
+set -Eex
+
+# Set LC_ALL to prevent errors
+export LC_ALL=C
+
+# Set DEBIAN_FRONTEND to noninteractive
+if [ "${DEBIAN_FRONTEND}" != "noninteractive" ]; then
+ export DEBIAN_FRONTEND=noninteractive
+fi
# Source CustomPIOS common.sh
+# shellcheck disable=SC1091
source /common.sh
install_cleanup_trap
-echo_green "Input Shaper Requirements preinstall"
+echo_green "Install Input Shaper requirements ..."
### install all deps at once for time consumption reasons.
## APT: Update Repo Database and install Dependencies
-
+# Force apt update
+apt update
echo_green "Installing Input Shaper Dependencies ..."
-apt update && apt install ${IS_REQ_PREINSTALL_DEPS} -y
-
-### numpy Parallelizing
-function max_cores() {
- local core_count
- core_count="$(nproc)"
- if [ "${core_count}" -lt 3 ]; then
- echo "$core_count"
- else
- echo "$(("${core_count}" -1))"
- fi
-}
-
-echo_green "System has $(nproc) Cores available."
-echo_green "Using $(max_cores)..."
-export NPY_NUM_BUILD_JOBS="$(max_cores)"
-
+# shellcheck disable=SC2086
+# Disabling shellcheck SC2086, because we want "wordsplitting"
+check_install_pkgs ${IS_REQ_PREINSTALL_DEPS}
### Check for Klipper Venv installed.
-pushd /home/${BASE_USER}
+pushd "/home/${BASE_USER}" &> /dev/null || exit 1
if [ -d "${IS_REQ_PREINSTALL_VENV_DIR}" ] && [ -x "${IS_REQ_PREINSTALL_VENV_DIR}/bin/pip" ]; then
echo_green "Installing numpy..."
- sudo -u ${BASE_USER} ${IS_REQ_PREINSTALL_VENV_DIR}/bin/pip install -v "${IS_REQ_PREINSTALL_PIP}"
+ sudo -u "${BASE_USER}" "${IS_REQ_PREINSTALL_VENV_DIR}"/bin/pip install -v "${IS_REQ_PREINSTALL_PIP}"
else
echo_red "Klipper Venv not found!"
return 1
fi
## Cleanup
-sudo -u ${BASE_USER} rm -rf /home/${BASE_USER}/.cache
-popd
+echo_green "Cleanup ..."
+sudo -u "${BASE_USER}" rm -rf "/home/${BASE_USER}/.cache"
+popd &> /dev/null || exit 1
## Enable spi interface by default
echo_green "Enabling SPI Interface..."
-sed -i 's/#dtparam=spi=on/dtparam=spi=on/' ${IS_REQ_PREINSTALL_CFG_FILE}
+sed -i 's/#dtparam=spi=on/dtparam=spi=on/' "${IS_REQ_PREINSTALL_CFG_FILE}"
-### EOF
\ No newline at end of file
+echo_green "Install Input Shaper requirements ... done!"
diff --git a/src/modules/klipper/config b/src/modules/klipper/config
index 2df7b914c..b4e3b072d 100644
--- a/src/modules/klipper/config
+++ b/src/modules/klipper/config
@@ -1,10 +1,12 @@
+#!/bin/bash
+# shellcheck disable=all
[ -n "$KLIPPER_SRC_DIR" ] || KLIPPER_SRC_DIR=/home/${BASE_USER}/klipper
[ -n "$KLIPPER_PYTHON_DIR" ] || KLIPPER_PYTHON_DIR=/home/${BASE_USER}/klippy-env
[ -n "$KLIPPER_REPO_SHIP" ] || KLIPPER_REPO_SHIP=https://github.com/Klipper3d/klipper.git
[ -n "$KLIPPER_REPO_BRANCH" ] || KLIPPER_REPO_BRANCH=master
[ -n "$KLIPPER_DEPS" ] || KLIPPER_DEPS="wget git gpiod \
-virtualenv python-dev python-matplotlib \
+virtualenv python3-dev python3-matplotlib \
libffi-dev build-essential \
libncurses-dev libusb-dev \
avrdude gcc-avr binutils-avr avr-libc \
@@ -12,4 +14,4 @@ stm32flash dfu-util libnewlib-arm-none-eabi \
gcc-arm-none-eabi binutils-arm-none-eabi libusb-1.0"
[ -n "$KLIPPER_USER_GROUPS" ] || KLIPPER_USER_GROUPS=tty,dialout
[ -n "$KLIPPER_USER_DIRS" ] || KLIPPER_USER_DIRS="klipper_config klipper_logs gcode_files"
-[ -n "$KLIPPER_PYENV_REQ" ] || KLIPPER_PYENV_REQ=scripts/klippy-requirements.txt
\ No newline at end of file
+[ -n "$KLIPPER_PYENV_REQ" ] || KLIPPER_PYENV_REQ=scripts/klippy-requirements.txt
diff --git a/src/modules/klipper/filesystem/root/etc/systemd/system/klipper.service b/src/modules/klipper/filesystem/root/etc/systemd/system/klipper.service
index 5591a27c0..d8dec38ba 100644
--- a/src/modules/klipper/filesystem/root/etc/systemd/system/klipper.service
+++ b/src/modules/klipper/filesystem/root/etc/systemd/system/klipper.service
@@ -3,7 +3,7 @@
[Unit]
Description=Starts Klipper and provides klippy Unix Domain Socket API
Documentation=https://www.klipper3d.org/
-After=network.target
+After=network-online.target
Before=moonraker.service
Wants=udev.target
@@ -21,4 +21,4 @@ User=pi
RemainAfterExit=yes
ExecStart= /home/pi/klippy-env/bin/python /home/pi/klipper/klippy/klippy.py ${KLIPPER_CONFIG} -l ${KLIPPER_LOG} -a ${KLIPPER_SOCKET}
Restart=always
-RestartSec=10
\ No newline at end of file
+RestartSec=10
diff --git a/src/modules/klipper/start_chroot_script b/src/modules/klipper/start_chroot_script
index af4d408d8..02cfeae12 100644
--- a/src/modules/klipper/start_chroot_script
+++ b/src/modules/klipper/start_chroot_script
@@ -1,60 +1,83 @@
#!/usr/bin/env bash
-# Klipper install script
-# Script that installs Klipper
-# Written by Raymond Himle and Stefan Dej
-# Revamped by KwadFan
-# Thanks to KevinOConnor
-# GPL V3
-########
+#### MainsailOS Build Chain
+####
+#### Klipper Install Module
+####
+#### Based on work done by Raymond Himle and Stefan Dej
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2022
+#### https://github.com/mainsail-crew/MainsailOS
+####
+#### This File is distributed under GPLv3
+####
+
+# shellcheck enable=require-variable-braces
### For easier maintainability look to klipper/config.
## Source error handling, leave this in place
-set -xe
+set -Eex
+
+## Set LC_ALL to prevent errors
+export LC_ALL=C
+
+# Set DEBIAN_FRONTEND to noninteractive
+if [ "${DEBIAN_FRONTEND}" != "noninteractive" ]; then
+ export DEBIAN_FRONTEND=noninteractive
+fi
-# Source CustomPIOS common.sh
+## Source CustomPIOS common.sh
+# shellcheck disable=SC1091
source /common.sh
install_cleanup_trap
unpack /filesystem/root /
-echo_green "Installing Klipper and enable klippy Service"
+echo_green "Installing Klipper and enable klippy Service ..."
-### install all deps at once for time consumption reasons.
+## Install all deps at once for time consumption reasons.
## APT: Update Repo Database and install Dependencies
+# Force apt update
+apt update
+echo_green "Installing Klipper Dependencies ..."
+## Disabling shellcheck SC2086, because we want "wordsplitting"
+# shellcheck disable=SC2086
+check_install_pkgs ${KLIPPER_DEPS}
-apt update && apt install ${KLIPPER_DEPS} -y
-
-### Make sure user pi has access to serial ports
+## Make sure user pi has access to serial ports
## NOTE: BASE_USER is defined by CustomPIOS Variable
## there for if you plan to set something else than "pi"
## Currently CustomPIOS uses "pi"
-usermod -a -G ${KLIPPER_USER_GROUPS} ${BASE_USER}
+usermod -a -G "${KLIPPER_USER_GROUPS}" "${BASE_USER}"
-### clone klipper repo
-pushd /home/${BASE_USER}
+## Clone klipper repo
+pushd "/home/${BASE_USER}" &> /dev/null || exit 1
gitclone KLIPPER_REPO klipper
-### Create needed dirs
-for dirs in ${KLIPPER_USER_DIRS}; do
- if [ -d /home/$BASE_USER/$dirs ]; then
- echo_green "$dirs already exists!"
+## Create needed dirs
+for dir in ${KLIPPER_USER_DIRS}; do
+ if [ -d "/home/${BASE_USER}/$dir" ]; then
+ echo_green "${dir} already exists!"
else
- echo_green "Creating $dirs"
- sudo -u $BASE_USER mkdir -p $dirs
+ echo_green "Creating ${dir}"
+ sudo -u "${BASE_USER}" mkdir -p "${dir}"
fi
done
-popd
+popd &> /dev/null || exit 1
-# create python virtualenv and install klipper requirements
-pushd /home/${BASE_USER}
-echo_green "Creating Virtualenv for Klipper (klipper-env) ..."
-sudo -u ${BASE_USER} virtualenv -p python2 ${KLIPPER_PYTHON_DIR}
+## create python virtualenv and install klipper requirements
+pushd "/home/${BASE_USER}" &> /dev/null || exit 1
+echo_green "Creating Virtualenv for Klipper (klippy-env) ..."
+sudo -u "${BASE_USER}" virtualenv -p python3 "${KLIPPER_PYTHON_DIR}"
echo_green "Installing klippy Python Dependencies ..."
-sudo -u ${BASE_USER} ${KLIPPER_PYTHON_DIR}/bin/pip install -r ${KLIPPER_SRC_DIR}/${KLIPPER_PYENV_REQ}
-popd
+sudo -u "${BASE_USER}" "${KLIPPER_PYTHON_DIR}"/bin/pip install -r "${KLIPPER_SRC_DIR}"/"${KLIPPER_PYENV_REQ}"
+popd &> /dev/null || exit 1
-# enable systemd service
+## Enable systemd service
systemctl_if_exists enable klipper.service
+
+## Done message
+echo_green "Installing Klipper and enable klippy Service ... done!"
diff --git a/src/modules/mainsailos/config b/src/modules/mainsailos/config
index bc47fd421..04091d5bb 100644
--- a/src/modules/mainsailos/config
+++ b/src/modules/mainsailos/config
@@ -1,2 +1,4 @@
-# This file is intentionally left blank
-# Preparation for later use cases
+#!/bin/bash
+# shellcheck disable=all
+
+[ -n "$MAINSAILOS_DEPS" ] || MAINSAILOS_DEPS="python3-serial"
diff --git a/src/modules/mainsailos/start_chroot_script b/src/modules/mainsailos/start_chroot_script
index 274ac5931..0acaef549 100644
--- a/src/modules/mainsailos/start_chroot_script
+++ b/src/modules/mainsailos/start_chroot_script
@@ -21,3 +21,8 @@ function get_parent {
cat /etc/os-release | grep VERSION_CODENAME | cut -d '=' -f2
}
echo "${DIST_NAME} release ${DIST_VERSION} ($(get_parent))" > /etc/${DIST_NAME,,}-release
+
+### Install CANBoot Dependency
+apt update
+# shellcheck disable=SC2086
+check_install_pkgs ${MAINSAILOS_DEPS}
diff --git a/src/modules/mjpgstreamer/config b/src/modules/mjpgstreamer/config
deleted file mode 100644
index c6f0d4870..000000000
--- a/src/modules/mjpgstreamer/config
+++ /dev/null
@@ -1,5 +0,0 @@
-[ -n "$MJPGSTREAMER_MJPGSTREAMER_REPO_SHIP" ] || MJPGSTREAMER_MJPGSTREAMER_REPO_SHIP=https://github.com/jacksonliam/mjpg-streamer.git
-[ -n "$MJPGSTREAMER_MJPGSTREAMER_REPO_BUILD" ] || MJPGSTREAMER_MJPGSTREAMER_REPO_BUILD=
-[ -n "$MJPGSTREAMER_MJPGSTREAMER_REPO_BRANCH" ] || MJPGSTREAMER_MJPGSTREAMER_REPO_BRANCH=master
-[ -n "$MJPGSTREAMER_MJPGSTREAMER_REPO_DEPTH" ] || MJPGSTREAMER_MJPGSTREAMER_REPO_DEPTH=1
-[ -n "$MJPGSTREAMER_INCLUDE_MJPGSTREAMER" ] || MJPGSTREAMER_INCLUDE_MJPGSTREAMER=yes
diff --git a/src/modules/mjpgstreamer/filesystem/home/pi/klipper_config/webcam.txt b/src/modules/mjpgstreamer/filesystem/home/pi/klipper_config/webcam.txt
deleted file mode 100644
index e8bd44c72..000000000
--- a/src/modules/mjpgstreamer/filesystem/home/pi/klipper_config/webcam.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-### Windows users: To edit this file use Notepad++, VSCode, Atom or SublimeText.
-### Do not use Notepad or WordPad.
-
-### MacOSX users: If you use Textedit to edit this file make sure to use
-### "plain text format" and "disable smart quotes" in "Textedit > Preferences"
-
-### Configure which camera to use
-#
-# Available options are:
-# - auto: tries first usb webcam, if that's not available tries raspi cam
-# - usb: only tries usb webcam
-# - raspi: only tries raspi cam
-#
-# Defaults to auto
-#
-#camera="auto"
-
-### Additional options to supply to MJPG Streamer for the USB camera
-#
-# See https://faq.octoprint.org/mjpg-streamer-config for available options
-#
-# Defaults to a resolution of 640x480 px and a framerate of 10 fps
-#
-#camera_usb_options="-r 640x480 -f 10"
-
-### Additional webcam devices known to cause problems with -f
-#
-# Apparently there a some devices out there that with the current
-# mjpg_streamer release do not support the -f parameter (for specifying
-# the capturing framerate) and will just refuse to output an image if it
-# is supplied.
-#
-# The webcam daemon will detect those devices by their USB Vendor and Product
-# ID and remove the -f parameter from the options provided to mjpg_streamer.
-#
-# By default, this is done for the following devices:
-# Logitech C170 (046d:082b)
-# GEMBIRD (1908:2310)
-# Genius F100 (0458:708c)
-# Cubeternet GL-UPC822 UVC WebCam (1e4e:0102)
-#
-# Using the following option it is possible to add additional devices. If
-# your webcam happens to show above symptoms, try determining your cam's
-# vendor and product id via lsusb, activating the line below by removing # and
-# adding it, e.g. for two broken cameras "aabb:ccdd" and "aabb:eeff"
-#
-# additional_brokenfps_usb_devices=("aabb:ccdd" "aabb:eeff")
-#
-#
-#additional_brokenfps_usb_devices=()
-
-### Additional options to supply to MJPG Streamer for the RasPi Cam
-#
-# See https://faq.octoprint.org/mjpg-streamer-config for available options
-#
-# Defaults to 10fps
-#
-#camera_raspi_options="-fps 10"
-
-### Configuration of camera HTTP output
-#
-# Usually you should NOT need to change this at all! Only touch if you
-# know what you are doing and what the parameters mean.
-#
-# Below settings are used in the mjpg-streamer call like this:
-#
-# -o "output_http.so -w $camera_http_webroot $camera_http_options"
-#
-# Current working directory is the mjpg-streamer base directory.
-#
-#camera_http_webroot="./www-mainsail"
-#camera_http_options="-n"
-
-### EXPERIMENTAL
-# Support for different streamer types.
-#
-# Available options:
-# mjpeg [default] - stable MJPG-streamer
-#camera_streamer=mjpeg
diff --git a/src/modules/mjpgstreamer/filesystem/root/etc/logrotate.d/webcamd b/src/modules/mjpgstreamer/filesystem/root/etc/logrotate.d/webcamd
deleted file mode 100644
index ecd9f7803..000000000
--- a/src/modules/mjpgstreamer/filesystem/root/etc/logrotate.d/webcamd
+++ /dev/null
@@ -1,12 +0,0 @@
-
-/var/log/webcamd.log
-{
- rotate 2
- weekly
- maxsize 32M
- missingok
- notifempty
- compress
- delaycompress
- sharedscripts
-}
diff --git a/src/modules/mjpgstreamer/filesystem/root/etc/systemd/system/webcamd.service b/src/modules/mjpgstreamer/filesystem/root/etc/systemd/system/webcamd.service
deleted file mode 100644
index d5c121118..000000000
--- a/src/modules/mjpgstreamer/filesystem/root/etc/systemd/system/webcamd.service
+++ /dev/null
@@ -1,15 +0,0 @@
-
-[Unit]
-Description=the MainsailOS webcam daemon (based on OctoPi) with the user specified config
-
-[Service]
-WorkingDirectory=/usr/local/bin
-StandardOutput=append:/var/log/webcamd.log
-StandardError=append:/var/log/webcamd.log
-ExecStart=/usr/local/bin/webcamd
-Restart=always
-Type=forking
-User=pi
-
-[Install]
-WantedBy=multi-user.target
diff --git a/src/modules/mjpgstreamer/filesystem/root/usr/local/bin/webcamd b/src/modules/mjpgstreamer/filesystem/root/usr/local/bin/webcamd
deleted file mode 100755
index 7d3dbed2c..000000000
--- a/src/modules/mjpgstreamer/filesystem/root/usr/local/bin/webcamd
+++ /dev/null
@@ -1,303 +0,0 @@
-#!/bin/bash
-
-########################################################################
-### DO NOT EDIT THIS FILE TO CHANGE THE CONFIG!!! ###
-### ---------------------------------------------------------------- ###
-### There is no need to edit this file for changing resolution, ###
-### frame rates or any other mjpg-streamer parameters. Please edit ###
-### /home/pi/klipper_config/webcam.txt instead - that's what it's ###
-### there for! You can even do this with your Pi powered down by ###
-### directly accessing the file when using the SD card as thumb ###
-### drive in your regular computer. ###
-########################################################################
-
-MJPGSTREAMER_HOME=/home/pi/mjpg-streamer
-MJPGSTREAMER_INPUT_USB="input_uvc.so"
-MJPGSTREAMER_INPUT_RASPICAM="input_raspicam.so"
-
-brokenfps_usb_devices=("046d:082b" "1908:2310" "0458:708c" "1e4e:0102" "0471:0311" "038f:6001" "046d:0804" "046d:0825" "046d:0994" "0ac8:3450")
-
-config_dir="/home/pi/klipper_config"
-
-echo "Starting up webcamDaemon..."
-echo ""
-
-cfg_files=()
-#cfg_files+=/boot/mainsail.txt
-if [[ -d ${config_dir} ]]; then
- cfg_files+=( `ls ${config_dir}/webcam*.txt` )
-fi
-
-array_camera_config=()
-array_camera=()
-array_camera_usb_options=()
-array_camera_usb_device=()
-array_camera_raspi_options=()
-array_camera_http_webroot=()
-array_camera_http_options=()
-array_additional_brokenfps_usb_devices=()
-array_camera_device=()
-array_assigned_device=()
-
-echo "--- Configuration: ----------------------------"
-for cfg_file in ${cfg_files[@]}; do
- # init configuration - DO NOT EDIT, USE /home/pi/klipper_config/webcam*.txt INSTEAD!
- camera="auto"
- camera_usb_options="-r 640x480 -f 10"
- camera_raspi_options="-fps 10"
- camera_http_webroot="./www-mjpgstreamer"
- camera_http_options="-n"
- additional_brokenfps_usb_devices=()
-
- if [[ -e ${cfg_file} ]]; then
- source "$cfg_file"
- fi
- usb_options="$camera_usb_options"
-
- # if webcam device is explicitly given in /home/pi/klipper_config/webcam*.txt, save the path of the device
- # to a variable and remove its parameter from usb_options
- extracted_device=`echo $usb_options | sed 's@.*-d \(/dev/\(video[0-9]\+\|v4l/[^ ]*\)\).*@\1@'`
- if [ "$extracted_device" != "$usb_options" ]
- then
- # the camera options refer to a device, save it in a variable
- # replace video device parameter with empty string and strip extra whitespace
- usb_options=`echo $usb_options | sed 's/\-d \/dev\/\(video[0-9]\+\|v4l\/[^ ]*\)//g' | awk '$1=$1'`
- else
- extracted_device=""
- fi
-
- # echo configuration
- echo "cfg_file: $cfg_file"
- echo "camera: $camera"
- echo "usb options: $camera_usb_options"
- echo "raspi options: $camera_raspi_options"
- echo "http options: -w $camera_http_webroot $camera_http_options"
- echo ""
- echo "Explicitly USB device: $extracted_device"
- echo "-----------------------------------------------"
- echo ""
-
- array_camera_config+=( $cfg_file )
- array_camera+=( $camera )
- array_camera_usb_options+=("$usb_options")
- array_camera_usb_device+=("$extracted_device")
- array_camera_raspi_options+=("$camera_raspi_options")
- array_camera_http_webroot+=("$camera_http_webroot")
- array_camera_http_options+=("$camera_http_options")
- array_camera_brokenfps_usb_devices+=("${brokenfps_usb_devices[*]} ${additional_brokenfps_usb_devices[*]}")
- array_camera_device+=("")
-done
-
-# check if array contains a string
-function containsString() {
- local e match="$1"
- shift
- for e; do [[ "$e" == "$match" ]] && return 0; done
- return 1
-}
-
-# cleans up when the script receives a SIGINT or SIGTERM
-function cleanup() {
- # make sure that all child processed die when we die
- local pids=$(jobs -pr)
- [ -n "$pids" ] && kill $pids
- exit 0
-}
-
-# says goodbye when the script shuts down
-function goodbye() {
- # say goodbye
- echo ""
- echo "Goodbye..."
- echo ""
-}
-
-# runs MJPG Streamer, using the provided input plugin + configuration
-function runMjpgStreamer {
- input=$1
-
- # There are problems with 0x000137ab firmware on VL805 (Raspberry Pi 4}).
- # Try to autodetect offending firmware and temporarily fix the issue
- # by changing power management mode
- echo "Checking for VL805 (Raspberry Pi 4)..."
- if [[ -f /usr/bin/vl805 ]]; then
- VL805_VERSION=$(/usr/bin/vl805)
- VL805_VERSION=${VL805_VERSION#*: }
- echo " - version 0x${VL805_VERSION} detected"
- case "$VL805_VERSION" in
- 00013701)
- echo " - nothing to be done. It shouldn't cause USB problems."
- ;;
- 000137ab)
- echo -e " - \e[31mThis version is known to cause problems with USB cameras.\e[39m"
- echo -e " You may want to downgrade to 0x0013701."
- echo -e " - [FIXING] Trying the setpci -s 01:00.0 0xD4.B=0x41 hack to mitigate the"
- echo -e " issue. It disables ASPM L1 on the VL805. Your board may (or may not) get"
- echo -e " slightly hotter. For details see:"
- echo -e " https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=244421"
- setpci -s 01:00.0 0xD4.B=0x41
- ;;
- *)
- echo " - unknown firmware version. Doing nothing."
- ;;
- esac
- else
- echo " - It seems that you don't have VL805 (Raspberry Pi 4)."
- echo " There should be no problems with USB (a.k.a. select() timeout)"
- fi
-
- pushd $MJPGSTREAMER_HOME > /dev/null 2>&1
- echo Running ./mjpg_streamer -o "output_http.so -w $camera_http_webroot $camera_http_options" -i "$input"
- LD_LIBRARY_PATH=. ./mjpg_streamer -o "output_http.so -w $camera_http_webroot $camera_http_options" -i "$input" &
- sleep 1 &
- sleep_pid=$!
- wait ${sleep_pid}
- popd > /dev/null 2>&1
-}
-
-# starts up the RasPiCam
-function startRaspi {
- logger -s "Starting Raspberry Pi camera"
- runMjpgStreamer "$MJPGSTREAMER_INPUT_RASPICAM $camera_raspi_options"
-}
-
-# starts up the USB webcam
-function startUsb {
- options="$usb_options"
- device="video0"
-
- # check for parameter and set the device if it is given as a parameter
- input=$1
- if [[ -n $input ]]; then
- device=`basename "$input"`
- fi
-
- # add video device into options
- options="$options -d /dev/$device"
-
- uevent_file="/sys/class/video4linux/$device/device/uevent"
- if [ -e $uevent_file ]; then
- # let's see what kind of webcam we have here, fetch vid and pid...
- product=`cat $uevent_file | grep PRODUCT | cut -d"=" -f2`
- vid=`echo $product | cut -d"/" -f1`
- pid=`echo $product | cut -d"/" -f2`
- vidpid=`printf "%04x:%04x" "0x$vid" "0x$pid"`
-
- # ... then look if it is in our list of known broken-fps-devices and if so remove
- # the -f parameter from the options (if it's in there, else that's just a no-op)
- for identifier in ${brokenfps_usb_devices[@]};
- do
- if [ "$vidpid" = "$identifier" ]; then
- echo
- echo "Camera model $vidpid is known to not work with -f parameter, stripping it out"
- echo
- options=`echo $options | sed -e "s/\(\s\+\|^\)-f\s\+[0-9]\+//g"`
- fi
- done
- fi
-
- logger -s "Starting USB webcam"
- runMjpgStreamer "$MJPGSTREAMER_INPUT_USB $options"
-}
-
-# make sure our cleanup function gets called when we receive SIGINT, SIGTERM
-trap "cleanup" SIGINT SIGTERM
-# say goodbye when we EXIT
-trap "goodbye" EXIT
-
-# we need this to prevent the later calls to vcgencmd from blocking
-# I have no idea why, but that's how it is...
-vcgencmd version > /dev/null 2>&1
-
-# keep mjpg streamer running if some camera is attached
-while true; do
-
- # get list of usb video devices into an array
- video_devices=($(find /dev -regextype sed -regex '\/dev/video[0-9]\+' | sort -nk1.11 2> /dev/null))
-
- # add list of raspi camera into an array
- if [ "`vcgencmd get_camera`" = "supported=1 detected=1" ]; then
- video_devices+=( "raspi" )
- fi
-
- echo "Found video devices:"
- printf '%s\n' "${video_devices[@]}"
-
- for scan_mode in "usb" "usb-auto" "raspi" "auto"; do
- camera=$scan_mode
- if [[ "usb-auto" == "$scan_mode" ]]; then
- camera="usb"
- fi
- for ((i=0;i<${#array_camera[@]};i++)); do
- if [[ -z ${array_camera_device[${i}]} ]] && [[ $camera == ${array_camera[${i}]} ]]; then
- camera_config="${array_camera_config[${i}]}"
- usb_options="${array_camera_usb_options[${i}]}"
- camera_usb_device="${array_camera_usb_device[${i}]}"
- camera_raspi_options="${array_camera_raspi_options[${i}]}"
- camera_http_webroot="${array_camera_http_webroot[${i}]}"
- camera_http_options="${array_camera_http_options[${i}]}"
- brokenfps_usb_devices="${array_camera_brokenfps_usb_devices[${i}]}"
- if [[ ${camera_usb_device} ]] && { [[ "usb" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; }; then
- # usb device is explicitly set in options
- usb_device_path=`readlink -f ${camera_usb_device}`
- if containsString "$usb_device_path" "${array_camera_device[@]}"; then
- if [[ "auto" != ${scan_mode} ]]; then
- array_camera_device[${i}]="alredy_in_use"
- echo "config file='$camera_config':Video device already in use."
- continue
- fi
- elif containsString "$usb_device_path" "${video_devices[@]}"; then
- array_camera_device[${i}]="$usb_device_path"
- # explicitly set usb device was found in video_devices array, start usb with the found device
- echo "config file='$camera_config':USB device was set in options and found in devices, start MJPG-streamer with the configured USB video device: $usb_device_path"
- startUsb "$usb_device_path"
- continue
- fi
- elif [[ -z ${camera_usb_device} ]] && { [[ "usb-auto" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; }; then
- for video_device in "${video_devices[@]}"; do
- if [[ "raspi" != "$video_device" ]]; then
- if containsString "$video_device" "${array_camera_device[@]}"; then
- : #already in use
- else
- array_camera_device[${i}]="$video_device"
- # device is not set explicitly in options, start usb with first found usb camera as the device
- echo "config file='$camera_config':USB device was not set in options, start MJPG-streamer with the first found video device: ${video_device}"
- startUsb "${video_device}"
- break
- fi
- fi
- done
- if [[ -n ${array_camera_device[${i}]} ]]; then
- continue
- fi
- fi
- if [[ "raspi" == ${scan_mode} ]] || [[ "auto" == ${scan_mode} ]]; then
- video_device="raspi"
- if containsString "$video_device" "${array_camera_device[@]}"; then
- if [[ "auto" != ${scan_mode} ]]; then
- array_camera_device[${i}]="alredy_in_use"
- echo "config file='$camera_config':RasPiCam device already in use."
- fi
- elif containsString "$video_device" "${video_devices[@]}"; then
- array_camera_device[${i}]="$video_device"
- echo "config file='$camera_config':Start MJPG-streamer with video device: ${video_device}"
- startRaspi
- sleep 30 &
- sleep_pid=$!
- wait ${sleep_pid}
- fi
- fi
- fi
- done
- done
- array_assigned_device=( ${array_camera_device[*]} )
- if [[ ${#array_camera[@]} -eq ${#array_assigned_device[@]} ]]; then
- echo "Done bring up all configured video device"
- exit 0
- else
- echo "Scan again in two minutes"
- sleep 120 &
- sleep_pid=$!
- wait ${sleep_pid}
- fi
-done
diff --git a/src/modules/mjpgstreamer/start_chroot_script b/src/modules/mjpgstreamer/start_chroot_script
deleted file mode 100644
index 377286ba5..000000000
--- a/src/modules/mjpgstreamer/start_chroot_script
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env bash
-# MJPGstreamer installer
-# Builds MJPGstreamer for rPi devices
-# Written by Raymond Himle (Adapted from the OctoPi image creation scripts. Thanks Gina and Guysoft!)
-# GPL V3
-########
-
-
-# Source error handling, leave this in place
-set -x
-set -e
-
-source /common.sh
-install_cleanup_trap
-
-unpack /filesystem/home/${BASE_USER} /home/${BASE_USER} ${BASE_USER}
-
-apt update
-apt install -y --allow-downgrades git cmake=3.13.4-1 cmake-data=3.13.4-1
-cd /home/${BASE_USER}
- #mjpg-streamer
- if [ "$MJPGSTREAMER_INCLUDE_MJPGSTREAMER" == "yes" ]
- then
- echo "--- Installing mjpg-streamer"
- if [ $( is_in_apt libjpeg62-turbo-dev ) -eq 1 ]; then
- apt-get -y --force-yes install libjpeg62-turbo-dev
- elif [ $( is_in_apt libjpeg8-dev ) -eq 1 ]; then
- apt-get -y --force-yes install libjpeg8-dev
- fi
-
- apt-get -y --allow-downgrades --allow-remove-essential --allow-change-held-packages --no-install-recommends install imagemagick ffmpeg libv4l-dev
- gitclone MJPGSTREAMER_MJPGSTREAMER_REPO mjpg-streamer
- pushd mjpg-streamer
- mv mjpg-streamer-experimental/* .
-
- # As said in Makefile, it is just a wrapper around CMake.
- # To apply -j option, we have to unwrap it.
- MJPG_STREAMER_BUILD_DIR=_build
- [ -d ${MJPG_STREAMER_BUILD_DIR} ] || (mkdir ${MJPG_STREAMER_BUILD_DIR} && \
- chown ${BASE_USER}:${BASE_USER} ${MJPG_STREAMER_BUILD_DIR})
- [ -f ${MJPG_STREAMER_BUILD_DIR}/Makefile ] || (cd ${MJPG_STREAMER_BUILD_DIR} && \
- sudo -u ${BASE_USER} cmake -DCMAKE${MJPG_STREAMER_BUILD_DIR}_TYPE=Release ..)
-
- sudo -u ${BASE_USER} make -j $(nproc) -C ${MJPG_STREAMER_BUILD_DIR}
-
- sudo -u ${BASE_USER} cp ${MJPG_STREAMER_BUILD_DIR}/mjpg_streamer .
- sudo -u ${BASE_USER} find ${MJPG_STREAMER_BUILD_DIR} -name "*.so" -type f -exec cp {} . \;
-
- # create our custom web folder and add a minimal index.html to it
- sudo -u ${BASE_USER} mkdir www-mjpgstreamer
- pushd www-mjpgstreamer
- cat <> index.html
-
-mjpg_streamer test page
-
-Snapshot
-Refresh the page to refresh the snapshot
-
-Stream
-
-
-
-EOT
- popd
- popd
- fi
-
-### mjpg_streamer
-
-unpack /filesystem/root /
-
-if [ "$MJPGSTREAMER_INCLUDE_MJPGSTREAMER" == "yes" ]
-then
- systemctl_if_exists enable webcamd.service
-else
- rm /etc/logrotate.d/webcamd
- rm /etc/systemd/system/webcamd.service
- rm /root/bin/webcamd
-fi
-
-#cleanup
-apt-get clean
-apt-get autoremove -y
-
-# link logfiles to klipper_logs
-ln -s /var/log/webcamd.log /home/${BASE_USER}/klipper_logs/
-
-# Run installation steps defined above
-
-# Unpack root at the end, so files are modified before
-unpack /filesystem/root /
diff --git a/src/modules/moonraker/filesystem/home/pi/klipper_config/moonraker.conf b/src/modules/moonraker/filesystem/home/pi/klipper_config/moonraker.conf
index ef8366511..a874d816d 100644
--- a/src/modules/moonraker/filesystem/home/pi/klipper_config/moonraker.conf
+++ b/src/modules/moonraker/filesystem/home/pi/klipper_config/moonraker.conf
@@ -33,9 +33,15 @@ trusted_clients:
# enables moonraker to track and store print history.
[history]
+# this enables moonraker announcements for mainsail
+[announcements]
+subscriptions:
+ mainsail
+
# this enables moonraker's update manager
[update_manager]
refresh_interval: 168
+enable_auto_refresh: True
[update_manager mainsail]
type: web
diff --git a/src/modules/piconfig/filesystem/root/boot/config.txt b/src/modules/piconfig/filesystem/root/boot/config.txt
index 339f0dc24..5748a7a19 100644
--- a/src/modules/piconfig/filesystem/root/boot/config.txt
+++ b/src/modules/piconfig/filesystem/root/boot/config.txt
@@ -56,29 +56,60 @@ dtparam=spi=on
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
+
+####################################################
+#### MainsailOS specific configurations ####
+####################################################
+#### DO NOT CHANGE SECTION BELOW !!! ####
+#### UNLESS YOU KNOW WHAT YOU ARE DOING !!! ####
+####################################################
+
+## For more options and information see
+## https://www.raspberrypi.com/documentation/computers/config_txt.html
+## Some settings may impact device functionality. See link above for details
+
+## For additional information about device filters see
+## https://www.raspberrypi.com/documentation/computers/config_txt.html#model-filters
+
+
+[pi0]
+## This affects Pi Zero(W) and Pi Zero2
+## Due lag of RAM, limit GPU RAM
+gpu_mem=128
+
+[pi2]
+gpu_mem=256
+
+[pi3]
+## Use 256 if 1Gb Ram Model!
+gpu_mem=128
+# gpu_mem=256
+
[pi4]
-# Enable DRM VC4 V3D driver on top of the dispmanx display stack
+## Enable DRM VC4 V3D driver on top of the dispmanx display stack
dtoverlay=vc4-fkms-v3d
max_framebuffers=2
-# Do not use more than 256Mb on Pi Model 4, it uses its own Management.
+## Do not use more than 256Mb on Pi Model 4, it uses its own Management.
gpu_mem=256
[all]
-#dtoverlay=vc4-fkms-v3d
-# Enable Hardware UART for Serial Communication
+## SPI Interface is enabled by default for Input Shaper
+## To revert that comment out line #48
+## This colides with Hyperpixel Display!
+## Hyperpixel Screen uses the same Pin for Backlight.
+
+## Enable Hardware UART for Serial Communication
enable_uart=1
dtoverlay=disable-bt
-# Enable Raspicam devices at boot
+## Enable VideoCore at boot, needed for Raspicams and DSI devices.
start_x=1
-gpu_mem=256
-
-## Zero Models need special handling
-[pi0w]
-dtoverlay=pi3-disable-bt
-gpu_mem=128
-[pi02]
-gpu_mem=128
+### EXPERIMENTAL - Enable 64bit Kernel
+### The 64-bit kernel will only work on:
+### Raspberry Pi 3, 3+, 4, 400, Zero 2 W and 2B rev 1.2
+### and Raspberry Pi Compute Modules 3, 3+ and 4.
+# arm_64bit=1
+####################################################
diff --git a/src/modules/postrename/config b/src/modules/postrename/config
new file mode 100644
index 000000000..64222e8b6
--- /dev/null
+++ b/src/modules/postrename/config
@@ -0,0 +1,12 @@
+#!/bin/bash
+#### Post Rename
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2021
+#### https://github.com/mainsail-crew/MainsailOS
+####
+#### This File is distributed under GPLv3
+####
+# shellcheck disable=all
+
+# Intentionaly left blank
diff --git a/src/modules/postrename/filesystem/root/postrename b/src/modules/postrename/filesystem/root/postrename
new file mode 100644
index 000000000..dab0f55dc
--- /dev/null
+++ b/src/modules/postrename/filesystem/root/postrename
@@ -0,0 +1,207 @@
+#!/bin/bash
+#### Post Rename
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2021
+#### https://github.com/mainsail-crew/MainsailOS
+####
+#### This File is distributed under GPLv3
+####
+
+## Error handling
+set -e
+
+# Colored Output
+RED="\e[31m"
+YEL="\e[33m"
+GRE="\e[32m"
+WHITE="\e[97m"
+NOC="\e[0m"
+
+### Setup
+SERVICES=(moonraker klipper nginx sonar crowsnest)
+SYSTEMD_DIR="/etc/systemd/system"
+DEFAULT_USER="$(grep "1000" /etc/passwd | awk -F ':' '{print $1}')"
+
+
+## Helper funcs
+### Mangle services
+function stop_services {
+ for srv in "${SERVICES[@]}"; do
+ if [ "$(systemctl is-active "${srv}.service")" != "inactive" ]; then
+ systemctl stop "${srv}.service"
+ fi
+ done
+}
+
+function start_services {
+ for srv in "${SERVICES[@]}"; do
+ systemctl start "${srv}.service"
+ done
+}
+
+### Clean up
+function cleanup {
+ # Cleanup self
+ rm -rf "${0}"
+ # Cleanup rc.local
+ sed -i '/\/postrename/d' /etc/rc.local
+}
+
+### Change nginx root
+function change_www_root {
+ bash -c "
+ sed -i 's|/home/pi/mainsail|/home/${DEFAULT_USER}/mainsail|g' \
+ /etc/nginx/sites-available/mainsail
+ "
+}
+
+
+### change username in service files
+function change_service_user {
+ ### Filter nginx service first!
+ local -a servicefile
+ servicefile=( "${SERVICES[@]/nginx}" )
+
+ for i in "${servicefile[@]}"; do
+ if [ -n "${i}" ]; then
+ sed -i 's/pi/'"${DEFAULT_USER}"'/g' "${SYSTEMD_DIR}/${i}.service"
+ fi
+ done
+}
+
+function relocate_venv {
+ local -a venvs
+ venvs=(klippy-env moonraker-env)
+
+ for venv in "${venvs[@]}"; do
+ sudo -u "${DEFAULT_USER}" \
+ virtualenv --relocatable -p /usr/bin/python3 "/home/${DEFAULT_USER}/${venv}"
+ done
+}
+
+function patch_polkit_rules {
+ local polkit_dir polkit_legacy_dir polkit_usr_dir
+ polkit_legacy_dir="/etc/polkit-1/localauthority/50-local.d"
+ polkit_dir="/etc/polkit-1/rules.d"
+ polkit_usr_dir="/usr/share/polkit-1/rules.d"
+ if [ -f "${polkit_dir}/moonraker.rules" ]; then
+ sed -i 's/pi/'"${DEFAULT_USER}"'/g' "${polkit_dir}/moonraker.rules"
+ fi
+ if [ -f "${polkit_usr_dir}/moonraker.rules" ]; then
+ sed -i 's/pi/'"${DEFAULT_USER}"'/g' "${polkit_usr_dir}/moonraker.rules"
+ fi
+ if [ -f "${polkit_legacy_dir}/10-moonraker.pkla" ]; then
+ sed -i 's/pi/'"${DEFAULT_USER}"'/g' "${polkit_legacy_dir}/10-moonraker.pkla"
+ fi
+}
+
+function patch_cn_logrotate {
+ if [ -f "/etc/logrotate.d/crowsnest" ]; then
+ sed -i 's/pi/'"${DEFAULT_USER}"'/g' "/etc/logrotate.d/crowsnest"
+ fi
+}
+
+## Fix broken links
+function fix_broken_links {
+ local -a brokenlinks
+ local path
+ brokenlinks=(crowsnest sonar)
+ path="/usr/local/bin"
+
+ for bl in "${brokenlinks[@]}"; do
+ if [ -h "${path}/${bl}" ]; then
+ rm -rf "${path:?}/${bl}"
+ fi
+ find "/home/${DEFAULT_USER}" -type f -iname "${bl}" -print0 | \
+ xargs -0 -I {} ln -s {} "${path}/${bl}"
+ done
+}
+
+function fix_timelapse_links {
+ local config_dir src_dir target_dir
+ config_dir="/home/${DEFAULT_USER}/klipper_config"
+ src_dir="/home/${DEFAULT_USER}/moonraker-timelapse"
+ target_dir="/home/${DEFAULT_USER}/moonraker/moonraker/components"
+ sudo -u "${DEFAULT_USER}" \
+ ln -sf "${src_dir}/component/timelapse.py" "${target_dir}/timelapse.py"
+ sudo -u "${DEFAULT_USER}" \
+ ln -sf "${src_dir}/klipper_macro/timelapse.cfg" "${config_dir}/timelapse.cfg"
+}
+
+function main {
+local cmdltxt
+cmdltxt="/boot/cmdline.txt"
+
+## Make sure init_resize.sh and firstrun.sh are finished
+if [ "$(grep -c "init" "${cmdltxt}")" != "0" ] && \
+[ "$(grep -c "systemd.run.*" "${cmdltxt}")" != "0" ]; then
+ echo -e "[${RED}ERROR${NOC}] \
+ ${WHITE}Sdcard resize and firtrun are not finished yet ... [ABORT]${NOC}"
+ ## Make sure not failing rc.local
+ return 0
+fi
+## Abort if user = pi
+if [ "${DEFAULT_USER}" == "pi" ]; then
+ echo -e "[${YEL}SKIPPED${NOC}] \
+ ${WHITE}User is ${DEFAULT_USER}! Nothing to do ...${NOC}"
+ cleanup
+ exit 0
+fi
+
+echo -e "\n\t${WHITE}Trying to fix user rename ...${NOC}"
+## Stop services
+echo -en "${WHITE}Stopping all related services ...${NOC}\r"
+stop_services
+echo -e "${WHITE}Stopping all related services ...${NOC}[${GRE}OK${NOC}]"
+## Change www root
+echo -en "${WHITE}Try changing path of nginx root ...${NOC}\r"
+change_www_root
+echo -e "${WHITE}Try changing path of nginx root ...${NOC}[${GRE}OK${NOC}]"
+## patch service files
+echo -en "${WHITE}Patching service files ...${NOC}\r"
+change_service_user
+echo -e "${WHITE}Patching service files ...${NOC}[${GRE}OK${NOC}]"
+## relocate venvs
+echo -e "${WHITE}Trying to relocate venv's ...${NOC}"
+relocate_venv
+echo -e "${WHITE}Trying to relocate venv's ...${NOC}[${GRE}OK${NOC}]"
+## patch polkit rules
+echo -e "${WHITE}Patching moonraker's polkit rules ...${NOC}"
+patch_polkit_rules
+echo -e "${WHITE}Patching moonraker's polkit rules ...${NOC}[${GRE}OK${NOC}]"
+## patch crowsnest logrotate
+echo -e "${WHITE}Patching crowsnest logrotate ...${NOC}"
+patch_cn_logrotate
+echo -e "${WHITE}Patching crowsnest logrotate ...${NOC}[${GRE}OK${NOC}]"
+## fix broken links
+echo -en "${WHITE}Fix broken symlinks ...${NOC}\r"
+fix_broken_links
+fix_timelapse_links
+echo -e "${WHITE}Fix broken symlinks ...${NOC}[${GRE}OK${NOC}]"
+## do a short break
+sleep 2
+## reload daemons
+echo -en "${WHITE}Apply service file changes ...${NOC}\r"
+systemctl daemon-reload
+echo -e "${WHITE}Apply service file changes ...${NOC}[${GRE}OK${NOC}]"
+## do a short break
+sleep 2
+## restart services
+echo -e "${WHITE}Trying to restart services ...${NOC}"
+start_services
+## wait 30sec to come up
+echo -e "${WHITE}Waiting for service restart completed ...${NOC}"
+sleep 30
+## Cleanup
+echo -e "${WHITE}Cleanup files, remove postrename ...${NOC}"
+cleanup
+sleep 2
+## reboot system
+echo -e "${WHITE}Reboot system ...${NOC}"
+reboot
+}
+
+### MAIN
+main
+exit 0
diff --git a/src/modules/postrename/start_chroot_script b/src/modules/postrename/start_chroot_script
new file mode 100644
index 000000000..8ea8937da
--- /dev/null
+++ b/src/modules/postrename/start_chroot_script
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+#!/bin/bash
+#### Post Rename
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2021
+#### https://github.com/mainsail-crew/MainsailOS
+####
+#### This File is distributed under GPLv3
+####
+# shellcheck disable=all
+
+# Source error handling, leave this in place
+set -xe
+
+# Source CustomPIOS common.sh
+source /common.sh
+install_cleanup_trap
+
+## unpack postrename
+unpack filesystem/root / root
+## set executable
+sudo chmod 0755 /postrename
+
+## run postrename via rc.local
+sed -i 's@exit 0@@' /etc/rc.local
+echo -e "/postrename\nexit 0\n" | tee -a /etc/rc.local
diff --git a/src/modules/sonar/config b/src/modules/sonar/config
new file mode 100644
index 000000000..0e350bd8e
--- /dev/null
+++ b/src/modules/sonar/config
@@ -0,0 +1,21 @@
+#!/bin/bash
+#### Sonar - A WiFi Keepalive daemon
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2022
+#### https://github.com/mainsail-crew/sonar
+####
+#### This File is distributed under GPLv3
+####
+# shellcheck disable=all
+
+# Sonar
+[ -n "$SONAR_REPO_SHIP" ] || SONAR_REPO_SHIP=https://github.com/mainsail-crew/sonar.git
+[ -n "$SONAR_REPO_BRANCH" ] || SONAR_REPO_BRANCH=main
+[ -n "$SONAR_DEPS" ] || SONAR_DEPS="git crudini iputils-ping"
+[ -n "$SONAR_DEFAULT_CONF_DIR" ] || SONAR_DEFAULT_CONF_DIR="/home/${BASE_USER}/klipper_config"
+[ -n "$SONAR_DEFAULT_CONF" ] || SONAR_DEFAULT_CONF="mainsail_default.conf"
+# Add Sonar to moonraker.conf (update_manager) bool (1:yes / 0:no)
+[ -n "$SONAR_ADD_SONAR_MOONRAKER" ] || SONAR_ADD_SONAR_MOONRAKER=1
+# Path to moonraker.conf (must be set if add to update_manager)
+[ -n "$SONAR_MOONRAKER_CONF" ] || SONAR_MOONRAKER_CONF="${SONAR_DEFAULT_CONF_DIR}/moonraker.conf"
diff --git a/src/modules/sonar/start_chroot_script b/src/modules/sonar/start_chroot_script
new file mode 100644
index 000000000..5ca66588f
--- /dev/null
+++ b/src/modules/sonar/start_chroot_script
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+#### Sonar - A WiFi Keepalive daemon
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2022
+#### https://github.com/mainsail-crew/sonar
+####
+#### This File is distributed under GPLv3
+####
+
+# shellcheck enable=require-variable-braces
+
+## Source error handling, leave this in place
+set -Eex
+
+## Set LC_ALL to prevent errors
+export LC_ALL=C
+
+# Set DEBIAN_FRONTEND to noninteractive
+if [ "${DEBIAN_FRONTEND}" != "noninteractive" ]; then
+ export DEBIAN_FRONTEND=noninteractive
+fi
+
+## Source CustomPIOS common.sh
+# shellcheck disable=SC1091
+source /common.sh
+install_cleanup_trap
+
+# Setup template file
+MOONRAKER_TEMPLATE="/home/${BASE_USER}/sonar/file_templates/moonraker_update.txt"
+
+## helper func moonraker update_manager
+function add_update_entry {
+ if [ -f "${SONAR_MOONRAKER_CONF}" ]; then
+ if [ -f "/tmp/moonraker.conf" ]; then
+ rm -rf /tmp/moonraker.conf
+ fi
+ sudo -u "${BASE_USER}" \
+ cat "${SONAR_MOONRAKER_CONF}" "${MOONRAKER_TEMPLATE}" | \
+ tee /tmp/moonraker.conf > /dev/null &&
+ cp -rf /tmp/moonraker.conf "${SONAR_MOONRAKER_CONF}"
+ fi
+}
+
+echo_green "Installing sonar and enable sonar Service ..."
+
+## Install all deps at once for time consumption reasons.
+## APT: Update Repo Database and install Dependencies
+# Force apt update
+apt update
+echo_green "Installing Sonar Dependencies ..."
+# shellcheck disable=SC2086
+## Disabling shellcheck SC2086, because we want "wordsplitting"
+check_install_pkgs ${SONAR_DEPS}
+## Move to $HOME dir
+pushd "/home/${BASE_USER}" &> /dev/null || exit 1
+ ## clone Repo
+ echo_green "Clone sonar repository ..."
+ gitclone SONAR_REPO sonar
+ ## Install sonar, using builtin installer
+ pushd "/home/${BASE_USER}/sonar" &> /dev/null || exit 1
+ echo_green "Running sonar installer ..."
+ sudo -u "${BASE_USER}" make unattended
+ ## Make sure config dir exits
+ if [ ! -d "${SONAR_DEFAULT_CONF_DIR}" ]; then
+ sudo -u "${BASE_USER}" mkdir -p "${SONAR_DEFAULT_CONF_DIR}"
+ fi
+ ## Copy default config
+ echo_green "Copying default config file ..."
+ sudo -u "${BASE_USER}" \
+ cp "${PWD}/sample_config/${SONAR_DEFAULT_CONF}" \
+ "${SONAR_DEFAULT_CONF_DIR}/sonar.conf"
+
+ ## Add moonraker update_manager
+ if [ "${SONAR_ADD_SONAR_MOONRAKER}" == "1" ]; then
+ echo -en "Adding Sonar Update Manager entry to moonraker.conf ...\r"
+ add_update_entry
+ echo -e "Adding Sonar Update Manager entry to moonraker.conf ... [OK]"
+ fi
+ popd &> /dev/null || exit 1
+popd &> /dev/null || exit 1
+
+## Done message
+echo_green "Installing sonar and enable sonar Service ... done!"
diff --git a/src/modules/timelapse/config b/src/modules/timelapse/config
new file mode 100644
index 000000000..ad56380f4
--- /dev/null
+++ b/src/modules/timelapse/config
@@ -0,0 +1,16 @@
+#!/bin/bash
+# moonraker-timelapse custompios module
+# https://github.com/mainsail-crew/moonraker-timelapse
+# written by Stephan Wendel aka KwadFan
+#
+# GPL V3
+########
+# shellcheck disable=all
+[ -n "$TIMELAPSE_SRC_DIR" ] || TIMELAPSE_SRC_DIR="/home/${BASE_USER}/moonraker-timelapse"
+[ -n "$TIMELAPSE_PYTHON_DIR" ] || TIMELAPSE_TARGET_DIR="/home/${BASE_USER}/moonraker/moonraker/components"
+[ -n "$TIMELAPSE_CONFIG_DIR" ] || TIMELAPSE_CONFIG_DIR="/home/${BASE_USER}/klipper_config"
+[ -n "$TIMELAPSE_TEMPLATE" ] || TIMELAPSE_TEMPLATE="/home/${BASE_USER}/klipper_config/timelapse_template.txt"
+
+[ -n "$TIMELAPSE_REPO_SHIP" ] || TIMELAPSE_REPO_SHIP=https://github.com/mainsail-crew/moonraker-timelapse.git
+[ -n "$TIMELAPSE_REPO_BRANCH" ] || TIMELAPSE_REPO_BRANCH=main
+[ -n "$TIMELAPSE_DEPS" ] || TIMELAPSE_DEPS="ffmpeg"
diff --git a/src/modules/timelapse/filesystem/home/pi/klipper_config/timelapse_template.txt b/src/modules/timelapse/filesystem/home/pi/klipper_config/timelapse_template.txt
new file mode 100644
index 000000000..910ab0e16
--- /dev/null
+++ b/src/modules/timelapse/filesystem/home/pi/klipper_config/timelapse_template.txt
@@ -0,0 +1,13 @@
+
+### moonraker-timelapse
+### Don't forget to include timelapse.cfg to your printer.cfg
+### Uncomment to enable moonraker-timelapse
+
+#[timelapse]
+
+#[update_manager timelapse]
+#type: git_repo
+#primary_branch: main
+#path: ~/moonraker-timelapse
+#origin: https://github.com/mainsail-crew/moonraker-timelapse.git
+#managed_services: klipper moonraker
diff --git a/src/modules/timelapse/start_chroot_script b/src/modules/timelapse/start_chroot_script
new file mode 100644
index 000000000..ac985d168
--- /dev/null
+++ b/src/modules/timelapse/start_chroot_script
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+#### MainsailOS Build Chain
+####
+#### moonraker-timelapse install module
+#### #!/bin/bash
+#### https://github.com/mainsail-crew/moonraker-timelapse
+####
+#### Written by Stephan Wendel aka KwadFan
+#### Copyright 2022
+#### https://github.com/mainsail-crew/MainsailOS
+####
+#### This File is distributed under GPLv3
+####
+
+###### THIS MODULE HAS TO BE PLACED RIGHT AFTER "moonraker" MODULE!!!
+
+# shellcheck enable=require-variable-braces
+
+## Source error handling, leave this in place
+set -Eex
+
+## Set LC_ALL to prevent errors
+export LC_ALL=C
+
+# Set DEBIAN_FRONTEND to noninteractive
+if [ "${DEBIAN_FRONTEND}" != "noninteractive" ]; then
+ export DEBIAN_FRONTEND=noninteractive
+fi
+
+## Source CustomPIOS common.sh
+# shellcheck disable=SC1091
+source /common.sh
+install_cleanup_trap
+
+## helper func moonraker update_manager
+function add_timelapse_entry {
+ if [ -f "${TIMELAPSE_CONFIG_DIR}/moonraker.conf" ]; then
+ if [ -f "/tmp/moonraker.conf" ]; then
+ rm -rf /tmp/moonraker.conf
+ fi
+ sudo -u "${BASE_USER}" \
+ cat "${TIMELAPSE_CONFIG_DIR}/moonraker.conf" "${TIMELAPSE_TEMPLATE}" | \
+ tee /tmp/moonraker.conf > /dev/null &&
+ cp -rf /tmp/moonraker.conf "${TIMELAPSE_CONFIG_DIR}/moonraker.conf"
+ fi
+ ## Cleanup template
+ if [ -f "${TIMELAPSE_TEMPLATE}" ]; then
+ rm -f "${TIMELAPSE_TEMPLATE}"
+ fi
+}
+
+echo_green "Installing moonraker-timelapse plugin ..."
+
+### Install template file
+unpack /filesystem/home/${BASE_USER} /home/${BASE_USER} ${BASE_USER}
+
+## Install all deps at once for time consumption reasons.
+## APT: Update Repo Database and install Dependencies
+# Force apt update
+apt update
+echo_green "Installing moonraker-timelapse Dependencies ..."
+## Disabling shellcheck SC2086, because we want "wordsplitting"
+# shellcheck disable=SC2086
+check_install_pkgs ${TIMELAPSE_DEPS}
+
+
+## Clone klipper repo
+pushd "/home/${BASE_USER}" &> /dev/null || exit 1
+gitclone TIMELAPSE_REPO moonraker-timelapse
+
+## Link component
+if [ -d "${TIMELAPSE_TARGET_DIR}" ]; then
+ echo "Linking extension to moonraker ..."
+ sudo -u "${BASE_USER}" \
+ ln -sf "${TIMELAPSE_SRC_DIR}/component/timelapse.py" "${TIMELAPSE_TARGET_DIR}/timelapse.py"
+fi
+
+## Link timelapse.cfg
+if [ -d "/home/${BASE_USER}/klipper_config" ]; then
+ echo "Linking macro file ..."
+ sudo -u "${BASE_USER}" \
+ ln -sf "${TIMELAPSE_SRC_DIR}/klipper_macro/timelapse.cfg" "${TIMELAPSE_CONFIG_DIR}/timelapse.cfg"
+fi
+
+## Extend moonraker.conf
+echo "Modifying moonraker.conf ..."
+add_timelapse_entry
+
+
+popd || exit 1
+
+
+## Done message
+echo_green "Installing moonraker-timelapse plugin ... done!"