From 4626e5d9f6e1f5276e6dee304929eed3f3d27970 Mon Sep 17 00:00:00 2001 From: Alvin Schiller <103769832+AlvinSchiller@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:28:46 +0200 Subject: [PATCH 1/7] Installation fixes and refactoring (#2350) * fix: added vars into logfile * refactor: split finish_installation routine extracted rfid_reader setup * refactor: removed "Replace homedir" code in installation v2 will not be refactored to be used with a different user * fix: add escaping for all vars written to PhonieboxInstall.conf * refactor: change and unify sed delimiter to pipe char * fix: add escaping for sed replacements * refactor: moved network setup into install_main * refactor: introduce helper script, moved functions * fix: add escaping for autohotspot * fix: command for special char handling * fix: update testdata for special chars --- scripts/helperscripts/inc.helper.sh | 17 + scripts/helperscripts/inc.networkHelper.sh | 2 +- scripts/helperscripts/setup_autohotspot.sh | 59 ++-- scripts/installscripts/install-jukebox.sh | 315 +++++++++--------- ...installation_autohotspot_NetworkManager.sh | 12 +- .../run_installation_autohotspot_dhcpcd.sh | 12 +- .../tests/run_installation_classic.sh | 4 +- .../tests/run_installation_rfid.sh | 4 +- .../tests/run_installation_spotify.sh | 4 +- .../tests/run_installation_staticip_dhcpcd.sh | 8 +- 10 files changed, 221 insertions(+), 216 deletions(-) create mode 100644 scripts/helperscripts/inc.helper.sh diff --git a/scripts/helperscripts/inc.helper.sh b/scripts/helperscripts/inc.helper.sh new file mode 100644 index 000000000..04cac78c8 --- /dev/null +++ b/scripts/helperscripts/inc.helper.sh @@ -0,0 +1,17 @@ +# Reads a textfile and pipes all lines as args to the given command. +# Does filter out comments. +# Arguments: +# 1 : textfile to read +# 2... : command to receive args (e.g. 'echo', 'apt-get -y install', ...) +call_with_args_from_file () { + local package_file="$1" + shift + + sed 's|#.*||g' ${package_file} | xargs "$@" +} + +# escape relevant chars for strings used in 'sed' commands. implies delimiter char '|' +escape_for_sed() { + local escaped=$(echo "$1" | sed -e 's/[\&'\''|]/\\&/g') + echo "$escaped" +} diff --git a/scripts/helperscripts/inc.networkHelper.sh b/scripts/helperscripts/inc.networkHelper.sh index ff0df181e..f96a9138d 100644 --- a/scripts/helperscripts/inc.networkHelper.sh +++ b/scripts/helperscripts/inc.networkHelper.sh @@ -74,7 +74,7 @@ add_wireless_network() { local wpa_network_with_dummy_psk=$(wpa_passphrase "$ssid" "dummypsk") if echo "$wpa_network_with_dummy_psk" | grep -qF 'network='; then local wpa_network=$(echo "$wpa_network_with_dummy_psk" | sed -e '/#psk/d' -e "s/psk=.*$/psk=${pass}/" -e "/^}/i\\\tpriority=${prio}" ) - sudo bash -c "echo '${wpa_network}' >> $WPA_CONF" + echo "${wpa_network}" | sudo tee -a "$WPA_CONF" > /dev/null fi fi fi diff --git a/scripts/helperscripts/setup_autohotspot.sh b/scripts/helperscripts/setup_autohotspot.sh index 2ae9cd361..61d0e2297 100644 --- a/scripts/helperscripts/setup_autohotspot.sh +++ b/scripts/helperscripts/setup_autohotspot.sh @@ -14,18 +14,6 @@ if [[ "$#" -lt 2 || ( "${AUTOHOTSPOTconfig}" != "NO" && "${AUTOHOTSPOTconfig}" ! exit 1 fi -# Reads a textfile and pipes all lines as args to the given command. -# Does filter out comments. -# Arguments: -# 1 : textfile to read -# 2... : command to receive args (e.g. 'echo', 'apt-get -y install', ...) -call_with_args_from_file () { - local package_file="$1" - shift - - sed 's/#.*//g' ${package_file} | xargs "$@" -} - _get_last_ip_segment() { local ip="$1" echo $ip | cut -d'.' -f1-3 @@ -80,6 +68,7 @@ wifi_interface=wlan0 ip_without_last_segment=$(_get_last_ip_segment $AUTOHOTSPOTip) autohotspot_profile="Phoniebox_Hotspot" +source "${JUKEBOX_HOME_DIR}"/scripts/helperscripts/inc.helper.sh source "${JUKEBOX_HOME_DIR}"/scripts/helperscripts/inc.networkHelper.sh _install_autohotspot_dhcpcd() { @@ -103,8 +92,8 @@ _install_autohotspot_dhcpcd() { config_file_backup "${dnsmasq_conf}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/dnsmasq.conf "${dnsmasq_conf}" - sudo sed -i "s|%WIFI_INTERFACE%|${wifi_interface}|g" "${dnsmasq_conf}" - sudo sed -i "s|%IP_WITHOUT_LAST_SEGMENT%|${ip_without_last_segment}|g" "${dnsmasq_conf}" + sudo sed -i "s|%WIFI_INTERFACE%|$(escape_for_sed "${wifi_interface}")|g" "${dnsmasq_conf}" + sudo sed -i "s|%IP_WITHOUT_LAST_SEGMENT%|$(escape_for_sed "${ip_without_last_segment}")|g" "${dnsmasq_conf}" sudo chown root:root "${dnsmasq_conf}" sudo chmod 644 "${dnsmasq_conf}" @@ -112,10 +101,10 @@ _install_autohotspot_dhcpcd() { config_file_backup "${hostapd_conf}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/hostapd.conf "${hostapd_conf}" - sudo sed -i "s|%WIFI_INTERFACE%|${wifi_interface}|g" "${hostapd_conf}" - sudo sed -i "s|%AUTOHOTSPOTssid%|${AUTOHOTSPOTssid}|g" "${hostapd_conf}" - sudo sed -i "s|%AUTOHOTSPOTpass%|${AUTOHOTSPOTpass}|g" "${hostapd_conf}" - sudo sed -i "s|%AUTOHOTSPOTcountryCode%|${AUTOHOTSPOTcountryCode}|g" "${hostapd_conf}" + sudo sed -i "s|%WIFI_INTERFACE%|$(escape_for_sed "${wifi_interface}")|g" "${hostapd_conf}" + sudo sed -i "s|%AUTOHOTSPOTssid%|$(escape_for_sed "${AUTOHOTSPOTssid}")|g" "${hostapd_conf}" + sudo sed -i "s|%AUTOHOTSPOTpass%|$(escape_for_sed "${AUTOHOTSPOTpass}")|g" "${hostapd_conf}" + sudo sed -i "s|%AUTOHOTSPOTcountryCode%|$(escape_for_sed "${AUTOHOTSPOTcountryCode}")|g" "${hostapd_conf}" sudo chown root:root "${hostapd_conf}" sudo chmod 644 "${hostapd_conf}" @@ -123,7 +112,7 @@ _install_autohotspot_dhcpcd() { config_file_backup "${hostapd_deamon}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/hostapd "${hostapd_deamon}" - sudo sed -i "s|%HOSTAPD_CONF%|${hostapd_conf}|g" "${hostapd_deamon}" + sudo sed -i "s|%HOSTAPD_CONF%|$(escape_for_sed "${hostapd_conf}")|g" "${hostapd_deamon}" sudo chown root:root "${hostapd_deamon}" sudo chmod 644 "${hostapd_deamon}" @@ -136,28 +125,28 @@ _install_autohotspot_dhcpcd() { fi if [[ ! $(grep -w "${dhcpcd_conf_nohook_wpa_supplicant}" ${dhcpcd_conf}) ]]; then - sudo bash -c "echo ${dhcpcd_conf_nohook_wpa_supplicant} >> ${dhcpcd_conf}" + echo "${dhcpcd_conf_nohook_wpa_supplicant}" | sudo tee -a "${dhcpcd_conf}" > /dev/null fi # create service to trigger hotspot sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/autohotspot "${autohotspot_script}" - sudo sed -i "s|%WIFI_INTERFACE%|${wifi_interface}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_IP%|${AUTOHOTSPOTip}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_SERVICE_DAEMON%|${autohotspot_service_daemon}|g" "${autohotspot_script}" + sudo sed -i "s|%WIFI_INTERFACE%|$(escape_for_sed "${wifi_interface}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_IP%|$(escape_for_sed "${AUTOHOTSPOTip}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_SERVICE_DAEMON%|$(escape_for_sed "${autohotspot_service_daemon}")|g" "${autohotspot_script}" sudo chmod +x "${autohotspot_script}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/autohotspot-daemon.service "${autohotspot_service_daemon_path}" - sudo sed -i "s|%WIFI_INTERFACE%|${wifi_interface}|g" "${autohotspot_service_daemon_path}" + sudo sed -i "s|%WIFI_INTERFACE%|$(escape_for_sed "${wifi_interface}")|g" "${autohotspot_service_daemon_path}" sudo chown root:root "${autohotspot_service_daemon_path}" sudo chmod 644 "${autohotspot_service_daemon_path}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/autohotspot.service "${autohotspot_service_path}" - sudo sed -i "s|%AUTOHOTSPOT_SCRIPT%|${autohotspot_script}|g" "${autohotspot_service_path}" + sudo sed -i "s|%AUTOHOTSPOT_SCRIPT%|$(escape_for_sed "${autohotspot_script}")|g" "${autohotspot_service_path}" sudo chown root:root "${autohotspot_service_path}" sudo chmod 644 "${autohotspot_service_path}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/dhcpcd/autohotspot.timer "${autohotspot_timer_path}" - sudo sed -i "s|%AUTOHOTSPOT_SERVICE%|${autohotspot_service}|g" "${autohotspot_timer_path}" + sudo sed -i "s|%AUTOHOTSPOT_SERVICE%|$(escape_for_sed "${autohotspot_service}")|g" "${autohotspot_timer_path}" sudo chown root:root "${autohotspot_timer_path}" sudo chmod 644 "${autohotspot_timer_path}" @@ -212,22 +201,22 @@ _install_autohotspot_NetworkManager() { # create service to trigger hotspot sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/NetworkManager/autohotspot "${autohotspot_script}" - sudo sed -i "s|%WIFI_INTERFACE%|${wifi_interface}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_PROFILE%|${autohotspot_profile}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_SSID%|${AUTOHOTSPOTssid}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_PASSWORD%|${AUTOHOTSPOTpass}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_IP%|${AUTOHOTSPOTip}|g" "${autohotspot_script}" - sudo sed -i "s|%IP_WITHOUT_LAST_SEGMENT%|${ip_without_last_segment}|g" "${autohotspot_script}" - sudo sed -i "s|%AUTOHOTSPOT_TIMER_NAME%|${autohotspot_timer}|g" "${autohotspot_script}" + sudo sed -i "s|%WIFI_INTERFACE%|$(escape_for_sed "${wifi_interface}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_PROFILE%|$(escape_for_sed "${autohotspot_profile}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_SSID%|$(escape_for_sed "${AUTOHOTSPOTssid}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_PASSWORD%|$(escape_for_sed "${AUTOHOTSPOTpass}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_IP%|$(escape_for_sed "${AUTOHOTSPOTip}")|g" "${autohotspot_script}" + sudo sed -i "s|%IP_WITHOUT_LAST_SEGMENT%|$(escape_for_sed "${ip_without_last_segment}")|g" "${autohotspot_script}" + sudo sed -i "s|%AUTOHOTSPOT_TIMER_NAME%|$(escape_for_sed "${autohotspot_timer}")|g" "${autohotspot_script}" sudo chmod +x "${autohotspot_script}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/NetworkManager/autohotspot.service "${autohotspot_service_path}" - sudo sed -i "s|%AUTOHOTSPOT_SCRIPT%|${autohotspot_script}|g" "${autohotspot_service_path}" + sudo sed -i "s|%AUTOHOTSPOT_SCRIPT%|$(escape_for_sed "${autohotspot_script}")|g" "${autohotspot_service_path}" sudo chown root:root "${autohotspot_service_path}" sudo chmod 644 "${autohotspot_service_path}" sudo cp "${JUKEBOX_HOME_DIR}"/misc/sampleconfigs/autohotspot/NetworkManager/autohotspot.timer "${autohotspot_timer_path}" - sudo sed -i "s|%AUTOHOTSPOT_SERVICE%|${autohotspot_service}|g" "${autohotspot_timer_path}" + sudo sed -i "s|%AUTOHOTSPOT_SERVICE%|$(escape_for_sed "${autohotspot_service}")|g" "${autohotspot_timer_path}" sudo chown root:root "${autohotspot_timer_path}" sudo chmod 644 "${autohotspot_timer_path}" diff --git a/scripts/installscripts/install-jukebox.sh b/scripts/installscripts/install-jukebox.sh index 6faf90cfd..9025afeba 100644 --- a/scripts/installscripts/install-jukebox.sh +++ b/scripts/installscripts/install-jukebox.sh @@ -19,8 +19,6 @@ PATHDATA="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" GIT_BRANCH=${GIT_BRANCH:-master} GIT_URL=${GIT_URL:-https://github.com/MiczFlor/RPi-Jukebox-RFID.git} -echo GIT_BRANCH $GIT_BRANCH -echo GIT_URL $GIT_URL DATETIME=$(date +"%Y%m%d_%H%M%S") @@ -29,8 +27,7 @@ JOB="${SCRIPTNAME}" CURRENT_USER="${SUDO_USER:-$(whoami)}" HOME_DIR=$(getent passwd "$CURRENT_USER" | cut -d: -f6) -echo "Current User: $CURRENT_USER" -echo "User home dir: $HOME_DIR" + JUKEBOX_HOME_DIR="${HOME_DIR}/RPi-Jukebox-RFID" LOGDIR="${HOME_DIR}"/phoniebox_logs @@ -38,7 +35,6 @@ JUKEBOX_BACKUP_DIR="${HOME_DIR}/BACKUP" # Get the Raspberry Pi OS codename (e.g. buster, bullseye, ...) OS_CODENAME="$( . /etc/os-release; printf '%s\n' "$VERSION_CODENAME"; )" -printf "Used Raspberry Pi OS: ${OS_CODENAME}\n" WIFI_INTERFACE="wlan0" @@ -94,18 +90,14 @@ log_close() { fi } -escape_for_shell() { +# local function as it is needed before the repo is checked out! +_escape_for_shell() { local escaped="${1//\"/\\\"}" escaped="${escaped//\`/\\\`}" escaped="${escaped//\$/\\\$}" echo "$escaped" } -escape_for_sed() { - local escaped=$(echo "$1" | sed -e 's/[\/&'\'']/\\&/g') - echo "$escaped" -} - checkPrerequisite() { #currently the user 'pi' is mandatory #https://github.com/MiczFlor/RPi-Jukebox-RFID/issues/1785 @@ -227,12 +219,12 @@ case "$response" in *) # append variables to config file { - echo "WIFIconfig=\"$WIFIconfig\""; - echo "WIFIcountryCode=\"$WIFIcountryCode\""; - echo "WIFIssid=\"$WIFIssid\""; - echo "WIFIpass=\"$WIFIpass\""; - echo "WIFIip=\"$WIFIip\""; - echo "WIFIipRouter=\"$WIFIipRouter\""; + echo "WIFIconfig=\"$(_escape_for_shell "$WIFIconfig")\""; + echo "WIFIcountryCode=\"$(_escape_for_shell "$WIFIcountryCode")\""; + echo "WIFIssid=\"$(_escape_for_shell "$WIFIssid")\""; + echo "WIFIpass=\"$(_escape_for_shell "$WIFIpass")\""; + echo "WIFIip=\"$(_escape_for_shell "$WIFIip")\""; + echo "WIFIipRouter=\"$(_escape_for_shell "$WIFIipRouter")\""; } >> "${HOME_DIR}/PhonieboxInstall.conf" ;; esac @@ -241,9 +233,9 @@ case "$response" in WIFIconfig=NO echo "You want to configure WiFi later." # append variables to config file - echo "WIFIconfig=$WIFIconfig" >> "${HOME_DIR}/PhonieboxInstall.conf" + echo "WIFIconfig=\"$(_escape_for_shell "$WIFIconfig")\"" >> "${HOME_DIR}/PhonieboxInstall.conf" # make a fallback for WiFi Country Code, because we need that even without WiFi config - echo "WIFIcountryCode=DE" >> "${HOME_DIR}/PhonieboxInstall.conf" + echo "WIFIcountryCode=\"$(_escape_for_shell "DE")\"" >> "${HOME_DIR}/PhonieboxInstall.conf" ;; esac read -rp "Hit ENTER to proceed to the next step." INPUT @@ -315,18 +307,18 @@ case "$response" in esac # append variables to config file { - echo "AUTOHOTSPOTconfig=\"$AUTOHOTSPOTconfig\""; - echo "AUTOHOTSPOTssid=\"$AUTOHOTSPOTssid\""; - echo "AUTOHOTSPOTcountryCode=\"$AUTOHOTSPOTcountryCode\""; - echo "AUTOHOTSPOTpass=\"$AUTOHOTSPOTpass\""; - echo "AUTOHOTSPOTip=\"$AUTOHOTSPOTip\""; + echo "AUTOHOTSPOTconfig=\"$(_escape_for_shell "$AUTOHOTSPOTconfig")\""; + echo "AUTOHOTSPOTssid=\"$(_escape_for_shell "$AUTOHOTSPOTssid")\""; + echo "AUTOHOTSPOTcountryCode=\"$(_escape_for_shell "$AUTOHOTSPOTcountryCode")\""; + echo "AUTOHOTSPOTpass=\"$(_escape_for_shell "$AUTOHOTSPOTpass")\""; + echo "AUTOHOTSPOTip=\"$(_escape_for_shell "$AUTOHOTSPOTip")\""; } >> "${HOME_DIR}/PhonieboxInstall.conf" ;; *) AUTOHOTSPOTconfig=NO echo "You don't want to configure Autohotspot." # append variables to config file - echo "AUTOHOTSPOTconfig=$AUTOHOTSPOTconfig" >> "${HOME_DIR}/PhonieboxInstall.conf" + echo "AUTOHOTSPOTconfig=\"$(_escape_for_shell "$AUTOHOTSPOTconfig")\"" >> "${HOME_DIR}/PhonieboxInstall.conf" ;; esac @@ -442,7 +434,7 @@ check_existing() { ;; esac # append variables to config file - echo "EXISTINGuseRfidConf=$EXISTINGuseRfidConf" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuseRfidConf=\"$(_escape_for_shell "$EXISTINGuseRfidConf")\"" >> "${local_home_dir}/PhonieboxInstall.conf" read -rp "RFID shortcuts to play audio folders? [Y/n] " response case "$response" in @@ -454,7 +446,7 @@ check_existing() { ;; esac # append variables to config file - echo "EXISTINGuseRfidLinks=$EXISTINGuseRfidLinks" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuseRfidLinks=\"$(_escape_for_shell "$EXISTINGuseRfidLinks")\"" >> "${local_home_dir}/PhonieboxInstall.conf" read -rp "Audio folders: use existing? [Y/n] " response case "$response" in @@ -466,7 +458,7 @@ check_existing() { ;; esac # append variables to config file - echo "EXISTINGuseAudio=$EXISTINGuseAudio" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuseAudio=\"$(_escape_for_shell "$EXISTINGuseAudio")\"" >> "${local_home_dir}/PhonieboxInstall.conf" read -rp "Sound effects: use existing startup / shutdown sounds? [Y/n] " response case "$response" in @@ -478,7 +470,7 @@ check_existing() { ;; esac # append variables to config file - echo "EXISTINGuseSounds=$EXISTINGuseSounds" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuseSounds=\"$(_escape_for_shell "$EXISTINGuseSounds")\"" >> "${local_home_dir}/PhonieboxInstall.conf" if [ "$(printf '%s\n' "2.1" "$(cat ${local_home_dir}/BACKUP/settings/version-number)" | sort -V | head -n1)" = "2.1" ]; then read -rp "GPIO: use existing file? [Y/n] " response @@ -500,7 +492,7 @@ https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Using-GPIO-hardware-buttons" config_gpio fi # append variables to config file - echo "EXISTINGuseGpio=$EXISTINGuseGpio" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuseGpio=\"$(_escape_for_shell "$EXISTINGuseGpio")\"" >> "${local_home_dir}/PhonieboxInstall.conf" read -rp "Button USB Encoder: use existing device and button mapping? [Y/n] " response case "$response" in @@ -512,7 +504,7 @@ https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Using-GPIO-hardware-buttons" ;; esac # append variables to config file - echo "EXISTINGuseButtonUSBEncoder=$EXISTINGuseButtonUSBEncoder" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuseButtonUSBEncoder=\"$(_escape_for_shell "$EXISTINGuseButtonUSBEncoder")\"" >> "${local_home_dir}/PhonieboxInstall.conf" echo "Thanks. Got it." echo "The existing install can be found in the BACKUP directory." @@ -521,7 +513,7 @@ https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Using-GPIO-hardware-buttons" esac fi # append variables to config file - echo "EXISTINGuse=$EXISTINGuse" >> "${local_home_dir}/PhonieboxInstall.conf" + echo "EXISTINGuse=\"$(_escape_for_shell "$EXISTINGuse")\"" >> "${local_home_dir}/PhonieboxInstall.conf" # Check if we found a Phoniebox install configuration earlier and ask if to run this now if [ "${EXISTINGusePhonieboxInstall}" == "YES" ]; then @@ -576,7 +568,7 @@ ${audio_interfaces} ;; esac # append variables to config file - echo "AUDIOiFace=\"$AUDIOiFace\"" >> "${HOME_DIR}/PhonieboxInstall.conf" + echo "AUDIOiFace=\"$(_escape_for_shell "$AUDIOiFace")\"" >> "${HOME_DIR}/PhonieboxInstall.conf" echo "Your iFace is called '$AUDIOiFace'" read -rp "Hit ENTER to proceed to the next step." INPUT } @@ -638,11 +630,11 @@ config_spotify() { esac # append variables to config file { - echo "SPOTinstall=\"$SPOTinstall\""; - echo "SPOTIuser=\"$(escape_for_shell "$SPOTIuser")\""; - echo "SPOTIpass=\"$(escape_for_shell "$SPOTIpass")\""; - echo "SPOTIclientid=\"$(escape_for_shell "$SPOTIclientid")\""; - echo "SPOTIclientsecret=\"$(escape_for_shell "$SPOTIclientsecret")\"" + echo "SPOTinstall=\"$(_escape_for_shell "$SPOTinstall")\""; + echo "SPOTIuser=\"$(_escape_for_shell "$SPOTIuser")\""; + echo "SPOTIpass=\"$(_escape_for_shell "$SPOTIpass")\""; + echo "SPOTIclientid=\"$(_escape_for_shell "$SPOTIclientid")\""; + echo "SPOTIclientsecret=\"$(_escape_for_shell "$SPOTIclientsecret")\"" } >> "${HOME_DIR}/PhonieboxInstall.conf" read -rp "Hit ENTER to proceed to the next step." INPUT } @@ -682,7 +674,7 @@ config_audio_folder() { ;; esac # append variables to config file - echo "DIRaudioFolders=\"$DIRaudioFolders\"" >> "${HOME_DIR}/PhonieboxInstall.conf" + echo "DIRaudioFolders=\"$(_escape_for_shell "$DIRaudioFolders")\"" >> "${HOME_DIR}/PhonieboxInstall.conf" echo "Your audio folders live in this dir:" echo "${DIRaudioFolders}" read -rp "Hit ENTER to proceed to the next step." INPUT @@ -723,7 +715,7 @@ config_gpio() { ;; esac # append variables to config file - echo "GPIOconfig=\"$GPIOconfig\"" >> "${HOME_DIR}/PhonieboxInstall.conf" + echo "GPIOconfig=\"$(_escape_for_shell "$GPIOconfig")\"" >> "${HOME_DIR}/PhonieboxInstall.conf" echo "" read -rp "Hit ENTER to proceed to the next step." INPUT } @@ -801,9 +793,7 @@ samba_config() { sudo chown root:root "${smb_conf}" sudo chmod 644 "${smb_conf}" # for $DIRaudioFolders using | as alternate regex delimiter because of the folder path slash - sudo sed -i 's|%DIRaudioFolders%|'"$DIRaudioFolders"'|' "${smb_conf}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${smb_conf}" + sudo sed -i 's|%DIRaudioFolders%|'"$(escape_for_sed "$DIRaudioFolders")"'|' "${smb_conf}" # Samba: create user 'pi' with password 'raspberry' # ToDo: use current user with a default password (echo "raspberry"; echo "raspberry") | sudo smbpasswd -s -a pi @@ -822,8 +812,6 @@ web_server_config() { sudo cp "${jukebox_dir}"/misc/sampleconfigs/lighttpd.conf-default.sample "${lighthttpd_conf}" sudo chown root:root "${lighthttpd_conf}" sudo chmod 644 "${lighthttpd_conf}" - # double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${lighthttpd_conf}" # Web server PHP7 fastcgi conf # -rw-r--r-- 1 root root 398 Apr 30 09:35 /etc/lighttpd/conf-available/15-fastcgi-php.conf @@ -844,18 +832,6 @@ web_server_config() { sudo chmod 440 "${sudoers_wwwdata}" } -# Reads a textfile and pipes all lines as args to the given command. -# Does filter out comments. -# Arguments: -# 1 : textfile to read -# 2... : command to receive args (e.g. 'echo', 'apt-get -y install', ...) -call_with_args_from_file () { - local package_file="$1" - shift - - sed 's/#.*//g' ${package_file} | xargs "$@" -} - install_main() { local jukebox_dir="$1" local apt_get="sudo apt-get -qq --yes" @@ -894,6 +870,14 @@ install_main() { # Start logging here log_open + echo "################################################" + echo "Interactive mode: ${INTERACTIVE}" + echo "GIT_BRANCH ${GIT_BRANCH}" + echo "GIT_URL ${GIT_URL}" + echo "Current User: ${CURRENT_USER}" + echo "User home dir: ${HOME_DIR}" + echo "Used Raspberry Pi OS: ${OS_CODENAME}" + # Add conffile into logfile for better debugging echo "################################################" grep -v -e "SPOTI" -e "WIFIpass" "${HOME_DIR}/PhonieboxInstall.conf" @@ -928,6 +912,10 @@ install_main() { cd "${HOME_DIR}" git clone ${GIT_URL} --branch "${GIT_BRANCH}" + source "${jukebox_dir}"/scripts/helperscripts/inc.helper.sh + source "${jukebox_dir}"/scripts/helperscripts/inc.networkHelper.sh + + # some packages are only available on raspberry pi's but not on test docker containers running on x86_64 machines if [[ $(uname -m) =~ ^armv.+$ ]]; then call_with_args_from_file "${jukebox_dir}"/packages-raspberrypi.txt ${apt_get} ${allow_downgrades} install @@ -1022,8 +1010,6 @@ install_main() { # create config file for web app from sample sudo cp "${jukebox_dir}"/htdocs/config.php.sample "${jukebox_dir}"/htdocs/config.php - # double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${jukebox_dir}"/htdocs/config.php # Starting web server and php7 sudo lighttpd-enable-mod fastcgi @@ -1058,24 +1044,16 @@ install_main() { # 2. install new ones - this is version > 1.1.8-beta RFID_READER_SERVICE="${systemd_dir}/phoniebox-rfid-reader.service" sudo cp "${jukebox_dir}"/misc/sampleconfigs/phoniebox-rfid-reader.service-default.sample "${RFID_READER_SERVICE}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${RFID_READER_SERVICE}" STARTUP_SCRIPT_SERVICE="${systemd_dir}/phoniebox-startup-scripts.service" sudo cp "${jukebox_dir}"/misc/sampleconfigs/phoniebox-startup-scripts.service-default.sample "${STARTUP_SCRIPT_SERVICE}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${STARTUP_SCRIPT_SERVICE}" IDLE_WATCHDOG_SERVICE="${systemd_dir}/phoniebox-idle-watchdog.service" sudo cp "${jukebox_dir}"/misc/sampleconfigs/phoniebox-idle-watchdog.service.sample "${IDLE_WATCHDOG_SERVICE}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${IDLE_WATCHDOG_SERVICE}" if [[ "${GPIOconfig}" == "YES" ]]; then GPIO_CONTROL_SERVICE="${systemd_dir}/phoniebox-gpio-control.service" sudo cp "${jukebox_dir}"/misc/sampleconfigs/phoniebox-gpio-control.service.sample "${GPIO_CONTROL_SERVICE}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${GPIO_CONTROL_SERVICE}" fi sudo chown root:root "${systemd_dir}"/phoniebox-*.service @@ -1097,11 +1075,9 @@ install_main() { # -rw-r----- 1 mpd audio 14043 Jul 17 20:16 /etc/mpd.conf sudo cp "${jukebox_dir}"/misc/sampleconfigs/mpd.conf.sample ${mpd_conf} # Change vars to match install config - sudo sed -i 's/%AUDIOiFace%/'"$AUDIOiFace"'/' "${mpd_conf}" + sudo sed -i 's|%AUDIOiFace%|'"$(escape_for_sed "$AUDIOiFace")"'|' "${mpd_conf}" # for $DIRaudioFolders using | as alternate regex delimiter because of the folder path slash - sudo sed -i 's|%DIRaudioFolders%|'"$DIRaudioFolders"'|' "${mpd_conf}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${mpd_conf}" + sudo sed -i 's|%DIRaudioFolders%|'"$(escape_for_sed "$DIRaudioFolders")"'|' "${mpd_conf}" sudo chown mpd:audio "${mpd_conf}" sudo chmod 640 "${mpd_conf}" @@ -1120,14 +1096,12 @@ install_main() { sudo locale-gen sudo cp "${jukebox_dir}"/misc/sampleconfigs/mopidy.conf.sample "${mopidy_conf}" # Change vars to match install config - sudo sed -i 's/%spotify_username%/'"$(escape_for_sed "$SPOTIuser")"'/' "${mopidy_conf}" - sudo sed -i 's/%spotify_password%/'"$(escape_for_sed "$SPOTIpass")"'/' "${mopidy_conf}" - sudo sed -i 's/%spotify_client_id%/'"$(escape_for_sed "$SPOTIclientid")"'/' "${mopidy_conf}" - sudo sed -i 's/%spotify_client_secret%/'"$(escape_for_sed "$SPOTIclientsecret")"'/' "${mopidy_conf}" + sudo sed -i 's|%spotify_username%|'"$(escape_for_sed "$SPOTIuser")"'|' "${mopidy_conf}" + sudo sed -i 's|%spotify_password%|'"$(escape_for_sed "$SPOTIpass")"'|' "${mopidy_conf}" + sudo sed -i 's|%spotify_client_id%|'"$(escape_for_sed "$SPOTIclientid")"'|' "${mopidy_conf}" + sudo sed -i 's|%spotify_client_secret%|'"$(escape_for_sed "$SPOTIclientsecret")"'|' "${mopidy_conf}" # for $DIRaudioFolders using | as alternate regex delimiter because of the folder path slash - sudo sed -i 's|%DIRaudioFolders%|'"$DIRaudioFolders"'|' "${mopidy_conf}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${mopidy_conf}" + sudo sed -i 's|%DIRaudioFolders%|'"$(escape_for_sed "$DIRaudioFolders")"'|' "${mopidy_conf}" fi # GPIO-Control @@ -1146,6 +1120,9 @@ install_main() { echo "classic" > "${jukebox_dir}"/settings/edition fi + wifi_settings "${jukebox_dir}" + autohotspot "${jukebox_dir}" + # / INSTALLATION ##################################################### } @@ -1172,7 +1149,7 @@ wifi_settings() { local wpa_supplicant_conf="/etc/wpa_supplicant/wpa_supplicant.conf" # -rw-rw-r-- 1 root netdev 137 Jul 16 08:53 /etc/wpa_supplicant/wpa_supplicant.conf sudo cp "${jukebox_dir}"/misc/sampleconfigs/wpa_supplicant.conf.sample "${wpa_supplicant_conf}" - sudo sed -i 's/%WIFIcountryCode%/'"$WIFIcountryCode"'/' "${wpa_supplicant_conf}" + sudo sed -i 's|%WIFIcountryCode%|'"$(escape_for_sed "$WIFIcountryCode")"'|' "${wpa_supplicant_conf}" sudo chown root:netdev "${wpa_supplicant_conf}" sudo chmod 664 "${wpa_supplicant_conf}" @@ -1184,11 +1161,11 @@ wifi_settings() { #-rw-rw-r-- 1 root netdev 0 Apr 17 11:25 /etc/dhcpcd.conf sudo cp "${jukebox_dir}"/misc/sampleconfigs/dhcpcd.conf-default-noHotspot.sample "${dhcpcd_conf}" # Change IP for router and Phoniebox - sudo sed -i 's/%WIFIinterface%/'"$WIFI_INTERFACE"'/' "${dhcpcd_conf}" - sudo sed -i 's/%WIFIip%/'"$WIFIip"'/' "${dhcpcd_conf}" - sudo sed -i 's/%WIFIipRouter%/'"$WIFIipRouter"'/' "${dhcpcd_conf}" - sudo sed -i 's/%WIFIipExtDNS%/'"$wifiExtDNS"'/' "${dhcpcd_conf}" - sudo sed -i 's/%WIFIcountryCode%/'"$WIFIcountryCode"'/' "${dhcpcd_conf}" + sudo sed -i 's|%WIFIinterface%|'"$(escape_for_sed "$WIFI_INTERFACE")"'|' "${dhcpcd_conf}" + sudo sed -i 's|%WIFIip%|'"$(escape_for_sed "$WIFIip")"'|' "${dhcpcd_conf}" + sudo sed -i 's|%WIFIipRouter%|'"$(escape_for_sed "$WIFIipRouter")"'|' "${dhcpcd_conf}" + sudo sed -i 's|%WIFIipExtDNS%|'"$(escape_for_sed "$wifiExtDNS")"'|' "${dhcpcd_conf}" + sudo sed -i 's|%WIFIcountryCode%|'"$(escape_for_sed "$WIFIcountryCode")"'|' "${dhcpcd_conf}" # Change user:group and access mod sudo chown root:netdev "${dhcpcd_conf}" sudo chmod 664 "${dhcpcd_conf}" @@ -1229,7 +1206,7 @@ existing_assets() { # Additional error check: key should not start with a hash and not be empty. if [ ! "${key:0:1}" == '#' ] && [ -n "$key" ]; then # Replace the matching value in the newly created conf file - sed -i 's/%'"$key"'%/'"$val"'/' "${jukebox_dir}"/settings/rfid_trigger_play.conf + sed -i 's|%'"$key"'%|'"$val"'|' "${jukebox_dir}"/settings/rfid_trigger_play.conf fi done <"${backup_dir}"/settings/rfid_trigger_play.conf fi @@ -1262,8 +1239,6 @@ existing_assets() { # make sure service is still enabled by registering again USB_BUTTONS_SERVICE="/etc/systemd/system/phoniebox-buttons-usb-encoder.service" sudo cp -v "${jukebox_dir}"/components/controls/buttons_usb_encoder/phoniebox-buttons-usb-encoder.service.sample "${USB_BUTTONS_SERVICE}" - # Replace homedir; double quotes for variable expansion - sudo sed -i "s%/home/pi%${HOME_DIR}%g" "${USB_BUTTONS_SERVICE}" sudo systemctl start phoniebox-buttons-usb-encoder.service sudo systemctl enable phoniebox-buttons-usb-encoder.service fi @@ -1350,80 +1325,101 @@ autohotspot() { fi } -finish_installation() { - local jukebox_dir="$1" +finished() { echo " # # INSTALLATION FINISHED # ##################################################### + +Let the sounds begin. +Find more information and documentation on the github account: +https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/ + " +} - ##################################################### - # Register external device(s) +register_rfid_reader() { + local jukebox_dir="$1" - echo "If you are using an RFID reader, connect it to your RPi." - echo "(In case your RFID reader required soldering, consult the manual.)" - # Use -e to display response of user in the logfile - read -e -r -p "Have you connected your RFID reader? [Y/n] " response - case "$response" in - [nN][oO]|[nN]) - ;; - *) - echo 'Please select the RFID reader you want to use' - options=("USB-Reader (e.g. Neuftech)" "RC522" "PN532" "Manual configuration" "Multiple RFID reader") - select opt in "${options[@]}"; do - case $opt in - "USB-Reader (e.g. Neuftech)") - cd "${jukebox_dir}"/scripts/ || exit - python3 RegisterDevice.py - sudo chown pi:www-data "${jukebox_dir}"/scripts/deviceName.txt - sudo chmod 644 "${jukebox_dir}"/scripts/deviceName.txt - break - ;; - "RC522") - bash "${jukebox_dir}"/components/rfid-reader/RC522/setup_rc522.sh - break - ;; - "PN532") - bash "${jukebox_dir}"/components/rfid-reader/PN532/setup_pn532.sh - break - ;; - "Manual configuration") - echo "Please configure your reader manually." - break - ;; - "Multiple RFID reader") - cd "${jukebox_dir}"/scripts/ || exit - sudo python3 RegisterDevice.py.Multi - break - ;; - *) - echo "This is not a number" - ;; - esac - done - esac + echo "" + echo "-----------------------------------------------------" + echo "Register RFID reader" + echo "-----------------------------------------------------" + + if [[ ${INTERACTIVE} == "true" ]]; then + echo "If you are using an RFID reader, connect it to your RPi." + echo "(In case your RFID reader required soldering, consult the manual.)" + # Use -e to display response of user in the logfile + read -e -r -p "Have you connected your RFID reader? [Y/n] " response + case "$response" in + [nN][oO]|[nN]) + ;; + *) + echo 'Please select the RFID reader you want to use' + options=("USB-Reader (e.g. Neuftech)" "RC522" "PN532" "Manual configuration" "Multiple RFID reader") + select opt in "${options[@]}"; do + case $opt in + "USB-Reader (e.g. Neuftech)") + cd "${jukebox_dir}"/scripts/ || exit + python3 RegisterDevice.py + sudo chown pi:www-data "${jukebox_dir}"/scripts/deviceName.txt + sudo chmod 644 "${jukebox_dir}"/scripts/deviceName.txt + break + ;; + "RC522") + bash "${jukebox_dir}"/components/rfid-reader/RC522/setup_rc522.sh + break + ;; + "PN532") + bash "${jukebox_dir}"/components/rfid-reader/PN532/setup_pn532.sh + break + ;; + "Manual configuration") + echo "Please configure your reader manually." + break + ;; + "Multiple RFID reader") + cd "${jukebox_dir}"/scripts/ || exit + sudo python3 RegisterDevice.py.Multi + break + ;; + *) + echo "This is not a number" + ;; + esac + done + esac + else + echo "Skipping RFID reader setup..." + echo "For manual registration of an RFID reader type:" + echo "python3 ${JUKEBOX_HOME_DIR}/scripts/RegisterDevice.py" + fi +} - echo - echo "DONE. Let the sounds begin." - echo "Find more information and documentation on the github account:" - echo "https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/" +cleanup_and_reboot() { - echo "Reboot is needed to activate all settings" - # Use -e to display response of user in the logfile - read -e -r -p "Would you like to reboot now? [Y/n] " response - case "$response" in - [nN][oO]|[nN]) - # Close logging - log_close - ;; - *) - # Close logging - log_close - sudo shutdown -r now - ;; - esac + echo "" + echo "-----------------------------------------------------" + echo "A reboot is required to activate all settings!" + local do_shutdown=false + if [[ ${INTERACTIVE} == "true" ]]; then + # Use -e to display response of user in the logfile + read -e -r -p "Would you like to reboot now? [Y/n] " response + case "$response" in + [nN][oO]|[nN]) + ;; + *) + do_shutdown=true + ;; + esac + fi + + # Close logging + log_close + if [[ ${do_shutdown} == "true" ]]; then + sudo shutdown -r now + fi } ######## @@ -1451,10 +1447,6 @@ main() { fi install_main "${JUKEBOX_HOME_DIR}" - source "${JUKEBOX_HOME_DIR}"/scripts/helperscripts/inc.networkHelper.sh - - wifi_settings "${JUKEBOX_HOME_DIR}" - autohotspot "${JUKEBOX_HOME_DIR}" existing_assets "${JUKEBOX_HOME_DIR}" "${JUKEBOX_BACKUP_DIR}" folder_access "${JUKEBOX_HOME_DIR}" "pi:www-data" 775 @@ -1463,15 +1455,10 @@ main() { sudo chown pi:www-data "${JUKEBOX_HOME_DIR}/settings/PhonieboxInstall.conf" sudo chmod 775 "${JUKEBOX_HOME_DIR}/settings/PhonieboxInstall.conf" - if [[ ${INTERACTIVE} == "true" ]]; then - finish_installation "${JUKEBOX_HOME_DIR}" - else - echo "Skipping USB device setup..." - echo "For manual registration of a USB card reader type:" - echo "python3 ${JUKEBOX_HOME_DIR}/scripts/RegisterDevice.py" - echo " " - echo "Reboot is required to activate all settings!" - fi + finished + + register_rfid_reader "${JUKEBOX_HOME_DIR}" + cleanup_and_reboot } start=$(date +%s) diff --git a/scripts/installscripts/tests/run_installation_autohotspot_NetworkManager.sh b/scripts/installscripts/tests/run_installation_autohotspot_NetworkManager.sh index 0979e7c72..cb935bec8 100644 --- a/scripts/installscripts/tests/run_installation_autohotspot_NetworkManager.sh +++ b/scripts/installscripts/tests/run_installation_autohotspot_NetworkManager.sh @@ -17,7 +17,8 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # y confirm interactive mode # n configure wifi (extra ENTER) # y configure autohotspot -# y use autohotspot default config (extra ENTER) +# n use autohotspot default config +# y use custom data (extra ENTER) # y use default audio iface (extra ENTER) # n spotify (extra ENTER) # y audio default location (extra ENTER) @@ -27,10 +28,15 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # n reboot export CI_TEST_DHCPCD="false" export CI_TEST_NETWORKMANAGER="true" -./../install-jukebox.sh <<< "y +./../install-jukebox.sh <<< 'y n y +n +a!b"c§d$e%f&g/h(i)j=k?l´m`n²o³p{q[r]s}t\u+v*w~x#y'\''z01234 +DE +ß,ä;ö.ü:Ä-Ö_Ü 1@2€3^4°5|6$7&8/9\0 +192.168.100.2 y y @@ -44,7 +50,7 @@ y y n n -" +' INSTALLATION_EXITCODE=$? # Test installation diff --git a/scripts/installscripts/tests/run_installation_autohotspot_dhcpcd.sh b/scripts/installscripts/tests/run_installation_autohotspot_dhcpcd.sh index 7091399bb..4e69e0866 100644 --- a/scripts/installscripts/tests/run_installation_autohotspot_dhcpcd.sh +++ b/scripts/installscripts/tests/run_installation_autohotspot_dhcpcd.sh @@ -17,7 +17,8 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # y confirm interactive mode # n configure wifi (extra ENTER) # y configure autohotspot -# y use autohotspot default config (extra ENTER) +# n use autohotspot default config +# y use custom data (extra ENTER) # y use default audio iface (extra ENTER) # n spotify (extra ENTER) # y audio default location (extra ENTER) @@ -27,10 +28,15 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # n reboot export CI_TEST_DHCPCD="true" export CI_TEST_NETWORKMANAGER="false" -./../install-jukebox.sh <<< "y +./../install-jukebox.sh <<< 'y n y +n +a!b"c§d$e%f&g/h(i)j=k?l´m`n²o³p{q[r]s}t\u+v*w~x#y'\''z01234 +DE +ß,ä;ö.ü:Ä-Ö_Ü 1@2€3^4°5|6$7&8/9\0 +10.0.0.1 y y @@ -44,7 +50,7 @@ y y n n -" +' INSTALLATION_EXITCODE=$? # Test installation diff --git a/scripts/installscripts/tests/run_installation_classic.sh b/scripts/installscripts/tests/run_installation_classic.sh index 434efc360..f52567f19 100644 --- a/scripts/installscripts/tests/run_installation_classic.sh +++ b/scripts/installscripts/tests/run_installation_classic.sh @@ -26,7 +26,7 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # n RFID registration # n reboot -./../install-jukebox.sh <<< "y +./../install-jukebox.sh <<< 'y n n @@ -42,7 +42,7 @@ y y n n -" +' INSTALLATION_EXITCODE=$? # Test installation diff --git a/scripts/installscripts/tests/run_installation_rfid.sh b/scripts/installscripts/tests/run_installation_rfid.sh index d9f150ad8..e3cfa08c2 100644 --- a/scripts/installscripts/tests/run_installation_rfid.sh +++ b/scripts/installscripts/tests/run_installation_rfid.sh @@ -28,7 +28,7 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # y, use legacy readermode # n reboot -./../install-jukebox.sh <<< "y +./../install-jukebox.sh <<< 'y n n @@ -47,7 +47,7 @@ y y y n -" +' INSTALLATION_EXITCODE=$? # Test installation diff --git a/scripts/installscripts/tests/run_installation_spotify.sh b/scripts/installscripts/tests/run_installation_spotify.sh index e65552b5d..d99ce30b3 100644 --- a/scripts/installscripts/tests/run_installation_spotify.sh +++ b/scripts/installscripts/tests/run_installation_spotify.sh @@ -33,8 +33,8 @@ n y y -abc$defghijkl&mnopqrstuvwxyzßäöüÄÖÜ$1234567890 -!"§$%&/()=?´`²³{[]}\+*~#'\'',;.:-_ @€^° +a!b"c§d$e%f&g/h(i)j=k?l´m`n²o³p{q[r]s}t\u+v*w~x#y'\''zß,ä;ö.ü:Ä-Ö_Ü 1@2€3^4°5|67890 +a!b"c§d$e%f&g/h(i)j=k?l´m`n²o³p{q[r]s}t\u+v*w~x#y'\''zß,ä;ö.ü:Ä-Ö_Ü 1@2€3^4°5|67890 myclient_id myclient+SECRET/0123456789= diff --git a/scripts/installscripts/tests/run_installation_staticip_dhcpcd.sh b/scripts/installscripts/tests/run_installation_staticip_dhcpcd.sh index a01610170..b143e8c70 100644 --- a/scripts/installscripts/tests/run_installation_staticip_dhcpcd.sh +++ b/scripts/installscripts/tests/run_installation_staticip_dhcpcd.sh @@ -28,11 +28,11 @@ echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selecti # n reboot export CI_TEST_DHCPCD="true" export CI_TEST_NETWORKMANAGER="false" -./../install-jukebox.sh <<< "y +./../install-jukebox.sh <<< 'y y -TestWifi +a!b"c§d$e%f&g/h(i)j=k?l´m`n²o³p{q[r]s}t\u+v*w~x#y'\''z01234 DE -TestWifiPW +ß,ä;ö.ü:Ä-Ö_Ü 1@2€3^4°5|6$7&8/9\0 192.168.100.2 192.168.100.1 y @@ -50,7 +50,7 @@ y y n n -" +' INSTALLATION_EXITCODE=$? # Test installation From 8069179297671a6343f92eb448c29535616eb447 Mon Sep 17 00:00:00 2001 From: Alvin Schiller <103769832+AlvinSchiller@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:43:50 +0200 Subject: [PATCH 2/7] Fix button function call (#2355) * fix: handle args for rotaryEncoder don't pass args to callback if 'None'. reorder args for callback to pass functionCallArgs first, if definied. added functionCallArgs to rotaryEncoder definition, to support other functionCalls then volume. * doc: update docs. removed duplicate / obsolete examples Merged wiki page "Audio-RotaryKnobVolume" * fix: fix tests * Apply suggestions from code review Co-authored-by: s-martin * Update codeblock type Co-authored-by: s-martin --------- Co-authored-by: s-martin --- README.md | 2 +- components/gpio_control/README.md | 76 ++++++++++++++++--- .../gpio_setting_rotary_vol_prevnext.ini | 36 --------- .../example_configs/gpio_settings.ini | 18 ++++- .../gpio_settings_rotary_and_led.ini | 49 ------------ .../gpio_settings_status_led.ini | 7 -- components/gpio_control/function_calls.py | 18 ++--- components/gpio_control/gpio_control.py | 9 ++- .../gpio_control/test/test_gpio_control.py | 49 +++++++++--- misc/sampleconfigs/gpio_settings.ini.sample | 18 ++++- 10 files changed, 148 insertions(+), 134 deletions(-) delete mode 100755 components/gpio_control/example_configs/gpio_setting_rotary_vol_prevnext.ini delete mode 100644 components/gpio_control/example_configs/gpio_settings_rotary_and_led.ini delete mode 100644 components/gpio_control/example_configs/gpio_settings_status_led.ini diff --git a/README.md b/README.md index 3b2266d84..ebaada654 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Check out the following references. * [PN532](components/rfid-reader/PN532/README.md) * PC/SC * also [multiple readers](https://github.com/MiczFlor/RPi-Jukebox-RFID/pull/1012#issue-434052529) simultaneously -* [**GPIO** control](components/gpio_control/README.md) for [buttons](https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Using-GPIO-hardware-buttons), [knobs / dials](https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Audio-RotaryKnobVolume) and much more to control your Phoniebox via GPIO. +* [**GPIO** control](components/gpio_control/README.md) for [buttons](https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Using-GPIO-hardware-buttons) and much more to control your Phoniebox via GPIO. * Control via smooth [**Web App**](https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/MANUAL#webapp) running on ajax from your phone, tablet or PC. You can play, upload, move files, assign new RFID cards, control playout, settings, etc. * Support for files with embedded chapters metadata (like m4a) * Customizable poweroff command diff --git a/components/gpio_control/README.md b/components/gpio_control/README.md index 8661e8d27..bae060400 100644 --- a/components/gpio_control/README.md +++ b/components/gpio_control/README.md @@ -24,20 +24,18 @@ Up to now the following input devices are implemented: * **ShutdownButton**: A specialized implementation for a shutdown button with integrated (but optional) LED support. It initializes a shutdown if the button is pressed more than `time_pressed` seconds and a (optional) LED on GPIO `led_pin` is flashing until that time is reached. For additional information, see [extended documentation below](#shutdownbutton). -* **RotaryEncoder**: - Control of a rotary encoder, for example KY040, see also in [Wiki](https://github.com/MiczFlor/RPi-Jukebox-RFID/wiki/Audio-RotaryKnobVolume). - It can be configured using `pinUp` and `PiNDown` (use GPIO numbers here), `functionCallUp`, `functionCallDown`, and `timeBase` see [extended documentation below](#rotaryencoder). - * **TwoButtonControl**: This Device uses two Buttons and implements a third action if both buttons are pressed together. See [extended documentation below](#twobuttoncontrol). +* **RotaryEncoder**: + Control of a rotary encoder, for example KY040. + It can be configured using `Pin1` and `Pin2` (use GPIO numbers here), `functionCall1`, `functionCall2` see [extended documentation below](#rotaryencoder). + * **StatusLED**: A LED which will light up once the Phoniebox has fully booted up and is ready to be used. For additional information, see [extended documentation below](#statusled). Each section needs to be activated by setting `enabled: True`. -Many example files are located in `~/RPi-Jukebox-RFID/components/gpio_control/example_configs/`. - ## Extended documentation This section provides some extended documentation and guideline. Especially some exemplary configurations are introduced showing how these controls can be set up in the configuration file `~/RPi-Jukebox-RFID/settings/gpio_settings.ini`. @@ -172,19 +170,75 @@ Furthermore, the following settings can be used as described for the [regular bu A RotaryEncoder can be created using an `ini` entry like this: ```bash -[VolumeControl] +[RotaryVolumeControl] enabled: True Type: RotaryEncoder -Pin1: 7 -Pin2: 8 -timeBase: 0.02 +Pin1: 22 +Pin2: 23 +timeBase: 0.1 functionCall1: functionCallVolU functionCall2: functionCallVolD ``` -Pin1 and FunctionCall1 correspond to rotary direction "up", while Pin2 and FunctionCall2 correspond to "down". +* **enabled**: This needs to be `True` for the rotary encoder to work. +* **Pin1**: GPIO number corresponding to rotary direction "clockwise" ('CLK') +* **Pin2**: GPIO number corresponding to rotary direction "counter clockwise" ('DT') +* **functionCall1**: function called for every rotation step corresponding to rotary direction "clockwise". See below for passed arguments. See [function documentation below](#functions). +* **functionCall2**: function called for every rotation step corresponding to rotary direction "counter clockwise". See below for passed arguments. See [function documentation below](#functions). +* **timeBase**: Factor used for calculating the rotation value base on rotation speed, defaults to `0.1`. Use `0` for deactivating rotation speed influence. +Example: + * a single rotation step leads to the value 1 passed to the function. + * steady rotation of two to or more steps, leads to the value 1 for the first call and the value 2 for all further calls. + * speeding up rotation of two to or more steps, leads to the value 1 for the first call, the value 2 for the second, the value 3 for the third and so on. +* **functionCall1Args**: Arguments for `functionCall1`, defaults to `None`. If defined takes precedence over rotation value. Arguments are ignored, if `functionCall1` does not take any. +* **functionCall2Args**: Arguments for `functionCall2`, defaults to `None`. If defined takes precedence over rotation value. Arguments are ignored, if `functionCall1` does not take any. + +To also use the push button of the encoder just a button definition: +```bash +[Mute] +enabled: True +Type: Button +Pin: 27 +functionCall: functionCallVol0 +``` + Note that the old configuration entries PinUp/PinDown and functionCallUp/functionCallDown are deprecated and might stop working in future. + +```bash +[RotarySeekingControl] +enabled: True +Type: RotaryEncoder +Pin1: 22 +Pin2: 23 +timeBase: 0.1 +functionCall1: functionCallPlayerSeekFwd +functionCall1Args: 5 +functionCall2: functionCallPlayerSeekBack +functionCall2Args: 5 +``` + +In this example, the encoder will be used to seek for- and backwards by 5 seconds on every rotation step. The rotation value will **NOT** be used in this case as the function args are defined! + + +#### Circuit diagram +```text + .---------------. .---------------. + | | | | + | CLK |----------------------| GPIO 22 | + | | | | + | DT |----------------------| GPIO 23 | + | | | | + | SW |----------------------| GPIO 27 | + | | | | + | + |----------------------| 3.3V | + | | | | + | GND |----------------------| GND | + | | | | + '---------------' '---------------' + KY-040 Raspberry +``` + ### StatusLED A StatusLED can be created using an `ini` entry like this: diff --git a/components/gpio_control/example_configs/gpio_setting_rotary_vol_prevnext.ini b/components/gpio_control/example_configs/gpio_setting_rotary_vol_prevnext.ini deleted file mode 100755 index 3e829d55e..000000000 --- a/components/gpio_control/example_configs/gpio_setting_rotary_vol_prevnext.ini +++ /dev/null @@ -1,36 +0,0 @@ -[DEFAULT] -enabled: True - -[VolumeControl] -enabled: True -Type: RotaryEncoder -PinUp: 7 -PinDown: 8 -timeBase: 0.02 -functionCallDown: functionCallVolD -functionCallUp: functionCallVolU - -[PrevNextControl] -enabled: True -Type: RotaryEncoder -PinUp: 16 -PinDown: 20 -timeBase: 0.02 -functionCallDown: functionCallPlayerPrev -functionCallUp: functionCallPlayerNext - -[Shutdown] -enabled: True -Type: Button -Pin: 17 -pull_up_down: pull_down -edge: raising -functionCall: functionCallShutdown - -[PlayPause] -enabled: True -Type: Button -Pin: 25 -pull_up_down: pull_up -edge: falling -functionCall: functionCallPlayerPause diff --git a/components/gpio_control/example_configs/gpio_settings.ini b/components/gpio_control/example_configs/gpio_settings.ini index 4f84ba01b..a386d6ec6 100755 --- a/components/gpio_control/example_configs/gpio_settings.ini +++ b/components/gpio_control/example_configs/gpio_settings.ini @@ -4,20 +4,28 @@ antibouncehack: False [VolumeControl] enabled: False -Type: TwoButtonControl ;or RotaryEncoder +Type: TwoButtonControl Pin1: 5 Pin2: 6 pull_up_down: pull_up hold_time: 0.3 hold_mode: Repeat -timeBase: 0.1 ;only for RotaryEncoder functionCall1: functionCallVolU functionCall2: functionCallVolD -functionCallTwoButtons: functionCallVol0 ;only for TwoButtonControl +functionCallTwoButtons: functionCallVol0 ;functionCall1Args: 1 ;functionCall2Args: 1 ;functionCallTwoButtonsArgs: x +[RotaryVolumeControl] +enabled: False +Type: RotaryEncoder +Pin1: 22 +Pin2: 23 +timeBase: 0.1 +functionCall1: functionCallVolU +functionCall2: functionCallVolD + [PrevNextControl] enabled: False Type: TwoButtonControl @@ -143,3 +151,7 @@ pull_up_down: pull_up functionCall: functionCallTriggerPlayFolder functionCallArgs: someRelativeFolderName +[StatusLED] +enabled: False +Type: StatusLED +Pin: 14 diff --git a/components/gpio_control/example_configs/gpio_settings_rotary_and_led.ini b/components/gpio_control/example_configs/gpio_settings_rotary_and_led.ini deleted file mode 100644 index f636adbcf..000000000 --- a/components/gpio_control/example_configs/gpio_settings_rotary_and_led.ini +++ /dev/null @@ -1,49 +0,0 @@ -[DEFAULT] -enabled: True - -[VolumeControl] -enabled: True -Type: RotaryEncoder -PinUp: 17 -PinDown: 22 -pull_up_down: pull_up -timeBase: 0.2 -; only for rotary encoder -functionCallDown: functionCallVolD -functionCallUp: functionCallVolU -functionCallTwoButtons: functionCallStop -; only for TwoButtonControl -functionCallButton: functionCallPlayerPause -; only for RotaryEncoderClickable - -[PrevNextControl] -enabled: True -Type: TwoButtonControl -Pin1: 6 -Pin2: 12 -functionCall1: functionCallPlayerPrev -functionCall2: functionCallPlayerNext -functionCallTwoButtons: functionCallPlayerStop -pull_up_down: pull_up -hold_time: 0.3 -hold_mode: None - - -[Shutdown] -enabled: False -Type: Button -Pin: 3 -functionCall: functionCallShutdown - - -[PlayPause] -enabled: True -Type: Button -Pin: 27 -pull_up_down: pull_up -functionCall: functionCallPlayerPause - -[StatusLED] -enabled: True -Type: MPDStatusLED -Pin: 16 diff --git a/components/gpio_control/example_configs/gpio_settings_status_led.ini b/components/gpio_control/example_configs/gpio_settings_status_led.ini deleted file mode 100644 index 68e6461b2..000000000 --- a/components/gpio_control/example_configs/gpio_settings_status_led.ini +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -enabled: True - -[StatusLED] -enabled: True -Type: StatusLED -Pin: 14 diff --git a/components/gpio_control/function_calls.py b/components/gpio_control/function_calls.py index e293282c0..c3eda5913 100644 --- a/components/gpio_control/function_calls.py +++ b/components/gpio_control/function_calls.py @@ -18,7 +18,7 @@ def __init__(self): def functionCallShutdown(self, *args): function_call("{command} -c=shutdown".format(command=self.playout_control), shell=True) - def functionCallVolU(self, steps=None): + def functionCallVolU(self, steps=None, *args): if steps is None: function_call("{command} -c=volumeup".format(command=self.playout_control), shell=True) else: @@ -26,7 +26,7 @@ def functionCallVolU(self, steps=None): command=self.playout_control), shell=True) - def functionCallVolD(self, steps=None): + def functionCallVolD(self, steps=None, *args): if steps is None: function_call("{command} -c=volumedown".format(command=self.playout_control), shell=True) else: @@ -65,22 +65,22 @@ def functionCallPlayerStop(self, *args): function_call("{command} -c=playerstop".format(command=self.playout_control), shell=True) - def functionCallPlayerSeekFwd(self, seconds=None): + def functionCallPlayerSeekFwd(self, seconds=None, *args): if seconds is None: seconds = 10 function_call("{command} -c=playerseek -v=+{value}".format(command=self.playout_control, value=seconds), shell=True) - def functionCallPlayerSeekBack(self, seconds=None): + def functionCallPlayerSeekBack(self, seconds=None, *args): if seconds is None: seconds = 10 function_call("{command} -c=playerseek -v=-{value}".format(command=self.playout_control, value=seconds), shell=True) - def functionCallPlayerSeekFarFwd(self, seconds=None): + def functionCallPlayerSeekFarFwd(self, seconds=None, *args): if seconds is None: seconds = 60 function_call("{command} -c=playerseek -v=+{value}".format(command=self.playout_control, value=seconds), shell=True) - def functionCallPlayerSeekFarBack(self, seconds=None): + def functionCallPlayerSeekFarBack(self, seconds=None, *args): if seconds is None: seconds = 60 function_call("{command} -c=playerseek -v=-{value}".format(command=self.playout_control, value=seconds), shell=True) @@ -94,15 +94,15 @@ def functionCallPlayerRandomCard(self, *args): def functionCallPlayerRandomFolder(self, *args): function_call("{command} -c=randomfolder".format(command=self.playout_control), shell=True) - def functionCallBluetoothToggle(self, mode=None): + def functionCallBluetoothToggle(self, mode=None, *args): if mode is None: mode = 'toggle' function_call("{command} -c=bluetoothtoggle -v={value}".format(command=self.playout_control, value=mode), shell=True) - def functionCallTriggerPlayCardId(self, cardid): + def functionCallTriggerPlayCardId(self, cardid, *args): function_call("{command} --cardid={value}".format(command=self.rfid_trigger, value = cardid), shell=True) - def functionCallTriggerPlayFolder(self, folder): + def functionCallTriggerPlayFolder(self, folder, *args): function_call("{command} --dir={value}".format(command=self.rfid_trigger, value = folder), shell=True) def getFunctionCall(self, functionName): diff --git a/components/gpio_control/gpio_control.py b/components/gpio_control/gpio_control.py index 45977b89b..3e91df31b 100755 --- a/components/gpio_control/gpio_control.py +++ b/components/gpio_control/gpio_control.py @@ -32,7 +32,10 @@ def getFunctionCall(self, function_name, function_args): try: if function_name is not None and function_name != 'None': functionCall = getattr(self.function_calls, function_name) - return (lambda *args: functionCall(*args, function_args)) + if function_args is not None and function_args != 'None': + return (lambda *args: functionCall(function_args, *args)) + else: + return (lambda *args: functionCall(*args)) except AttributeError: self.logger.error('Could not find FunctionCall {function_name}'.format(function_name=function_name)) return lambda *args: None @@ -77,8 +80,8 @@ def generate_device(self, config, deviceName): elif device_type == 'RotaryEncoder': return RotaryEncoder(config.getint('Pin1'), config.getint('Pin2'), - self.getFunctionCall(config.get('functionCall1'), None), - self.getFunctionCall(config.get('functionCall2'), None), + self.getFunctionCall(config.get('functionCall1'), config.get('functionCall1Args', fallback=None)), + self.getFunctionCall(config.get('functionCall2'), config.get('functionCall2Args', fallback=None)), config.getfloat('timeBase', fallback=0.1), name=deviceName) elif device_type == 'ShutdownButton': diff --git a/components/gpio_control/test/test_gpio_control.py b/components/gpio_control/test/test_gpio_control.py index 4c139b67e..26d6885fa 100644 --- a/components/gpio_control/test/test_gpio_control.py +++ b/components/gpio_control/test/test_gpio_control.py @@ -16,11 +16,11 @@ def gpio_control_class(): class MockFunctionCalls: - def funcTestWithoutParameter(self, *args): - return "funcTestWithoutParameter" + def funcTestWithoutParameter(*args): + return "funcTestWithoutParameter()" - def funcTestWithParameter(self, param1): - return f"funcTestWithParameter({param1})" + def funcTestWithParameter(param1, *args): + return f"funcTestWithParameter({str(param1)})" _gpio_control_class = GPIOControl(MockFunctionCalls) # function_calls will be mocked return _gpio_control_class @@ -226,19 +226,44 @@ def test_generateDevice_TwoButtonControl(self, gpio_control_class): def test_getFunctionCall_None_None(self, gpio_control_class): result = gpio_control_class.getFunctionCall(None, None) - assert result(()) is None + assert result() is None result = gpio_control_class.getFunctionCall('None', None) - assert result(()) is None + assert result() is None result = gpio_control_class.getFunctionCall("nonExisting", None) - assert result(()) is None + assert result() is None - def test_getFunctionCall_withoutParam(self, gpio_control_class): + def test_getFunctionCall_funcWithoutArgs(self, gpio_control_class): result = gpio_control_class.getFunctionCall("funcTestWithoutParameter", None) - assert result(()) == "funcTestWithoutParameter" + assert result() == "funcTestWithoutParameter()" - def test_getFunctionCall_withParam(self, gpio_control_class): - result = gpio_control_class.getFunctionCall("funcTestWithParameter", "param1") - assert result(()) == "funcTestWithParameter(param1)" + def test_getFunctionCall_funcWithoutArgs_withParam(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithoutParameter", None) + assert result("param") == "funcTestWithoutParameter()" + + def test_getFunctionCall_funcWithoutArgs_withFuncArgs(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithoutParameter", "funcArgs") + assert result() == "funcTestWithoutParameter()" + + def test_getFunctionCallfuncWithoutArgs_withFuncArgsAndParam(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithoutParameter", "funcArgs") + assert result("param") == "funcTestWithoutParameter()" + + def test_getFunctionCall_funcWithArgs_withoutFuncArgsAndParam(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithParameter", None) + with pytest.raises(TypeError): + result() + + def test_getFunctionCall_funcWithArgs_withParam(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithParameter", None) + assert result("param") == "funcTestWithParameter(param)" + + def test_getFunctionCall_funcWithArgs_withFuncArgs(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithParameter", "funcArgs") + assert result() == "funcTestWithParameter(funcArgs)" + + def test_getFunctionCall_funcWithArgs_withFuncArgsAndParam(self, gpio_control_class): + result = gpio_control_class.getFunctionCall("funcTestWithParameter", "funcArgs") + assert result("param") == "funcTestWithParameter(funcArgs)" # --------------- diff --git a/misc/sampleconfigs/gpio_settings.ini.sample b/misc/sampleconfigs/gpio_settings.ini.sample index 4f84ba01b..a386d6ec6 100644 --- a/misc/sampleconfigs/gpio_settings.ini.sample +++ b/misc/sampleconfigs/gpio_settings.ini.sample @@ -4,20 +4,28 @@ antibouncehack: False [VolumeControl] enabled: False -Type: TwoButtonControl ;or RotaryEncoder +Type: TwoButtonControl Pin1: 5 Pin2: 6 pull_up_down: pull_up hold_time: 0.3 hold_mode: Repeat -timeBase: 0.1 ;only for RotaryEncoder functionCall1: functionCallVolU functionCall2: functionCallVolD -functionCallTwoButtons: functionCallVol0 ;only for TwoButtonControl +functionCallTwoButtons: functionCallVol0 ;functionCall1Args: 1 ;functionCall2Args: 1 ;functionCallTwoButtonsArgs: x +[RotaryVolumeControl] +enabled: False +Type: RotaryEncoder +Pin1: 22 +Pin2: 23 +timeBase: 0.1 +functionCall1: functionCallVolU +functionCall2: functionCallVolD + [PrevNextControl] enabled: False Type: TwoButtonControl @@ -143,3 +151,7 @@ pull_up_down: pull_up functionCall: functionCallTriggerPlayFolder functionCallArgs: someRelativeFolderName +[StatusLED] +enabled: False +Type: StatusLED +Pin: 14 From 1fb7a84a2344aa00b5f464f4062da06f2a18acd5 Mon Sep 17 00:00:00 2001 From: Alvin Schiller <103769832+AlvinSchiller@users.noreply.github.com> Date: Fri, 26 Apr 2024 13:24:37 +0200 Subject: [PATCH 3/7] fix: update default handling for yes/no questions (#2357) --- components/audio/PirateAudioHAT/setup_pirateAudioHAT.sh | 7 +++---- .../buttons_usb_encoder/setup-buttons-usb-encoder.sh | 7 +++---- components/rfid-reader/PN532/setup_pn532.sh | 7 +++---- components/rfid-reader/RC522/setup_rc522.sh | 7 +++---- scripts/RegisterDevice.py.Multi | 6 +++--- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/components/audio/PirateAudioHAT/setup_pirateAudioHAT.sh b/components/audio/PirateAudioHAT/setup_pirateAudioHAT.sh index 97c91194b..2fc977443 100755 --- a/components/audio/PirateAudioHAT/setup_pirateAudioHAT.sh +++ b/components/audio/PirateAudioHAT/setup_pirateAudioHAT.sh @@ -7,11 +7,10 @@ source "${JUKEBOX_HOME_DIR}"/scripts/helperscripts/inc.systemHelper.sh question() { local question=$1 - read -p "${question} (y/n)? " choice + read -p "${question} (Y/n)? " choice case "$choice" in - y|Y ) ;; - n|N ) exit 0;; - * ) echo "Error: invalid" ; question ${question};; + [nN][oO]|[nN]) exit 0;; + * ) ;; esac } diff --git a/components/controls/buttons_usb_encoder/setup-buttons-usb-encoder.sh b/components/controls/buttons_usb_encoder/setup-buttons-usb-encoder.sh index 95db0199c..0fa5fadc0 100755 --- a/components/controls/buttons_usb_encoder/setup-buttons-usb-encoder.sh +++ b/components/controls/buttons_usb_encoder/setup-buttons-usb-encoder.sh @@ -6,11 +6,10 @@ BUTTONS_USB_ENCODER_DIR="${JUKEBOX_HOME_DIR}/components/controls/buttons_usb_enc question() { local question=$1 - read -p "${question} (y/n)? " choice + read -p "${question} (Y/n)? " choice case "$choice" in - y|Y ) ;; - n|N ) exit 0;; - * ) echo "Error: invalid" ; question ${question};; + [nN][oO]|[nN]) exit 0;; + * ) ;; esac } diff --git a/components/rfid-reader/PN532/setup_pn532.sh b/components/rfid-reader/PN532/setup_pn532.sh index 3b9ab1252..8f2e97bbb 100755 --- a/components/rfid-reader/PN532/setup_pn532.sh +++ b/components/rfid-reader/PN532/setup_pn532.sh @@ -5,11 +5,10 @@ JUKEBOX_HOME_DIR="${HOME_DIR}/RPi-Jukebox-RFID" question() { local question=$1 - read -p "${question} (y/n)? " choice + read -p "${question} (Y/n)? " choice case "$choice" in - y|Y ) ;; - n|N ) exit 0;; - * ) echo "Error: invalid" ; question ${question};; + [nN][oO]|[nN]) exit 0;; + * ) ;; esac } diff --git a/components/rfid-reader/RC522/setup_rc522.sh b/components/rfid-reader/RC522/setup_rc522.sh index 43ab60fa6..b6333767b 100644 --- a/components/rfid-reader/RC522/setup_rc522.sh +++ b/components/rfid-reader/RC522/setup_rc522.sh @@ -5,11 +5,10 @@ JUKEBOX_HOME_DIR="${HOME_DIR}/RPi-Jukebox-RFID" question() { local question=$1 - read -p "${question} (y/n)? " choice + read -p "${question} (Y/n)? " choice case "$choice" in - y|Y ) ;; - n|N ) exit 0;; - * ) echo "Error: invalid" ; question ${question};; + [nN][oO]|[nN]) exit 0;; + * ) ;; esac } diff --git a/scripts/RegisterDevice.py.Multi b/scripts/RegisterDevice.py.Multi index 66dc16898..008121496 100644 --- a/scripts/RegisterDevice.py.Multi +++ b/scripts/RegisterDevice.py.Multi @@ -17,7 +17,7 @@ def runCmd(cmd, wait=True): def setupPN532(): answer = input('Please make sure that the PN532 reader is wired up correctly ' 'to the GPIO ports before continuing...\n Continue?: [Y/n]') - if not answer or answer[0] != 'Y': + if answer and answer[0].lower() == 'n': return False print("Activating I2C interface...\n") runCmd("sudo raspi-config nonint do_i2c 0") @@ -41,7 +41,7 @@ def setupPN532(): def setupMFRC522(): answer = input('Please make sure that the RC522 reader is wired up correctly ' 'to the GPIO ports before continuing...\n Continue?: [Y/n]') - if not answer or answer[0] != 'Y': + if answer and answer[0].lower() == 'n': return False print("Installing Python requirements for RC522...\n") runCmd("sudo python3 -m pip install --upgrade --force-reinstall " @@ -78,7 +78,7 @@ def configureDevices(): addDevice() while True: answer = input('Do you want to add another device: [Y/n]') - if not answer or answer[0] != 'Y': + if answer and answer[0].lower() == 'n': break addDevice() From 31c2142693e0fbb776561038673b3846cc24db97 Mon Sep 17 00:00:00 2001 From: Alvin Schiller <103769832+AlvinSchiller@users.noreply.github.com> Date: Fri, 26 Apr 2024 20:40:12 +0200 Subject: [PATCH 4/7] Bump version to 2.7.0 --- settings/version-number | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/version-number b/settings/version-number index 1dba92d5c..24ba9a38d 100644 --- a/settings/version-number +++ b/settings/version-number @@ -1 +1 @@ -2.7.0-alpha +2.7.0 From ef130103cd1ccc80bd10d5aa4b110ebb75ab1457 Mon Sep 17 00:00:00 2001 From: Alvin Schiller <103769832+AlvinSchiller@users.noreply.github.com> Date: Fri, 26 Apr 2024 20:49:09 +0200 Subject: [PATCH 5/7] Bump version to 2.8.0-alpha --- settings/version-number | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/version-number b/settings/version-number index 24ba9a38d..c338ba7ef 100644 --- a/settings/version-number +++ b/settings/version-number @@ -1 +1 @@ -2.7.0 +2.8.0-alpha From cffa96c86b73a0d5c119f8caf1f75262125cceba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 20:42:26 +0200 Subject: [PATCH 6/7] Bump docker/setup-buildx-action from 3.2.0 to 3.3.0 (#2362) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test_docker_debian_codename_sub.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_docker_debian_codename_sub.yml b/.github/workflows/test_docker_debian_codename_sub.yml index 00ef4efd7..7aa1cd41e 100644 --- a/.github/workflows/test_docker_debian_codename_sub.yml +++ b/.github/workflows/test_docker_debian_codename_sub.yml @@ -58,7 +58,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 with: # network=host driver-opt needed to push to local registry driver-opts: network=host @@ -155,7 +155,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 - name: Artifact Download Docker Image uses: actions/download-artifact@v4 From f3b4b2065261b37cc14ecaf7dcf4304a68a15523 Mon Sep 17 00:00:00 2001 From: Alvin Schiller <103769832+AlvinSchiller@users.noreply.github.com> Date: Thu, 9 May 2024 00:20:30 +0200 Subject: [PATCH 7/7] Fix button SecondFunc trigger (#2367) * fix instant execution of main action on holding * simplify holdMode callback handling * Update test_SimpleButton.py * update docs * fix: only go into while loop if further held * docs: update comments * docs: add alert for behavior change * docs: fix typo --- .../gpio_control/GPIODevices/simple_button.py | 41 +++++++++++-------- components/gpio_control/README.md | 15 ++++--- .../gpio_control/test/test_SimpleButton.py | 4 +- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/components/gpio_control/GPIODevices/simple_button.py b/components/gpio_control/GPIODevices/simple_button.py index a022a812f..920358acf 100644 --- a/components/gpio_control/GPIODevices/simple_button.py +++ b/components/gpio_control/GPIODevices/simple_button.py @@ -106,11 +106,7 @@ def callbackFunctionHandler(self, *args): if inval != GPIO.LOW: return None - if self.hold_mode in ('Repeat', 'Postpone', 'SecondFunc', 'SecondFuncRepeat'): - return self.longPressHandler(*args) - else: - logger.info('{}: execute callback'.format(self.name)) - return self.when_pressed(*args) + return self._handleCallbackFunction(*args) @property def when_pressed(self): @@ -136,36 +132,45 @@ def when_pressed(self, func): def set_callbackFunction(self, callbackFunction): self.when_pressed = callbackFunction - def longPressHandler(self, *args): - logger.info('{}: longPressHandler, mode: {}'.format(self.name, self.hold_mode)) - # instant action (except Postpone mode) - if self.hold_mode != "Postpone": - self.when_pressed(*args) + def _handleCallbackFunction(self, *args): + logger.info('{}: handleCallbackFunction, mode: {}'.format(self.name, self.hold_mode)) - # action(s) after hold_time if self.hold_mode == "Repeat": - # Repeated call of main action (multiple times if button is held long enough) + # Instantly call primary action + self.when_pressed(*args) + # Repeated call of primary action if button is held long enough while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): self.when_pressed(*args) elif self.hold_mode == "Postpone": - # Postponed call of main action (once) + # Postponed call of primary action (once) if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): self.when_pressed(*args) - while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): - pass + while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): + pass elif self.hold_mode == "SecondFunc": # Call of secondary action (once) + # execute primary action if not held past hold_time if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): self.when_held(*args) - while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): - pass + while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): + pass + else: + self.when_pressed(*args) elif self.hold_mode == "SecondFuncRepeat": # Repeated call of secondary action (multiple times if button is held long enough) - while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): + # execute primary action if not held past hold_time + if checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): self.when_held(*args) + while checkGpioStaysInState(self.hold_time, self.pin, GPIO.LOW): + self.when_held(*args) + else: + self.when_pressed(*args) + + else: + self.when_pressed(*args) def __del__(self): logger.debug('remove event detection') diff --git a/components/gpio_control/README.md b/components/gpio_control/README.md index bae060400..3b53d26d0 100644 --- a/components/gpio_control/README.md +++ b/components/gpio_control/README.md @@ -59,15 +59,18 @@ functionCall: functionCallPlayerPause However, a button has more parameters than these. In the following comprehensive list you can also find the default values which are used automatically if you leave out these settings: * **functionCallArgs**: Arguments for primary function, defaults to `None`. Arguments are ignored, if `functionCall` does not take any. + +> [!IMPORTANT] +> Since v2.8.0 the behavior of `hold_mode` `SecondFunc` and `SecondFuncRepeat` has changed. The secondary function is no longer triggered additionally to the primary function. +> Now its called exclusively if `hold_time` is reached. The primary function will only be triggered if the button is pressed shorter then `hold_time`! +> Existing configurations may need to adapt to this. * **hold_mode**: Specifies what shall happen if the button is held pressed for longer than `hold_time`: * `None` (Default): Nothing special will happen. - * `Repeat`: The configured `functionCall` is repeated after each `hold_time` interval. + * `Repeat`: The configured `functionCall` is instantly called and repeatedly after each `hold_time` interval. * `Postpone`: The function will not be called before `hold_time`, i.e. the button needs to be pressed this long to activate the function - * `SecondFunc`: Holding the button for at least `hold_time` will additionally execute the function `functionCall2` with `functionCall2Args`. + * `SecondFunc`: Pressing the button (shorter than `hold_time`) will execute the function `functionCall` with `functionCallArgs`. Holding the button for at least `hold_time` will execute the function `functionCall2` with `functionCall2Args`. * `SecondFuncRepeat`: Like SecondFunc, but `functionCall2` is repeated after each `hold_time` interval. - In every `hold_mode` except `Postpone`, the main action `functionCall` gets executed instantly. - Holding the button even longer than `hold_time` will cause no further action unless you are in the `Repeat` or `SecondFuncRepeat` mode. * **hold_time**: Reference time for this buttons `hold_mode` feature in seconds. Default is `0.3`. This setting is ignored if `hold_mode` is unset or `None` @@ -79,10 +82,10 @@ However, a button has more parameters than these. In the following comprehensive * `pull_off`. Use this to deactivate internal pull-up/pulldown resistors. This is useful if your wiring includes your own (external) pull up / down resistors. * **edge**: Configures the events in which the GPIO library shall trigger the callback function. Valid settings: * `falling` (Default). Triggers if the GPIO voltage goes down. - * `rising`. Trigegrs only if the GPIO voltage goes up. + * `rising`. Triggers only if the GPIO voltage goes up. * `both`. Triggers in both cases. * **bouncetime**: This is a setting of the GPIO library to limit bouncing effects during button usage. Default is `500` ms. -* **antibouncehack**: Despite the integrated bounce reduction of the GPIO library some users may notice false triggers of their buttons (e.g. unrequested / double actions when releasing the button). If you encounter such problems, try setting this setting to `True` to activate an additional countermeasure. +* **antibouncehack**: Despite the integrated bounce reduction of the GPIO library some users may notice false triggers of their buttons (e.g. unrequested / double actions when releasing the button). If you encounter such problems, try setting this to `True` to activate an additional countermeasure. Note: If you prefer, you may also use `Type: SimpleButton` instead of `Type: Button` - this makes no difference. diff --git a/components/gpio_control/test/test_SimpleButton.py b/components/gpio_control/test/test_SimpleButton.py index d6f03acd3..b0b933e09 100644 --- a/components/gpio_control/test/test_SimpleButton.py +++ b/components/gpio_control/test/test_SimpleButton.py @@ -150,7 +150,7 @@ def test_hold_SecondFunc_longer_holdtime(self, simple_button): simple_button.hold_mode = 'SecondFunc' calls = mockedSecAction.call_count simple_button.callbackFunctionHandler(simple_button.pin) - mockedAction.assert_called_once() + mockedAction.assert_not_called() assert mockedSecAction.call_count - calls == 1 def test_hold_SecondFunc_shorter_holdtime(self, simple_button): @@ -170,7 +170,7 @@ def test_hold_SecondFuncRepeat_longer_holdtime(self, simple_button): simple_button.hold_mode = 'SecondFuncRepeat' calls = mockedSecAction.call_count simple_button.callbackFunctionHandler(simple_button.pin) - mockedAction.assert_called_once() + mockedAction.assert_not_called() assert mockedSecAction.call_count - calls == 3 def test_hold_SecondFuncRepeat_shorter_holdtime(self, simple_button):