diff --git a/.github/workflows/bin/create-version.sh b/.github/workflows/bin/create-version.sh new file mode 100644 index 0000000..0c251ae --- /dev/null +++ b/.github/workflows/bin/create-version.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +readonly PACKAGE_PATH="$1" +readonly CONTROL_PATH="$2" +readonly PACKAGE_VERSION="$3" +declare -a ARCH_LIST=("arm64" "armhf") + +# Create the "versions" directory if it doesn't exist +mkdir -p ${PACKAGE_PATH}/versions + +for arch_type in "${ARCH_LIST[@]}"; do + # first change the Architecture in the DEBIAN/control file + sed -i "/^Architecture:.*/ c Architecture: ${arch_type}" "${CONTROL_PATH}" + + # Run docker image and build the package in the mounted directory + docker run --rm -v "$PACKAGE_PATH":/pkg multiarch/debian-debootstrap:"${arch_type}"-bullseye \ + dpkg-deb --build /pkg/x735-script-pkg "/pkg/versions/x735-script_${PACKAGE_VERSION}_${arch_type}.deb" +done diff --git a/.github/workflows/bin/create_release_note.sh b/.github/workflows/bin/create_release_note.sh new file mode 100644 index 0000000..6256d6d --- /dev/null +++ b/.github/workflows/bin/create_release_note.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +set -e +set -o pipefail + +readonly FILE_NOT_EXIST="152" +readonly INVALID_CLI_ARG_COUNT="154" +readonly INVALID_REQUEST="155" + +terminate() { + local -r msg=$1 + local -r err_code=${2:-150} + echo "${msg}" >&2 + exit "${err_code}" +} + +if [[ $# -ne 5 ]]; then + terminate "Usage: $0 TEMPLATE_FILE GITHUB_REPO TOKEN RELEASE_ID CHANGELOG_MSG" "${INVALID_CLI_ARG_COUNT}" +fi + +readonly TEMPLATE_FILE_PATH=$1 +readonly GITHUB_REPO=$2 +readonly TOKEN=$3 +readonly RELEASE_ID=$4 +readonly CHANGELOG_MSG=$5 + +readonly release_url="https://api.github.com/repos/${GITHUB_REPO}/releases/${RELEASE_ID}" + +# Check if the artifact path exists +if [ ! -s "$TEMPLATE_FILE_PATH" ]; then + terminate "Template release note not found or empty: ${TEMPLATE_FILE_PATH}" "${FILE_NOT_EXIST}" +fi + +function convert_to_md_line_breaks() { + local input="$1" + local output="" + local IFS=$'\n' + + for line in $input; do + output="${output}${line}\\n\\n" + done + + echo "$output" +} + +release_data=$(curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${release_url}") + +## check if data are JSON and the response is correct +if ! [[ "$release_data" =~ ^\{.*\}$ ]] || [[ ! $(echo "$release_data" | jq -e --arg RELEASE_ID "$RELEASE_ID" '.id == ($RELEASE_ID|tonumber)') ]]; then + terminate "The script was not able to pull release data." "${INVALID_REQUEST}" +fi + +RELEASE_PUBLICH_DATE=$(echo "$release_data" | jq -r '.published_at') +TAG_NAME=$(echo "$release_data" | jq -r '.tag_name' | sed -n 's/^v//p') + +readme_text=$(awk -v tag_name="${TAG_NAME}" -v release_date="$RELEASE_PUBLICH_DATE" -v repo="$GITHUB_REPO" -v msg="$CHANGELOG_MSG" ' + { + gsub(/##TAG_NAME##/, tag_name); + gsub(/##RELEASE_DATE##/, release_date); + gsub(/##REPO##/, repo); + gsub(/##MSG##/, msg); + print; + } +' "$TEMPLATE_FILE_PATH") + +release_text=$(convert_to_md_line_breaks "$readme_text") + +send_release_note=$(curl -L \ + -X PATCH \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + -w "%{http_code}" \ + -o /dev/null \ + "${release_url}" \ + -d "{\"body\":\"${release_text}\"}") + +if [[ "$send_release_note" -ne "200" ]]; then + terminate "The script was not able to push release note." "${INVALID_REQUEST}" +fi \ No newline at end of file diff --git a/.github/workflows/bin/update_changelog.sh b/.github/workflows/bin/update_changelog.sh new file mode 100644 index 0000000..91f718a --- /dev/null +++ b/.github/workflows/bin/update_changelog.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +readonly CHANGELOG_PATH="$1" +readonly CHANGELOG_MSG="$2" +readonly VERSION="$3" + +readonly INVALID_CLI_ARG_COUNT="154" + +terminate() { + local -r msg=$1 + local -r err_code=${2:-150} + echo "${msg}" >&2 + exit "${err_code}" +} + +# Check if commit message is not empty +if [ ! -n "$CHANGELOG_MSG" ]; then + terminate "Error: No commit message found in the trigger commit." "${INVALID_CLI_ARG_COUNT}" +fi + +# Function to check if DEBIAN/changelog file exists +changelog_exists() { + [ -e "${CHANGELOG_PATH}" ] +} + +# Function to create or append to DEBIAN/changelog +create_or_append_changelog() { + local message=$1 + + if changelog_exists; then + dch --package x735-script --newversion "${VERSION}" --changelog "${CHANGELOG_PATH}" -- "$message [v${VERSION}]" + else + dch --package x735-script --newversion "${VERSION}" --create --changelog "${CHANGELOG_PATH}" -- "Initial Release. [v${VERSION}]" + fi +} + +# Main script +create_or_append_changelog "$CHANGELOG_MSG" \ No newline at end of file diff --git a/.github/workflows/bin/upload-artifacts.sh b/.github/workflows/bin/upload-artifacts.sh new file mode 100644 index 0000000..1c4e3ad --- /dev/null +++ b/.github/workflows/bin/upload-artifacts.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e + +readonly FILE_NOT_EXIST="152" +readonly INVALID_CLI_ARG_COUNT="154" + +terminate() { + local -r msg=$1 + local -r err_code=${2:-150} + echo "${msg}" >&2 + exit "${err_code}" +} + +# Function to upload an artifact to the release +upload_artifact() { + local artifact_path=$1 + local upload_url=$2 + local token=$3 + + filename=$(basename "$artifact_path") + echo "Uploading $filename..." + + curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${token}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + -H "Content-Type: application/octet-stream" \ + "$upload_url=$filename" \ + --data-binary "@${artifact_path}" +} + +# Main script starts here +if [ $# -lt 3 ]; then + terminate "Usage: $0 " "${INVALID_CLI_ARG_COUNT}" +fi + +artifact_path=$1 +upload_url=$2 +token=$3 + +# Check if the artifact path exists +if [ ! -f "$artifact_path" ]; then + terminate "Artifact file not found: ${artifact_path}" "${FILE_NOT_EXIST}" +fi + +upload_artifact "$artifact_path" "$upload_url" "$token" diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml new file mode 100644 index 0000000..040b23f --- /dev/null +++ b/.github/workflows/build-package.yml @@ -0,0 +1,127 @@ +name: Build and Package Fan Board Script + +on: + push: + tags: + - 'v*' + +env: + WORKFLOW_PATH: ${GITHUB_WORKSPACE}/.github/workflows + PACKAGE_DEBIAN: ${GITHUB_WORKSPACE}/x735-script-pkg/DEBIAN + USER_NAME: ${{ vars.USER_NAME }} + USER_EMAIL: ${{ vars.USER_EMAIL }} + +jobs: + update_repo: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + ref: main + token: ${{ secrets.CREATE_RELEASE_TOKEN }} + + - name: Install Dependencies + run: | + # Install any necessary dependencies for modifing the repository + sudo apt update && sudo apt install -y devscripts + + - name: Set up Environment + id: update_repo_settings + run: | + git config --local user.name "${{ env.USER_NAME }}" + git config --local user.email "${{ env.USER_EMAIL }}" + + # changelog message gonna be in this case the last commit message where the tag is created. + echo "changelog_msg=$(git log -1 --pretty=%B | head -n 1)" >> "$GITHUB_OUTPUT" + echo "tag_name=$(echo "${GITHUB_REF}" | sed -n 's|^refs/tags/v||p')" >> "$GITHUB_OUTPUT" + + - name: Update and Commit Changelog + run: | + export DEBFULLNAME="${{ env.USER_NAME }}" + export DEBEMAIL="${{ env.USER_EMAIL }}" + /bin/bash ${{ env.WORKFLOW_PATH }}/bin/update_changelog.sh "${{ env.PACKAGE_DEBIAN }}/changelog" "${{ steps.update_repo_settings.outputs.changelog_msg }}" "${{ steps.update_repo_settings.outputs.tag_name }}" + + - name: Update Version + run: | + sed -i '/^Version:.*/ c Version: ${{ steps.update_repo_settings.outputs.tag_name }}' "${{ env.PACKAGE_DEBIAN }}/control" + + - name: Commit Changelog Changes + id: commit_changelog + run: | + git add . + git commit -m "Update Changelog and Set Version to ${{ steps.update_repo_settings.outputs.tag_name }}" + #git remote set-url --push origin "https://${{ secrets.CREATE_RELEASE_TOKEN }}@github.com/${{ github.repository }}.git" + git push --follow-tags origin main + + # Store the commit SHA as an output variable + echo "update_repo_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + outputs: + tag_name: ${{ steps.update_repo_settings.outputs.tag_name }} + update_repo_sha: ${{ steps.commit_changelog.outputs.update_repo_sha }} + changelog_msg: ${{ steps.update_repo_settings.outputs.changelog_msg }} + + + + build_and_package: + runs-on: ubuntu-latest + needs: update_repo + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + ref: ${{ needs.update_repo.outputs.update_repo_sha }} # Use the update_repo_sha output from the previous job + + - name: Install Dependencies + run: | + echo "Install any necessary dependencies for building your script here" + sudo apt update && sudo apt install -y dpkg qemu-user-static jq + + - name: Set up QEMU for arm64 + run: | + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + + - name: Granting Permissions + run: | + # Grant execute permission to all files in DEBIAN directory, but the compat file + find ${{ env.PACKAGE_DEBIAN }} -type f ! -name 'compat' -exec chmod +x {} \; + + - name: Build different ARCH Versions + run: | + # Run the create-version.sh script to build different versions + /bin/bash ${{ env.WORKFLOW_PATH }}/bin/create-version.sh "${GITHUB_WORKSPACE}" "${{ env.PACKAGE_DEBIAN }}/control" "${{ needs.update_repo.outputs.tag_name }}" + + - name: Save Artifact + uses: actions/upload-artifact@v4 + with: + name: x735-script-artifacts + path: versions/x735-script_*.deb + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.CREATE_RELEASE_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release Version ${{ needs.update_repo.outputs.tag_name }} + draft: false + prerelease: false + + - name: Upload Artifacts to Release and update Release Note + run: | + # Update Release Note + /bin/bash ${{ env.WORKFLOW_PATH }}/bin/create_release_note.sh \ + "${{ env.WORKFLOW_PATH }}/templates/release_template.txt" \ + "${{ github.repository }}" \ + "${{ secrets.CREATE_RELEASE_TOKEN }}" \ + "${{ steps.create_release.outputs.id }}" \ + "${{ needs.update_repo.outputs.changelog_msg }}" + + # Add a loop to upload each artifact + for artifact in ${GITHUB_WORKSPACE}/versions/x735-script_*.deb; do + /bin/bash ${{ env.WORKFLOW_PATH }}/bin/upload-artifacts.sh "$artifact" "${{ steps.create_release.outputs.upload_url }}" "${{ secrets.CREATE_RELEASE_TOKEN }}" + done diff --git a/.github/workflows/templates/release_template.txt b/.github/workflows/templates/release_template.txt new file mode 100644 index 0000000..a2d0170 --- /dev/null +++ b/.github/workflows/templates/release_template.txt @@ -0,0 +1,79 @@ +# Fan Control and Power Management Script Release v##TAG_NAME## + +We're excited to announce the release of our Fan Control and Power Management Script for Linux Debian on ARM64 and ARMHF architectures! This script allows you to seamlessly control fan speed on your Raspberry Pi and efficiently manage power settings for optimal performance. + +## Installation + +To get started, follow these simple steps to install the script on your Raspberry Pi: + +### 1. Download the Package + +For ARM64 architecture, open your terminal and run: + +```bash +wget https://github.com/##REPO##/releases/download/v##TAG_NAME##/x735-script_##TAG_NAME##_arm64.deb +``` + +For ARMHF architecture, use: + +```bash +wget https://github.com/##REPO##/releases/download/v##TAG_NAME##/x735-script_##TAG_NAME##_armhf_.deb +``` + +### 2. Install the Package + +Once the package is downloaded, install it using the following command: + +For ARM64 architecture, run: + +```bash +sudo dpkg -i x735-script_##TAG_NAME##_arm64.deb +``` + +For ARMHF architecture, use: + +```bash +sudo dpkg -i x735-script_##TAG_NAME##_armhf_.deb +``` + +### 3. Reboot Your System + +After the installation is complete, it's important to reboot your system for the changes to take effect. Run: + +```bash +sudo reboot +``` + +## Usage + +This Bash script offers seamless control over your Raspberry Pi's fan speed and power management, ensuring optimal performance while maintaining efficient temperature regulation. With the added convenience of safe shutdown capabilities, you can confidently power down your system using the command: + +``` bash +sudo x735off +``` + +To monitor the fan speed and its operational status, simply query the `x735-fan.service` service using the following command: + +``` bash +systemctl status x735-fan.service +``` + +Should you wish to take manual control of the fan, you have the flexibility to halt or restart it using the respective commands: + +``` bash +sudo systemctl stop x735-fan.service +sudo systemctl start x735-fan.service +``` + +This script empowers you to fine-tune your Raspberry Pi's performance by automatically adjusting fan speed as needed, ensuring a cool and optimized computing experience. The seamless integration of power management and temperature control provides a comprehensive solution for Raspberry Pi enthusiasts seeking both reliability and performance. + +## Changelog + +- **v##TAG_NAME## (##RELEASE_DATE##):** + ##MSG## + +## Get Involved + +We're committed to continuous improvement and welcome your feedback. If you encounter any issues or have suggestions for enhancements, please [open an issue](https://github.com/##REPO##/issues) on our GitHub repository. + +Thank you for choosing our Fan Control and Power Management Script. We hope it enhances your Raspberry Pi usage experience! \ No newline at end of file diff --git a/README.md b/README.md index e35e18e..ba056fe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,74 @@ -# x735-script +# Overview: +The "x735-script" is a Bash script designed to manage the x735 Power Management and Cooling Expansion Board (HAT) for Raspberry Pi 3 and 4. This script enables the user to control various features of the x735 board, including restarting or shutting down the Raspberry Pi using a physical button on the x735 board. Additionally, it uses the PWM pin to control the speed of the fan based on the CPU temperature, providing efficient cooling. -User Guide: https://wiki.geekworm.com/X735-script +## Features: +Control over GPIO pins to activate the restart and shutdown button on the x735 board. +Dynamic fan speed control based on the CPU temperature for optimal cooling. +Automatic installation of the x735off command, enabling safe shutdown of the Raspberry Pi with the x735 HAT. -Email: support@geekworm.com +## Installation: +Installing the `x735-script` is a straightforward process. To begin, simply click on the link below to access the latest release: + +**[https://github.com/molhamalnasr/x735-script/releases/latest](https://github.com/molhamalnasr/x735-script/releases/latest)** + +Once you're on the release page, follow the provided instructions to install the script on your Raspberry Pi. The installation process will guide you through the necessary steps to set up the fan speed control and power management features seamlessly. + +If you prefer to install an older version of the script for compatibility or other reasons, you can do so by clicking on the link below: + +**[https://github.com/molhamalnasr/x735-script/releases](https://github.com/molhamalnasr/x735-script/releases)** + +## Developer's Guide: +If you are a developer and want to contribute to the "x735-script" or perform further development, follow these steps: + +- Install the necessary dependencies: + ``` bash + sudo apt-get update && sudo apt-get install -y dpkg + ``` + +- Clone the repository and change to the project directory: + ``` bash + git clone https://github.com/molhamalnasr/x735-script && cd x735-script + ``` + +- Grant necessary permissions: + ``` bash + chmod +x x735-script-pkg/usr/bin/x735off + chmod +x x735-script-pkg/usr/lib/x735-script/scripts/*.sh + find x735-script-pkg/DEBIAN -type f ! -name 'compat' -exec chmod +x {} \; + ``` + +- Build and test the package: + ``` bash + dpkg-deb --build x735-script-pkg + ``` + +You can verify the content and installation information of the "x735-script" package with the following command: + +``` bash +sudo dpkg -I x735-script-pkg.deb +``` + +## Release Information for Developers: +For developers, please note the following steps to set up the necessary configurations for automated releases: + +1. Generate a "Personal Access Token" in your Github account settings with the required permissions. Make sure to grant the "repo" scope to enable access for repository-related actions and name it `CREATE_RELEASE_TOKEN`. +2. Add this generated token to the repository's variable settings as a "Repository secret" named `CREATE_RELEASE_TOKEN`. +3. you need another 2 variables as a "Repository variables" + - USER_EMAIL="\" + - USER_NAME=\ + +These steps are essential for enabling the Github Actions workflow to create automated releases when you push a tag starting with the letter "v*" to the main branch. for example: v3.0.1. The "GITHUB_TOKEN" repository secret will be used by the workflow to build a new release and upload it as an artifact to Github. The release will be available under: https://github.com/molhamalnasr/x735-script/releases/latest. and the "USER_EMAIL" and "USER_NAME" are used to create/append the changelog file for the package. + +By following these instructions, you ensure a streamlined process for managing releases and promoting collaboration among developers in the project. + +## User Guide: +For detailed instructions on how to use the "x735-script" and leverage its features, please refer to the official User Guide available at: + +[https://wiki.geekworm.com/X735-script](https://wiki.geekworm.com/X735-script) + +#### Contact: +For any questions, issues, or support related to the "x735-script," you can reach out to the development team at: + +Email: [support@geekworm.com](mailto:support@geekworm.com) + +Thank you for using the "x735-script" to manage your x735 Power Management and Cooling Expansion Board. We hope this script enhances your Raspberry Pi experience and ensures smooth operations with improved cooling and power management capabilities. Should you need any assistance, feel free to contact our support team via email. Happy tinkering! \ No newline at end of file diff --git a/pwm_fan_control.py b/pwm_fan_control.py deleted file mode 100644 index d14611c..0000000 --- a/pwm_fan_control.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python3 - -import RPi.GPIO as IO -import time -import subprocess - -servo = 13 -IO.setwarnings(False) -IO.setmode (IO.BCM) -IO.setup(servo,IO.OUT) -fan = IO.PWM(servo,200) -fan.start(0) - -def get_temp(): - output = subprocess.run(['vcgencmd', 'measure_temp'], capture_output=True) - temp_str = output.stdout.decode() - try: - return float(temp_str.split('=')[1].split('\'')[0]) - except (IndexError, ValueError): - raise RuntimeError('Could not get temperature') -while 1: - temp = get_temp() # Get the current CPU temperature - if temp > 70: # Check temperature threshhold, in degrees celcius - fan.ChangeDutyCycle(100) # Set fan duty based on temperature, 100 is max speed and 0 is min speed or off. - elif temp > 60: - fan.ChangeDutyCycle(85) - elif temp > 50: - fan.ChangeDutyCycle(60) - elif temp > 40: - fan.ChangeDutyCycle(50) - elif temp > 32: - fan.ChangeDutyCycle(45) - elif temp > 25: - fan.ChangeDutyCycle(40) - else: - fan.ChangeDutyCycle(0) - time.sleep(5) # Sleep for 5 seconds diff --git a/read_fan_speed.py b/read_fan_speed.py deleted file mode 100644 index 80d9b0a..0000000 --- a/read_fan_speed.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/python -# this python code is base python 3, not python 2 -# -*- coding: utf-8 -*- -import RPi.GPIO as GPIO -import time - -TACH = 16 -PULSE = 2 -WAIT_TIME = 1 - -GPIO.setmode(GPIO.BCM) -GPIO.setwarnings(False) -GPIO.setup(TACH, GPIO.IN, pull_up_down=GPIO.PUD_UP) - -t = time.time() -rpm = 0 - -def fell(n): - global t - global rpm - - dt = time.time() - t - if dt < 0.005: return - - freq = 1 / dt - rpm = (freq / PULSE) * 60 - t = time.time() - -GPIO.add_event_detect(TACH, GPIO.FALLING, fell) - -try: - while True: - # print "%.f RPM" % rpm - print("%.f RPM" % rpm) - rpm = 0 - time.sleep(1) - -except KeyboardInterrupt: - GPIO.cleanup() diff --git a/x735-pwr.sh b/x735-pwr.sh deleted file mode 100644 index 407aae4..0000000 --- a/x735-pwr.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -SHUTDOWN=5 -REBOOTPULSEMINIMUM=200 -REBOOTPULSEMAXIMUM=600 -echo "$SHUTDOWN" > /sys/class/gpio/export -echo "in" > /sys/class/gpio/gpio$SHUTDOWN/direction -BOOT=12 -echo "$BOOT" > /sys/class/gpio/export -echo "out" > /sys/class/gpio/gpio$BOOT/direction -echo "1" > /sys/class/gpio/gpio$BOOT/value - -echo "Your device are shutting down..." - -while [ 1 ]; do - shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value) - if [ $shutdownSignal = 0 ]; then - /bin/sleep 0.2 - else - pulseStart=$(date +%s%N | cut -b1-13) - while [ $shutdownSignal = 1 ]; do - /bin/sleep 0.02 - if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMAXIMUM ]; then - echo "Your device are shutting down", SHUTDOWN, ", halting Rpi ..." - sudo poweroff - exit - fi - shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value) - done - if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMINIMUM ]; then - echo "Your device are rebooting", SHUTDOWN, ", recycling Rpi ..." - sudo reboot - exit - fi - fi -done \ No newline at end of file diff --git a/x735-script-pkg/DEBIAN/changelog b/x735-script-pkg/DEBIAN/changelog new file mode 100644 index 0000000..f72c89b --- /dev/null +++ b/x735-script-pkg/DEBIAN/changelog @@ -0,0 +1,8 @@ +x735-script (3.0.1) UNRELEASED; urgency=medium + + * Initial Release. [v3.0.0] + * edit README [v3.0.1] + * update checkout repo and the changelog_msg [v3.0.1] + * update action deps versions [v3.0.1] + + -- Molham Al Nasr Sat, 04 Jan 2025 11:23:36 +0000 diff --git a/x735-script-pkg/DEBIAN/compat b/x735-script-pkg/DEBIAN/compat new file mode 100644 index 0000000..3cacc0b --- /dev/null +++ b/x735-script-pkg/DEBIAN/compat @@ -0,0 +1 @@ +12 \ No newline at end of file diff --git a/x735-script-pkg/DEBIAN/control b/x735-script-pkg/DEBIAN/control new file mode 100644 index 0000000..d96f282 --- /dev/null +++ b/x735-script-pkg/DEBIAN/control @@ -0,0 +1,17 @@ +Package: x735-script +Description: Safe shutdown script and fan control for the x735 fan board from geekworm. This package contains a safe shutdown script and fan control for the x735 fan board. +Version: 3.0.1 +Section: misc +Priority: optional +Maintainer: Molham Al Nasr +Architecture: all +Build-Depends: debhelper (>= 12) +Standards-Version: 4.5.1 +Homepage: https://github.com/molhamalnasr/x735-script +Source: x735-script +Files: + /usr/bin/x735off + /usr/lib/x735-script/x735-fan.sh + /usr/lib/x735-script/x735-pwr.sh + /lib/systemd/system/x735-fan.service + /lib/systemd/system/x735-pwr.service diff --git a/x735-script-pkg/DEBIAN/postinst b/x735-script-pkg/DEBIAN/postinst new file mode 100644 index 0000000..62fdc62 --- /dev/null +++ b/x735-script-pkg/DEBIAN/postinst @@ -0,0 +1,80 @@ +#!/bin/bash + +set -e + +readonly LINE_TO_ADD="dtoverlay=pwm-2chan,pin2=13,func2=4" +readonly CONFIG_FILE="/boot/firmware/config.txt" +readonly BACKUP_FILE="${CONFIG_FILE}.backup" + +readonly PERMISSION_DENIED="151" +readonly FILE_NOT_EXIST="152" + +terminate() { + local -r msg=$1 + local -r err_code=${2:-150} + echo "${msg}" >&2 + exit "${err_code}" +} + +# Check if the script is not running with root privileges (sudo) +if [[ $EUID -ne 0 ]]; then + terminate "This script must be run with root privileges (sudo)." "${PERMISSION_DENIED}" +fi + +# Check if the config file exists +if [[ ! -e "${CONFIG_FILE}" ]]; then + terminate "Config file not found: ${CONFIG_FILE}" "${FILE_NOT_EXIST}" +fi + +# Backup the config file +cp -p "${CONFIG_FILE}" "${BACKUP_FILE}" + +# Search for the all directive in $CONFIG_FILE +search_directive=$(grep -n '^\[all\]' "${CONFIG_FILE}") + +# Create the [all] directive in $CONFIG_FILE, if it doesn't exist and append the $LINE_TO_ADD to it +if [[ -z "${search_directive}" ]]; then + echo "[all]" >> "${CONFIG_FILE}" + echo "${LINE_TO_ADD}" >> "${CONFIG_FILE}" + exit 0 +fi + +# Get the line number of the last defined "all" directive +last_directive_line_number=$(echo "${search_directive}" | cut -d':' -f1 | tail -n1) + +# Check if the $LINE_TO_ADD exists in the last [all] directive +line_exist=$(sed -n "${last_directive_line_number},/\[.*\]/ {/^\s*${LINE_TO_ADD}/=}" "${CONFIG_FILE}") + +if [[ -n "${line_exist}" ]]; then + echo "The line '${LINE_TO_ADD}' is already present in the right place in '${CONFIG_FILE}'" + echo "Continuing with the installation..." + exit 0 +fi + +# If the $LINE_TO_ADD exists but it's commented out, uncomment it. +line_commented=$(sed -n "${last_directive_line_number},/\[.*\]/ {/^\s*#*\s*${LINE_TO_ADD}/=}" "${CONFIG_FILE}") +if [[ -n "${line_commented}" ]]; then + echo "The line '${LINE_TO_ADD}' is already present in '${CONFIG_FILE}' but it is commented out" + echo "Uncommenting '${LINE_TO_ADD}'" + sed -i "${line_commented} s/^\s*#*\s*//" "${CONFIG_FILE}" + exit 0 +fi + +# If the "all" directive exists but the $LINE_TO_ADD does not exist, append the $LINE_TO_ADD to the all directive. +echo "Adding the line '${LINE_TO_ADD}' for the first time in '${CONFIG_FILE}'" +sed -i "${last_directive_line_number} a ${LINE_TO_ADD}" "${CONFIG_FILE}" + +# granting permession for all executables. +chmod +x /usr/bin/x735off +chmod +x /usr/lib/x735-script/scripts/*.sh + +# Enable and start the services after package installation +echo "enabling and starting the services..." +if [ -x /bin/systemctl ]; then + systemctl daemon-reload + systemctl enable x735-fan.service + systemctl enable x735-pwr.service +fi + +echo -e "\033[31mPlease restart the device by running the following command:\033[0m" +echo -e "\t'sudo reboot'" diff --git a/x735-script-pkg/DEBIAN/postrm b/x735-script-pkg/DEBIAN/postrm new file mode 100644 index 0000000..2a55233 --- /dev/null +++ b/x735-script-pkg/DEBIAN/postrm @@ -0,0 +1,8 @@ +#!/bin/bash + +readonly CONFIG_FILE="/boot/config.txt" + +sudo sed -i '/^\[all\]$/,/^\[.*\]$/ s/dtoverlay=pwm-2chan,pin2=13,func2=4$//' "${CONFIG_FILE}" + +echo -e "\033[31mPlease restart the device by running the following command:\033[0m" +echo -e "\t'sudo reboot'" \ No newline at end of file diff --git a/x735-script-pkg/DEBIAN/prerm b/x735-script-pkg/DEBIAN/prerm new file mode 100644 index 0000000..944735d --- /dev/null +++ b/x735-script-pkg/DEBIAN/prerm @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +# Enable and start the services after package installation +echo "disabling and stopping the services..." +if [ -x /bin/systemctl ]; then + sudo systemctl stop x735-fan.service + sudo systemctl stop x735-pwr.service + sudo systemctl disable x735-fan.service + sudo systemctl disable x735-pwr.service + sudo systemctl daemon-reload +fi \ No newline at end of file diff --git a/x735-script-pkg/DEBIAN/rules b/x735-script-pkg/DEBIAN/rules new file mode 100644 index 0000000..cbe925d --- /dev/null +++ b/x735-script-pkg/DEBIAN/rules @@ -0,0 +1,3 @@ +#!/usr/bin/make -f +%: + dh $@ diff --git a/x735-script-pkg/LICENSE b/x735-script-pkg/LICENSE new file mode 100644 index 0000000..b57d216 --- /dev/null +++ b/x735-script-pkg/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Geekworm.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/x735-script-pkg/README.md b/x735-script-pkg/README.md new file mode 100644 index 0000000..e35e18e --- /dev/null +++ b/x735-script-pkg/README.md @@ -0,0 +1,5 @@ +# x735-script + +User Guide: https://wiki.geekworm.com/X735-script + +Email: support@geekworm.com diff --git a/x735-fan.service b/x735-script-pkg/lib/systemd/system/x735-fan.service similarity index 72% rename from x735-fan.service rename to x735-script-pkg/lib/systemd/system/x735-fan.service index 22c9a27..173114c 100644 --- a/x735-fan.service +++ b/x735-script-pkg/lib/systemd/system/x735-fan.service @@ -2,8 +2,8 @@ Description=Daemon to monitor and control fan speed [Service] -ExecStart=/usr/local/bin/x735-fan.sh Type=simple +ExecStart=/usr/lib/x735-script/scripts/x735-fan.sh Restart=always [Install] diff --git a/x735-pwr.service b/x735-script-pkg/lib/systemd/system/x735-pwr.service similarity index 74% rename from x735-pwr.service rename to x735-script-pkg/lib/systemd/system/x735-pwr.service index 0d85694..56f3045 100644 --- a/x735-pwr.service +++ b/x735-script-pkg/lib/systemd/system/x735-pwr.service @@ -3,7 +3,7 @@ Description=Run Hardware Power Management & Safe Shutdown daemon [Service] Type=simple -ExecStart=/usr/local/bin/x735-pwr.sh +ExecStart=/usr/lib/x735-script/scripts/x735-pwr.sh Restart=always [Install] diff --git a/x735-script-pkg/usr/bin/x735off b/x735-script-pkg/usr/bin/x735off new file mode 100644 index 0000000..bae3344 --- /dev/null +++ b/x735-script-pkg/usr/bin/x735off @@ -0,0 +1,69 @@ +#!/bin/bash + +set -e + +SLEEP=${1:-4} + +readonly BUTTON=20 +readonly GPIO_PATH="/sys/class/gpio" +readonly BUTTON_PATH="${GPIO_PATH}/gpio${BUTTON}" + +readonly PERMISSION_DENIED="151" +readonly INVALID_CLI_ARG_TYPE="153" + +readonly STATE_EXPORT="export" +readonly STATE_UNEXPORT="unexport" + +terminate() { + local -r msg=$1 + local -r err_code=${2:-150} + echo "${msg}" >&2 + exit "${err_code}" +} + +# Guard clause to check if the script is running with root privileges (sudo) +if [[ $EUID -ne 0 ]]; then + terminate "This script must be run with root privileges (sudo)." "${PERMISSION_DENIED}" +fi + +if ! [[ $SLEEP =~ ^[0-9\.]+$ ]] ; then + terminate "error: sleep time not a number" "${INVALID_CLI_ARG_TYPE}" +fi + +# export / unexport a GPIO +un_export_gpio() { + local -r state="$1" + + echo "${BUTTON}" > "${GPIO_PATH}/${state}" +} + +modify_gpio_val() { + local -r file="$1" + local -r value="$2" + + echo "${value}" > "${BUTTON_PATH}/${file}" +} + +gpio_cleanup() { + un_export_gpio "${STATE_UNEXPORT}" + terminate "unexpected error..." +} + +# Main method +function __main__ { + un_export_gpio "${STATE_EXPORT}" + + # Handle exit and interrupt signals to cleanup GPIO + trap gpio_cleanup EXIT SIGINT SIGTERM + + modify_gpio_val "direction" "out" + modify_gpio_val "value" "1" + + # Broadcast the sutdown message to all tty sessions. + printf "Your device will shut down in %d seconds...\n" "$SLEEP" | wall + /bin/sleep $SLEEP + + modify_gpio_val "value" "0" +} + +__main__ \ No newline at end of file diff --git a/x735-fan.sh b/x735-script-pkg/usr/lib/x735-script/scripts/x735-fan.sh similarity index 100% rename from x735-fan.sh rename to x735-script-pkg/usr/lib/x735-script/scripts/x735-fan.sh diff --git a/x735-script-pkg/usr/lib/x735-script/scripts/x735-pwr.sh b/x735-script-pkg/usr/lib/x735-script/scripts/x735-pwr.sh new file mode 100644 index 0000000..cf8c115 --- /dev/null +++ b/x735-script-pkg/usr/lib/x735-script/scripts/x735-pwr.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +set -e + +readonly SHUTDOWN=5 +readonly BOOT=12 +readonly GPIO_PATH="/sys/class/gpio" +readonly REBOOT_PULSE_MINIMUM="200" +readonly REBOOT_PULSE_MAXIMUM="600" +readonly STATE_EXPORT="export" +readonly STATE_UNEXPORT="unexport" + +terminate() { + local -r msg=$1 + local -r err_code=${2:-150} + echo "${msg}" >&2 + exit "${err_code}" +} + +# Export / unexport a GPIO or modify its files' values +manage_gpio() { + local gpio_nr="$1" + local state="$2" + + # If function has 3 arguments, modify GPIO files' values + if [[ "${#}" -eq "3" ]]; then + local file="$2" + local value="$3" + echo "${value}" > "${GPIO_PATH}/gpio${gpio_nr}/${file}" + return 0 + fi + + echo "${gpio_nr}" > "${GPIO_PATH}/${state}" +} + +get_gpio_value() { + local -r gpio_nr="$1" + echo "$(<"${GPIO_PATH}/gpio${gpio_nr}/value")" +} + +gpio_cleanup() { + if [ -d "${GPIO_PATH}/gpio${SHUTDOWN}" ]; then + manage_gpio "${SHUTDOWN}" "${STATE_UNEXPORT}" + fi + + if [ -d "${GPIO_PATH}/gpio${BOOT}" ]; then + manage_gpio "${BOOT}" "${STATE_UNEXPORT}" + fi + + terminate "Unexpected exit..." +} + +init_gpio() { + if [ ! -d "${GPIO_PATH}/gpio${SHUTDOWN}" ]; then + manage_gpio "${SHUTDOWN}" "${STATE_EXPORT}" + fi + + if [ ! -d "${GPIO_PATH}/gpio${BOOT}" ]; then + manage_gpio "${BOOT}" "${STATE_EXPORT}" + fi + + manage_gpio "${SHUTDOWN}" "direction" "in" + manage_gpio "${BOOT}" "direction" "out" + manage_gpio "${BOOT}" "value" "1" +} + +echo "The x735-script is listening to the shutdown button clicks..." + +# Main method +__main__() { + # Handle exit and interrupt signals to clean up GPIO + trap gpio_cleanup EXIT SIGINT SIGTERM + + init_gpio + + while true; do + local shutdown_signal="$(get_gpio_value ${SHUTDOWN})" + if [ "$shutdown_signal" = "0" ]; then + sleep 0.2 + else + local pulse_start=$(($(date +%s%N | cut -b1-13))) + while [ "$shutdown_signal" = "1" ]; do + sleep 0.02 + local current_time=$(($(date +%s%N | cut -b1-13))) + local elapsed_time=$((current_time - pulse_start)) + if [ "$elapsed_time" -gt "$REBOOT_PULSE_MAXIMUM" ]; then + echo "Your device is shutting down (GPIO $SHUTDOWN), halting RPi ..." + sudo poweroff + exit + fi + shutdown_signal="$(get_gpio_value ${SHUTDOWN})" + done + if [ "$elapsed_time" -gt "$REBOOT_PULSE_MINIMUM" ]; then + echo "Your device is rebooting (GPIO $SHUTDOWN), recycling RPi ..." + sudo reboot + exit + fi + fi + done +} + +__main__ diff --git a/x735-softsd.sh b/x735-softsd.sh deleted file mode 100644 index 9a52c3e..0000000 --- a/x735-softsd.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -BUTTON=20 - -echo "$BUTTON" > /sys/class/gpio/export; -echo "out" > /sys/class/gpio/gpio$BUTTON/direction -echo "1" > /sys/class/gpio/gpio$BUTTON/value - -SLEEP=${1:-4} - -re='^[0-9\.]+$' -if ! [[ $SLEEP =~ $re ]] ; then - echo "error: sleep time not a number" >&2; exit 1 -fi - -echo "Your device will shutting down in 4 seconds..." -/bin/sleep $SLEEP - -echo "0" > /sys/class/gpio/gpio$BUTTON/value