mirror of
https://github.com/RetroPie/RetroPie-Setup.git
synced 2025-04-02 10:51:41 -04:00
510 lines
16 KiB
Bash
510 lines
16 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# This file is part of The RetroPie Project
|
|
#
|
|
# The RetroPie Project is the legal property of its developers, whose names are
|
|
# too numerous to list here. Please refer to the COPYRIGHT.md file distributed with this source.
|
|
#
|
|
# See the LICENSE.md file at the top-level directory of this distribution and
|
|
# at https://raw.githubusercontent.com/RetroPie/RetroPie-Setup/master/LICENSE.md
|
|
#
|
|
|
|
rp_module_id="bluetooth"
|
|
rp_module_desc="Configure Bluetooth Devices"
|
|
rp_module_section="config"
|
|
|
|
function _update_hook_bluetooth() {
|
|
# fix config location
|
|
[[ -f "$configdir/bluetooth.cfg" ]] && mv "$configdir/bluetooth.cfg" "$configdir/all/bluetooth.cfg"
|
|
local mode="$(_get_connect_mode)"
|
|
# if user has set bluetooth connect mode to boot or background, make sure we
|
|
# have the latest dependencies and update systemd script
|
|
if [[ "$mode" != "default" ]]; then
|
|
# make sure dependencies are up to date
|
|
! hasPackage "bluez-tools" && depends_bluetooth
|
|
connect_mode_set_bluetooth "$mode"
|
|
fi
|
|
}
|
|
|
|
function _get_connect_mode() {
|
|
# get bluetooth config
|
|
iniConfig "=" '"' "$configdir/all/bluetooth.cfg"
|
|
iniGet "connect_mode"
|
|
if [[ -n "$ini_value" ]]; then
|
|
echo "$ini_value"
|
|
else
|
|
echo "default"
|
|
fi
|
|
}
|
|
|
|
function depends_bluetooth() {
|
|
local depends=(bluetooth python3-dbus python3-gi bluez-tools)
|
|
if [[ "$__os_id" == "Raspbian" ]]; then
|
|
depends+=(pi-bluetooth raspberrypi-sys-mods)
|
|
fi
|
|
getDepends "${depends[@]}"
|
|
}
|
|
|
|
function get_script_bluetooth() {
|
|
name="$1"
|
|
if ! which "$name"; then
|
|
[[ "$name" == "bluez-test-input" ]] && name="bluez-test-device"
|
|
name="$md_data/$name"
|
|
fi
|
|
echo "$name"
|
|
}
|
|
|
|
function _slowecho_bluetooth() {
|
|
local line
|
|
|
|
IFS=$'\n'
|
|
for line in $(echo -e "${1}"); do
|
|
echo -e "$line"
|
|
sleep 1
|
|
done
|
|
unset IFS
|
|
}
|
|
|
|
function bluez_cmd_bluetooth() {
|
|
# create a named pipe & fd for input for bluetoothctl
|
|
local fifo="$(mktemp -u)"
|
|
mkfifo "$fifo"
|
|
exec 3<>"$fifo"
|
|
local line
|
|
while true; do
|
|
_slowecho_bluetooth "$1" >&3
|
|
# collect output for specified amount of time, then echo it
|
|
while read -r line; do
|
|
printf '%s\n' "$line"
|
|
# (slow) reply to any optional challenges
|
|
if [[ -n "$3" && "$line" =~ $3 ]]; then
|
|
_slowecho_bluetooth "$4" >&3
|
|
fi
|
|
done
|
|
_slowecho_bluetooth "quit\n" >&3
|
|
break
|
|
# read from bluetoothctl buffered line by line
|
|
done < <(timeout "$2" stdbuf -oL bluetoothctl --agent=NoInputNoOutput <&3)
|
|
exec 3>&-
|
|
}
|
|
|
|
function list_available_bluetooth() {
|
|
local mac
|
|
local name
|
|
local info_text="\n\nSearching ..."
|
|
|
|
declare -A paired=()
|
|
declare -A found=()
|
|
|
|
# get an asc array of paired mac addresses
|
|
while read mac; read name; do
|
|
paired+=(["$mac"]="$name")
|
|
done < <(list_paired_bluetooth)
|
|
|
|
# sixaxis: add USB pairing information
|
|
[[ -n "$(lsmod | grep hid_sony)" ]] && info_text="Searching ...\n\nDualShock registration: while this text is visible, unplug the controller, press the PS/SHARE button, and then replug the controller."
|
|
# sixaxis: configure workaround for PS3 when we detect one
|
|
if cat /proc/bus/input/devices | grep Name | grep -qE "(Sony )?PLAY.*3" ; then
|
|
_bonded_fix_bluetooth "add"
|
|
systemctl -q is-active bluetooth.service && systemctl restart bluetooth.service
|
|
fi
|
|
|
|
dialog --backtitle "$__backtitle" --infobox "$info_text" 7 60 >/dev/tty
|
|
if hasPackage bluez 5; then
|
|
# sixaxis: reply to authorization challenge on USB cable connect
|
|
while read mac; read name; do
|
|
found+=(["$mac"]="$name")
|
|
done < <(bluez_cmd_bluetooth "default-agent\nscan on" "15" "Authorize service$" "yes" >/dev/null; bluez_cmd_bluetooth "devices" "3" | grep "^Device " | cut -d" " -f2,3- | sed 's/ /\n/')
|
|
else
|
|
while read; read mac; read name; do
|
|
found+=(["$mac"]="$name")
|
|
done < <(hcitool scan --flush | tail -n +2 | sed 's/\t/\n/g')
|
|
fi
|
|
|
|
# display any found addresses that are not already paired
|
|
for mac in "${!found[@]}"; do
|
|
if [[ -z "${paired[$mac]}" ]]; then
|
|
echo "$mac"
|
|
echo "${found[$mac]}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function list_registered_bluetooth() {
|
|
local line
|
|
while read line; do
|
|
if [[ "$line" =~ ^(.+)\ \((.+)\)$ ]]; then
|
|
echo ${BASH_REMATCH[2]}
|
|
echo ${BASH_REMATCH[1]}
|
|
fi
|
|
done < <(bt-device --list 2>/dev/null)
|
|
}
|
|
|
|
function _devices_grep_bluetooth() {
|
|
declare -A devices=()
|
|
local pattern="$1"
|
|
|
|
local mac
|
|
local name
|
|
while read mac; read name; do
|
|
if bt-device --info "$mac" 2>/dev/null | grep -q "$pattern"; then
|
|
echo "$mac"
|
|
echo "$name"
|
|
fi
|
|
done < <(list_registered_bluetooth)
|
|
}
|
|
|
|
function list_paired_bluetooth() {
|
|
_devices_grep_bluetooth "Paired: 1"
|
|
}
|
|
|
|
function list_connected_bluetooth() {
|
|
_devices_grep_bluetooth "Connected: 1"
|
|
}
|
|
|
|
function status_bluetooth() {
|
|
local paired
|
|
local connected
|
|
|
|
local mac
|
|
local name
|
|
|
|
while read mac; read name; do
|
|
paired+="$mac - $name\n"
|
|
done < <(list_paired_bluetooth)
|
|
[[ -z "$paired" ]] && paired="There are no paired devices"
|
|
|
|
while read mac; read name; do
|
|
connected+="$mac - $name\n"
|
|
done < <(list_connected_bluetooth)
|
|
[[ -z "$connected" ]] && connected="There are no connected devices"
|
|
|
|
echo -e "Paired Devices:\n\n$paired\nConnected Devices:\n\n$connected"
|
|
}
|
|
|
|
function remove_device_bluetooth() {
|
|
declare -A devices=()
|
|
local mac
|
|
local name
|
|
|
|
local options=()
|
|
|
|
# show paired devices first
|
|
while read mac; read name; do
|
|
devices+=(["$mac"]="$name")
|
|
options+=("$mac" "$name")
|
|
done < <(list_paired_bluetooth)
|
|
|
|
# then list all other devices known
|
|
while read mac; read name; do
|
|
if [[ -z "${devices[$mac]}" ]]; then
|
|
devices+=(["$mac"]="$name")
|
|
options+=("$mac" "$name")
|
|
fi
|
|
done < <(list_registered_bluetooth)
|
|
|
|
if [[ ${#devices[@]} -eq 0 ]] ; then
|
|
printMsgs "dialog" "There are no devices to remove."
|
|
else
|
|
local cmd=(dialog --backtitle "$__backtitle" --menu "Please choose the bluetooth device you would like to remove" 22 76 16)
|
|
choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
[[ -z "$choice" ]] && return
|
|
|
|
local out
|
|
out=$(bt-device --remove "$choice" 2>&1)
|
|
if [[ "$?" -eq 0 ]] ; then
|
|
printMsgs "dialog" "Device ${devices[$choice]} removed"
|
|
# remove Bluez workaround if it's the last PS3 device
|
|
if [[ "${devices[$choice]}" == *PLAY*3* ]]; then
|
|
list_paired_bluetooth | grep -qE "(Sony )?PLAY.*3"
|
|
[[ ! $? -eq 0 ]] && _bonded_fix_bluetooth "remove"
|
|
fi
|
|
else
|
|
printMsgs "dialog" "Error removing device:\n\n$out"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function pair_bluetooth() {
|
|
declare -A devices=()
|
|
local mac
|
|
local name
|
|
local options=()
|
|
|
|
while read mac; read name; do
|
|
devices+=(["$mac"]="$name")
|
|
options+=("$mac" "$name")
|
|
done < <(list_available_bluetooth)
|
|
|
|
if [[ ${#devices[@]} -eq 0 ]] ; then
|
|
printMsgs "dialog" "No devices were found. Ensure device is on and try again"
|
|
return
|
|
fi
|
|
|
|
local cmd=(dialog --backtitle "$__backtitle" --menu "Please choose the bluetooth device you would like to connect to" 22 76 16)
|
|
choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
[[ -z "$choice" ]] && return
|
|
|
|
mac="$choice"
|
|
name="${devices[$choice]}"
|
|
|
|
if [[ "$name" =~ "PLAYSTATION(R)3 Controller" ]]; then
|
|
bt-device --disconnect="$mac" >/dev/null
|
|
bt-device --set "$mac" Trusted 1 >/dev/null
|
|
if [[ "$?" -eq 0 ]]; then
|
|
printMsgs "dialog" "Successfully authenticated $name ($mac).\n\nYou can now remove the USB cable."
|
|
else
|
|
printMsgs "dialog" "Unable to authenticate $name ($mac).\n\nPlease try to pair the device again, making sure to follow the on-screen steps exactly."
|
|
fi
|
|
return
|
|
fi
|
|
|
|
local cmd=(dialog --backtitle "$__backtitle" --menu "Please choose the security mode - Try the first one, then second if that fails" 22 76 16)
|
|
options=(
|
|
1 "DisplayYesNo"
|
|
2 "KeyboardDisplay"
|
|
3 "NoInputNoOutput"
|
|
4 "DisplayOnly"
|
|
5 "KeyboardOnly"
|
|
)
|
|
choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
[[ -z "$choice" ]] && return
|
|
|
|
local mode="${options[choice*2-1]}"
|
|
|
|
# create a named pipe & fd for input for bluez-simple-agent
|
|
local fifo="$(mktemp -u)"
|
|
mkfifo "$fifo"
|
|
exec 3<>"$fifo"
|
|
local line
|
|
local pin
|
|
local error=""
|
|
local skip_connect=0
|
|
while read -r line; do
|
|
case "$line" in
|
|
"RequestPinCode"*)
|
|
cmd=(dialog --nocancel --backtitle "$__backtitle" --menu "Please choose a pin" 22 76 16)
|
|
options=(
|
|
1 "Pin 0000"
|
|
2 "Enter own Pin"
|
|
)
|
|
choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
pin="0000"
|
|
if [[ "$choice" == "2" ]]; then
|
|
pin=$(dialog --backtitle "$__backtitle" --inputbox "Please enter a pin" 10 60 2>&1 >/dev/tty)
|
|
fi
|
|
dialog --backtitle "$__backtitle" --infobox "Please enter pin $pin on your bluetooth device" 10 60
|
|
echo "$pin" >&3
|
|
# read "Enter PIN Code:"
|
|
read -n 15 line
|
|
;;
|
|
"RequestConfirmation"*)
|
|
# read "Confirm passkey (yes/no): "
|
|
echo "yes" >&3
|
|
read -n 26 line
|
|
skip_connect=1
|
|
break
|
|
;;
|
|
"DisplayPasskey"*|"DisplayPinCode"*)
|
|
# extract key from end of line
|
|
# DisplayPasskey (/org/bluez/1284/hci0/dev_01_02_03_04_05_06, 123456)
|
|
[[ "$line" =~ ,\ (.+)\) ]] && pin=${BASH_REMATCH[1]}
|
|
dialog --backtitle "$__backtitle" --infobox "Please enter pin $pin on your bluetooth device" 10 60
|
|
;;
|
|
"Creating device failed"*)
|
|
error="$line"
|
|
;;
|
|
esac
|
|
# read from bluez-simple-agent buffered line by line
|
|
done < <(stdbuf -oL $(get_script_bluetooth bluez-simple-agent) -c "$mode" hci0 "$mac" <&3)
|
|
exec 3>&-
|
|
rm -f "$fifo"
|
|
|
|
if [[ "$skip_connect" -eq 1 ]]; then
|
|
if hcitool con | grep -q "$mac"; then
|
|
printMsgs "dialog" "Successfully paired and connected to $mac"
|
|
return 0
|
|
else
|
|
printMsgs "dialog" "Unable to connect to bluetooth device. Please try pairing with the commandline tool 'bluetoothctl'"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$error" ]]; then
|
|
error=$(bt-device --set "$mac" Trusted 1 2>&1)
|
|
if [[ "$?" -eq 0 ]] ; then
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
printMsgs "dialog" "An error occurred connecting to the bluetooth device ($error)"
|
|
return 1
|
|
}
|
|
|
|
function udev_bluetooth() {
|
|
declare -A devices=()
|
|
local mac
|
|
local name
|
|
local options=()
|
|
while read mac; read name; do
|
|
devices+=(["$mac"]="$name")
|
|
options+=("$mac" "$name")
|
|
done < <(list_paired_bluetooth)
|
|
|
|
if [[ ${#devices[@]} -eq 0 ]] ; then
|
|
printMsgs "dialog" "There are no paired bluetooth devices."
|
|
else
|
|
local cmd=(dialog --backtitle "$__backtitle" --menu "Please choose the bluetooth device you would like to create a udev rule for" 22 76 16)
|
|
choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
[[ -z "$choice" ]] && return
|
|
name="${devices[$choice]}"
|
|
local config="/etc/udev/rules.d/99-bluetooth.rules"
|
|
if ! grep -q "$name" "$config"; then
|
|
local line="SUBSYSTEM==\"input\", ATTRS{name}==\"$name\", MODE=\"0666\", ENV{ID_INPUT_JOYSTICK}=\"1\""
|
|
addLineToFile "$line" "$config"
|
|
printMsgs "dialog" "Added $line to $config\n\nPlease reboot for the configuration to take effect."
|
|
else
|
|
printMsgs "dialog" "An entry already exists for $name in $config"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function connect_bluetooth() {
|
|
local mac
|
|
local name
|
|
while read mac; read name; do
|
|
bt-device --connect "$mac" 2>/dev/null
|
|
done < <(list_paired_bluetooth)
|
|
}
|
|
|
|
function connect_mode_gui_bluetooth() {
|
|
local mode="$(_get_connect_mode)"
|
|
[[ -z "$mode" ]] && mode="default"
|
|
|
|
local cmd=(dialog --backtitle "$__backtitle" --default-item "$mode" --menu "Choose a connect mode" 22 76 16)
|
|
|
|
local options=(
|
|
default "Bluetooth stack default behaviour (recommended)"
|
|
boot "Connect to devices once at boot"
|
|
background "Force connecting to devices in the background"
|
|
)
|
|
|
|
local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
[[ -n "$choice" ]] && connect_mode_set_bluetooth "$choice"
|
|
}
|
|
|
|
function connect_mode_set_bluetooth() {
|
|
local mode="$1"
|
|
[[ -z "$mode" ]] && mode="default"
|
|
|
|
local config="/etc/systemd/system/connect-bluetooth.service"
|
|
case "$mode" in
|
|
boot|background)
|
|
mkdir -p "$md_inst"
|
|
sed -e "s#CONFIGDIR#$configdir#" -e "s#ROOTDIR#$rootdir#" "$md_data/connect.sh" >"$md_inst/connect.sh"
|
|
chmod a+x "$md_inst/connect.sh"
|
|
cat > "$config" << _EOF_
|
|
[Unit]
|
|
Description=Connect Bluetooth
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=nice -n19 "$md_inst/connect.sh"
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
_EOF_
|
|
systemctl enable "$config"
|
|
;;
|
|
default)
|
|
if systemctl is-enabled connect-bluetooth 2>/dev/null | grep -q "enabled"; then
|
|
systemctl disable "$config"
|
|
fi
|
|
rm -f "$config"
|
|
rm -rf "$md_inst"
|
|
;;
|
|
esac
|
|
iniConfig "=" '"' "$configdir/all/bluetooth.cfg"
|
|
iniSet "connect_mode" "$mode"
|
|
chown "$__user":"$__group" "$configdir/all/bluetooth.cfg"
|
|
}
|
|
|
|
function gui_bluetooth() {
|
|
addAutoConf "8bitdo_hack" 0
|
|
|
|
while true; do
|
|
local connect_mode="$(_get_connect_mode)"
|
|
|
|
local cmd=(dialog --backtitle "$__backtitle" --menu "Configure Bluetooth Devices" 22 76 16)
|
|
local options=(
|
|
P "Pair and Connect to Bluetooth Device"
|
|
X "Remove Bluetooth Device"
|
|
S "Show Paired & Connected Bluetooth Devices"
|
|
U "Set up udev rule for Joypad (required for joypads from 8Bitdo etc)"
|
|
C "Connect now to all paired devices"
|
|
M "Configure bluetooth connect mode (currently: $connect_mode)"
|
|
)
|
|
|
|
local atebitdo
|
|
if getAutoConf 8bitdo_hack; then
|
|
atebitdo=1
|
|
options+=(8 "8Bitdo mapping hack (ON - old firmware)")
|
|
else
|
|
atebitdo=0
|
|
options+=(8 "8Bitdo mapping hack (OFF - new firmware)")
|
|
fi
|
|
|
|
local choice=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
|
|
if [[ -n "$choice" ]]; then
|
|
# temporarily restore Bluetooth stack (if needed)
|
|
service sixad status &>/dev/null && sixad -r
|
|
case "$choice" in
|
|
P)
|
|
pair_bluetooth
|
|
;;
|
|
X)
|
|
remove_device_bluetooth
|
|
;;
|
|
S)
|
|
printMsgs "dialog" "$(status_bluetooth)"
|
|
;;
|
|
U)
|
|
udev_bluetooth
|
|
;;
|
|
C)
|
|
connect_bluetooth
|
|
;;
|
|
M)
|
|
connect_mode_gui_bluetooth
|
|
;;
|
|
8)
|
|
atebitdo="$((atebitdo ^ 1))"
|
|
setAutoConf "8bitdo_hack" "$atebitdo"
|
|
;;
|
|
esac
|
|
else
|
|
# restart sixad (if running)
|
|
service sixad status &>/dev/null && service sixad restart && printMsgs "dialog" "NOTICE: The ps3controller driver was temporarily interrupted in order to allow compatibility with standard Bluetooth peripherals. Please re-pair your Dual Shock controller to continue (or disregard this message if currently using another controller)."
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
function _bonded_fix_bluetooth() {
|
|
local mode=$1
|
|
# exit when we don't have an op mode (install/remove) or a config file
|
|
[[ -z "$mode" ]] && return
|
|
[[ ! -f "/etc/bluetooth/input.conf" ]] && return
|
|
|
|
if [[ "$mode" == "add" ]]; then
|
|
# configure the workaround
|
|
iniConfig "=" '' "/etc/bluetooth/input.conf"
|
|
iniSet "ClassicBondedOnly" "false"
|
|
fi
|
|
|
|
if [[ "$mode" == "remove" ]]; then
|
|
# remove the workaround
|
|
iniConfig "=" '' "/etc/bluetooth/input.conf"
|
|
iniSet "ClassicBondedOnly" "true"
|
|
fi
|
|
}
|