0

Why is the rofi menu not showing available the bluetooth devices when it's enabled and scanned via bluetoothctl? Here is the script that is used to make a shortcut to open a rofi styled bluetooth menu:

#!/usr/bin/env bash # Function to enable Bluetooth enable_bluetooth() { sudo rfkill unblock bluetooth sudo bluetoothctl power on sleep 2 # Short delay to ensure Bluetooth is powered on } # Function to disable Bluetooth disable_bluetooth() { sudo bluetoothctl power off sleep 2 # Short delay to ensure Bluetooth is powered off } # Function to scan for Bluetooth devices scan_bluetooth_devices() { sudo bluetoothctl scan on & sleep 10 # Allow some time for scanning sudo bluetoothctl devices | while read -r line; do device_mac=$(echo "$line" | awk '{print $2}') device_name=$(echo "$line" | cut -d' ' -f3-) if sudo bluetoothctl info "$device_mac" | grep -q "Connected: yes"; then echo " $device_name (connected)" else echo " $device_name" fi done } # Ensure the bluetooth service is running sudo systemctl start bluetooth # Check the status of the bluetooth service service_status=$(sudo systemctl is-active bluetooth) if [[ "$service_status" != "active" ]]; then notify-send "Bluetooth Service" "Bluetooth service is not active. Please check the systemctl status." exit 1 fi # Main menu loop while true; do # Get Bluetooth power status bluetooth_status=$(sudo bluetoothctl show | grep "Powered:" | awk '{print $2}') if [[ "$bluetooth_status" == "yes" ]]; then toggle="󰂯 Disable Bluetooth" else toggle="󰂮 Enable Bluetooth" fi # Prompt user to select an option chosen_option=$(echo -e "$toggle\n󰍾 Scan for devices\n󰍃 Exit" | rofi -dmenu -i -selected-row 1 -p "Bluetooth Menu") # Handle user selection if [ -z "$chosen_option" ]; then exit elif [ "$chosen_option" = "󰂮 Enable Bluetooth" ]; then enable_bluetooth elif [ "$chosen_option" = "󰂯 Disable Bluetooth" ]; then disable_bluetooth elif [ "$chosen_option" = "󰍾 Scan for devices" ]; then device_list=$(scan_bluetooth_devices) chosen_device=$(echo -e "$device_list\n󰍃 Back" | rofi -dmenu -i -selected-row 1 -p "Available Devices: ") if [ "$chosen_device" = "󰍃 Back" ] || [ -z "$chosen_device" ]; then continue else # Extract MAC address and name of connection chosen_mac=$(echo "$chosen_device" | awk '{print $2}') chosen_name=$(echo "$chosen_device" | cut -d' ' -f3- | sed 's/ (connected)//') # Message to show when connection is activated successfully success_message="You are now connected to the Bluetooth device \"$chosen_name\"." # Check if the device is already connected if sudo bluetoothctl info "$chosen_mac" | grep -q "Connected: yes"; then sudo bluetoothctl disconnect "$chosen_mac" && notify-send "Bluetooth Disconnected" "You are now disconnected from the Bluetooth device \"$chosen_name\"." else sudo bluetoothctl connect "$chosen_mac" && notify-send "Bluetooth Connected" "$success_message" fi fi elif [ "$chosen_option" = "󰍃 Exit" ]; then exit fi done 

1 Answer 1

0

I had now time to look at your script.
There is a problem with bluetoothctl scan on in asynchron mode.
This command never stops writing and exits. Thats why your menu doesn't show up.
EDIT: 12.7.24 - I rewrote the whole script.
Now I start bluetoothctl and made the script attendant in a process substitution for bluetoothctl input.
I integrated my own read-command editor, tested with gnome and xterm terminal and which supports POS1, DEL, Backspace, Arrow left-right, copy and paste, insert mode.

F1 ... Bluetooth Controller menu,
which shows up at program start up
hit ESC to get in bluetoothctl (read) editor mode

F2 ... Bluetooth Device Action menu - device actions show up like info, connect
After choosing an action, the Bluetooth Device menu shows up to choose a device for the action.

F3 ... Bluetooth Connector menu - its like the Bluetooth Device menu, which shows up after a chosen action, but it lets you connect and disconnect to a device.
A String shows up e.g C:1,P:1,T:0,B:0 - which means (C)onnected: yes, (P)aired:yes, (T)rusted: no, (B)locked: no

try it! and let me know your feedback!

#!/usr/bin/env bash #-------------------- #-- read-editor BEGIN #-------------------- #-- supports pos1, end, backspace, del, insert mode, left arrow, right arrow, copy and paste, and resizing terminal declare editor_text #-- editor_cursorLine INT ... cursor position line #-- editor_cursorColumn INT ... cursor position column #-- editor_textPos INT ... cursor position inside editor text starting with index 1, cursor text position #-- editor_insertMode BOOL ... indicates, if insertMode is ON or OFF (Standard: ON) declare -i editor_cursorLine editor_cursorColumn editor_textPos editor_insertMode=1 #-- get actual LINES and COLUMNS of the terminal editor_getLinesColumns () { LINES=$(tput lines cols); COLUMNS=${LINES#*$'\n'}; LINES=${LINES%$'\n'*} } #-- gets the actual cursor position of the terminal editor_getCursorPos () { local cursorPos echo -n $'\e[6n'; IFS= read -sd R cursorPos; cursorPos=${cursorPos:2} editor_cursorLine=${cursorPos%;*}; editor_cursorColumn=${cursorPos#*;} } editor_readKey () { #-- key CHAR ... the actual key in the loop (char as UTF-8) or a part of an Ansi Escape Code Sequence ( \e or [ or ..) #-- keys STR ... saves the recognized key or keys (pressed simultaneous) or the keys (characters) of an or more Ansi Escape Code Sequence(s) #-- keyCode INT ... the Unicode charCode decimal value for the key or the ASCII charCode decimal for a part of an Ansi Escape Code Sequence #-- cC_counter INT ... charCode counter for the charCode decimal values added to keyCodeSequence #-- to_counter INT ... timeout counter between two sent keys #-- keyCodeSequence STR ... one or a series of Unicode charCode decimals - e.g when pressed an arrow, page down or .. local key keys keyCodeSequence="" keyCodeSequence_break=$1 last_AECS="" timeout=${2:-"0.010"} timeEC=${3:-"0.2"} #-- last_AECS STR ... last Ansi Escape Code Sequence without '\e' local -i keyCode cC_counter=0 to_counter=0 to_lastCount=0 to_limit wasAECS=0 #-- wasAECS BOOL ... indicates, if last key send an Ansi Escape Code Sequence to_limit=$(bc <<< "scale=0; $timeEC/$timeout") #-- to_limit INT ... timeEC measured in timeouts - error max (is - real): -1 timeout editor_textPos=1; editor_text=""; editor_getCursorPos stty -echo #-- turn terminal echo off while :; do read -N 1 -r -t $timeout key #--check for time out failure, in exit status of read -> Time Out is End of Sequence! if (( $? == 0 )); then printf -v keyCode "%d" "'$key"; keyCodeSequence+="$keyCode "; keys+=$key; ((to_counter ? to_lastCount=to_counter, to_counter=0, cC_counter++ : cC_counter++)) else ((to_counter++)) if ((cC_counter)); then keyCodeSequence=${keyCodeSequence:0:-1} #-- remove space at end [[ $keyCodeSequence == "$1" ]] && break #-- break loop, if pressed key is breaking key if ((cC_counter>1)); then #-- possible Ansi Escape Code Sequence if [[ ${keyCodeSequence:0:3} == "27 " ]]; then #-- Ansi Escape Code Sequence editor_readKey_action wasAECS=1; last_AECS=${keys##*$'\e'} elif ((wasAECS)); then #-- last Sequence was an Ansi Escape Code Sequence #-- Error Correction of missing $'\e', if time between sent keys is smaller or equal timeEC if [[ $to_lastCount -le $to_limit && $keys == $last_AECS ]]; then keyCodeSequence="27 $keyCodeSequence"; editor_readKey_action else wasAECS=0; #-- if not, set wasAECS to 0 and skip complete sequence fi else editor_readKey_action #-- normal keys are pressed simultaneous fi else editor_readKey_action #-- normal single Unicode key and keyCode wasAECS=0 fi [[ $keyCodeSequence == "$1" ]] && break #-- break loop, if readKey_action changed keyCodeSequence to break keys=""; keyCodeSequence=""; cC_counter=0 fi fi done stty echo #-- turn terminal echo on } editor_readKey_action () { #-- a, b, c, d INT ... for result storage #-- editor_textL_S INT ... length of text at Start of editor_readKey_action func #-- editor_textL_E INT ... length of text after End of manipulation in editor_readKey_action local -i editor_textL_S=${#editor_text} editor_textL_E editor_textCL=${#keys} a b c d #-- for control keys case $keyCodeSequence in #-- F1 ... show Bluetooth Controller menu "27 79 80") controllerMenuInLoop=1; keyCodeSequence=$keyCodeSequence_break; editor_text=""; return;; #-- F2 ... show Bluetooh Device Action menu "27 79 81") show_deviceActionMenu; [[ $keys ]] && show_devicesMenu "CFA";; #-- F3 ... show Bluetooth Connector menu "27 79 82") show_devicesMenu;; #-- backspace *127*) if ((editor_textPos>1 && (editor_cursorColumn!=1 || editor_cursorLine!=1))); then if ((editor_cursorColumn > 1)); then ((editor_cursorColumn--, editor_textPos--)); echo -ne "\b\e[J\e7${editor_text:editor_textPos}\e8"; else ((editor_cursorLine--, editor_cursorColumn=COLUMNS, editor_textPos--)); echo -ne "\e[$editor_cursorLine;${editor_cursorColumn}H\e[J\e7${editor_text:editor_textPos}\e8" fi editor_text=${editor_text:0:editor_textPos-1}${editor_text:editor_textPos} fi;;& #-- del *"27 91 51 126"*) echo -ne "\e[J\e7${editor_text:editor_textPos}\e8"; editor_text=${editor_text:0:editor_textPos-1}${editor_text:editor_textPos};;& #-- insert *"27 91 50 126"*) (( editor_insertMode= ! editor_insertMode ));;& #-- right arrow *"27 91 67"*) if ((editor_textPos<=editor_textL_S)); then if ((editor_cursorColumn < COLUMNS)); then echo -n $'\e[1C'; ((editor_cursorColumn++, editor_textPos++)) elif ((editor_cursorLine != LINES)); then ((editor_cursorLine++, editor_cursorColumn=1, editor_textPos++)); echo -ne "\e[$editor_cursorLine;${editor_cursorColumn}H"; fi fi;;& #-- left arrow *"27 91 68"*) if ((editor_textPos>1 && (editor_cursorColumn!=1 || editor_cursorLine!=1))); then if ((editor_cursorColumn > 1)); then echo -n $'\e[1D'; ((editor_cursorColumn--, editor_textPos--)) else ((editor_cursorLine--, editor_cursorColumn=COLUMNS, editor_textPos--)); echo -ne "\e[$editor_cursorLine;${editor_cursorColumn}H" fi fi;;& #-- pos 1 *"27 91 72"*) if ((d=editor_cursorColumn+(editor_cursorLine-1)*COLUMNS, d>editor_textPos && (editor_cursorColumn!=1 || editor_cursorLine!=1))); then (( editor_cursorColumn=editor_cursorColumn-editor_textPos+1, editor_cursorColumn<=0 ? editor_cursorLine+=editor_cursorColumn/COLUMNS-1, editor_cursorColumn=editor_cursorColumn%COLUMNS+COLUMNS : 1, editor_textPos=1)) else ((editor_cursorColumn=1, editor_cursorLine=1, editor_textPos-=d-1)) fi echo -ne "\e[$editor_cursorLine;${editor_cursorColumn}H";;& #-- pos end *"27 91 70"*) (( editor_cursorColumn+=editor_textL_S-editor_textPos+1, editor_cursorLine+=editor_cursorColumn/COLUMNS, editor_cursorColumn%=COLUMNS, editor_cursorColumn==0 ? editor_cursorColumn=1, editor_cursorLine-- : 1 )) echo -ne "\e[$editor_cursorLine;${editor_cursorColumn}H"; editor_textPos=editor_textL_S+1;; esac #-- for printable keys, copy STRG+SHIFT+C and paste STRG+SHIFT+V if [[ $keyCodeSequence != "27 "* && $keyCodeSequence != *127* ]]; then #-- cursor at end position+1 of editor text, build new editor_text, and print pressed keys if ((editor_textPos>editor_textL_S)); then editor_text=$editor_text$keys; editor_textL_E=${#editor_text}; echo -n "$keys" #-- cursor position inside editor_text and insertMode is ON elif ((editor_insertMode)); then #-- print pressed keys if ((editor_cursorColumn==COLUMNS)); then echo -ne "\e7\e[J$keys${editor_text:editor_textPos-1}\e8" #-- cursor moves to next line else echo -ne "$keys\e7\e[J${editor_text:editor_textPos-1}\e8";fi #-- cursor stays in line #-- build new editor_text editor_text=${editor_text:0:editor_textPos-1}$keys${editor_text:editor_textPos-1}; editor_textL_E=${#editor_text} #-- if characters are inserted inside the editor_text, check if the end of the editor_text has exceeded the terminal LINES (( a=editor_cursorColumn+editor_textL_E-editor_textPos, b=a%COLUMNS, c= b == 0 ? editor_cursorLine+a/COLUMNS-1 : editor_cursorLine+a/COLUMNS )) (( c>LINES )) && { (( c=c-LINES, editor_cursorLine-=c )); echo -ne "\e[${c}A"; } #-- cursor position inside editor_text and insertMode is OFF, build new editor_text, and print pressed keys else editor_text=${editor_text:0:editor_textPos-1}$keys${editor_text:editor_textPos+editor_textCL-1}; editor_textL_E=${#editor_text}; echo -n "$keys" fi #-- calculate new cursor position - line and column, and new cursor text position (( editor_textPos+=editor_textCL, editor_cursorColumn+=editor_textCL, editor_cursorLine+=(editor_cursorColumn-1)/COLUMNS, editor_cursorLine>LINES ? editor_cursorLine=LINES:1 )) (( editor_cursorColumn=editor_cursorColumn%COLUMNS, editor_cursorColumn==0 ? editor_cursorColumn=COLUMNS : 1 )) #-- inserted text ends at column=COLUMNS, move cursor to column 1 at next line if ((editor_cursorColumn==1)); then if ((editor_textPos>editor_textL_E)); then echo -ne " \e[1D" #-- cursor at end position+1 of editor text elif (( !editor_insertMode )); then echo -ne "${editor_text:editor_textPos-1:1}\e[1D" #-- cursor position inside editor_text and insertMode is ON else echo -ne "${editor_text:editor_textPos-2:2}\e[1D"; fi #-- cursor position inside editor_text and insertMode is OFF fi fi } #------------------ #-- read-editor END #------------------ declare response_BCTL declare -i shortdelay=2 readInLoop=1 controllerMenuInLoop=1 #-- builds and opens Bluetooth Device Action menu show_deviceActionMenu () { local chosen_opt chosen_opt=$(echo -e "info\nremove\nconnect\ndisconnect\npair\ncancel-pairing\ntrust\nuntrust\nblock\nunblock" | rofi -dmenu -p "Bluetooth Device Action") keys=$chosen_opt; keyCodeSequence="" } #-- gets the device info string e.g C:1,P:1,T:0,B:0 -> (C)onnected: yes, (P)aired:yes, (T)rusted: no, (B)locked: no get_deviceInfo () { local response_BCTL=$(bluetoothctl info "$1") info_properties [[ $response_BCTL =~ "Connected:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then info_properties="C:1," else info_properties="C:0,"; fi [[ $response_BCTL =~ "Paired:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then info_properties=$info_properties"P:1," else info_properties=$info_properties"P:0,"; fi [[ $response_BCTL =~ "Trusted:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then info_properties=$info_properties"T:1," else info_properties=$info_properties"T:0,"; fi [[ $response_BCTL =~ "Blocked:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then info_properties=$info_properties"B:1" else info_properties=$info_properties"B:0"; fi echo "$info_properties" } #-- builds and opens the Bluetooth Connector menu or the Bluetooth Device menu, if called with first parameter "CFA" (C)alled (F)rom (A)ction show_devicesMenu () { local deviceMenu_opts chosen_opt deviceAsSTR macAddress device local -i connected response_BCTL=$(bluetoothctl devices) while IFS=" " read -r deviceAsSTR macAddress device; do [[ $macAddress ]] && deviceMenu_opts=$deviceMenu_opts"$device $(get_deviceInfo "$macAddress") $macAddress"$'\n' done <<< $response_BCTL if [[ $1 != "CFA" ]]; then #-- Bluetooth Connector menu deviceMenu_opts=$deviceMenu_opts"Show Bluetooth Controller Menu"$'\n' chosen_opt=$(echo -n "$deviceMenu_opts" | rofi -dmenu -p "Bluetooth Connector") case $chosen_opt in "Show Bluetooth Controller"*) controllerMenuInLoop=1; [[ $keyCodeSequence_break ]] && { keys=""; keyCodeSequence=$keyCodeSequence_break; editor_text=""; };; "") return;; *) device=${chosen_opt% *}; device=${device##* }; connected=${device:2:1} if ((connected));then echo "disconnect ${chosen_opt##* }" else echo "connect ${chosen_opt##* }" fi controllerMenuInLoop=0;; esac else #-- Bluetooth Device menu called from Bluetooth Device Action menu chosen_opt=$(echo -n "$deviceMenu_opts" | rofi -dmenu -p "Bluetooth Devices") case $chosen_opt in "") return;; *) keys=$keys" ${chosen_opt##* }"; keyCodeSequence=$keyCodeSequence_break;; esac fi } #-- builds and opens the Bluetooth Controller menu show_controllerMenu () { local controllerMenu_opts chosen_opt response_BCTL=$(bluetoothctl show) controllerMenu_opts=$controllerMenu_opts$'Scan for Devices: 15 seconds\n' controllerMenu_opts=$controllerMenu_opts$'Scan for Devices: 20 seconds\n' controllerMenu_opts=$controllerMenu_opts$'Scan for Devices: 25 seconds\n' controllerMenu_opts=$controllerMenu_opts$'Scan for Devices: 30 seconds\n' controllerMenu_opts=$controllerMenu_opts$'Show Bluetooth Connector\n' controllerMenu_opts=$controllerMenu_opts$'Show Controller Info\n' [[ $response_BCTL =~ "Discovering:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then controllerMenu_opts=$controllerMenu_opts"Scan On - set Scan Off"$'\n' else controllerMenu_opts=$controllerMenu_opts"Scan Off - set Scan On"$'\n'; fi [[ $response_BCTL =~ "Powered:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then controllerMenu_opts=$controllerMenu_opts"Power On - set Power Off"$'\n' else controllerMenu_opts=$controllerMenu_opts"Power Off - set Power On"$'\n'; fi [[ $response_BCTL =~ "Pairable:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then controllerMenu_opts=$controllerMenu_opts"Pairable On - set Pairable Off"$'\n' else controllerMenu_opts=$controllerMenu_opts"Pairable Off - set Pairable On"$'\n'; fi [[ $response_BCTL =~ "Discoverable:"[^$'\n']* ]] if [[ ${BASH_REMATCH[0]#* } == "yes" ]]; then controllerMenu_opts=$controllerMenu_opts"Discoverable On - set Discoverable Off"$'\n' else controllerMenu_opts=$controllerMenu_opts"Discoverable Off - set Discoverable On"$'\n'; fi controllerMenu_opts=$controllerMenu_opts$'Exit\n' chosen_opt=$(echo -n "$controllerMenu_opts" | rofi -dmenu -p "Bluetooth Controller") case $chosen_opt in *"15 seconds") echo "Scanning for 15 seconds" >/dev/tty; echo "scan on"; sleep 15; echo "scan off"; sleep $shortdelay;; *"20 seconds") echo "Scanning for 20 seconds" >/dev/tty; echo "scan on"; sleep 20; echo "scan off"; sleep $shortdelay;; *"25 seconds") echo "Scanning for 25 seconds" >/dev/tty; echo "scan on"; sleep 25; echo "scan off"; sleep $shortdelay;; *"30 seconds") echo "Scanning for 30 seconds" >/dev/tty; echo "scan on"; sleep 30; echo "scan off"; sleep $shortdelay;; "Show Bluetooth"*) show_devicesMenu;; *"Controller Info") echo "show"; controllerMenuInLoop=0;; *"Scan On") echo "scan on"; sleep $shortdelay;; *"Scan Off") echo "scan off"; sleep $shortdelay;; *"set Power Off") echo "power off"; sleep $shortdelay;; *"set Power On") echo "power on"; sleep $shortdelay;; *"set Pairable Off") echo "pairable off"; sleep $shortdelay;; *"set Pairable On") echo "pairable on"; sleep $shortdelay;; *"set Discoverable Off") echo "discoverable off"; sleep $shortdelay;; *"set Discoverable On") echo "discoverable on"; sleep $shortdelay;; "Exit") exit;; "") controllerMenuInLoop=0;; esac } # Ensure, that the bluetooth service is running [[ $(systemctl is-active bluetooth.service) != "active" ]] && sudo systemctl start bluetooth.service # Check the status of the bluetooth service [[ $(systemctl is-active bluetooth.service) != "active" ]] && { notify-send "Bluetooth Service" "Bluetooth service is not active. Please check the systemctl status." exit 1; } editor_getLinesColumns #-- get terminal LINES and COLUMNS #-- main loop in process substitution for bluetoothctl input bluetoothctl < <( trap 'editor_getCursorPos; editor_getLinesColumns' SIGWINCH while ((readInLoop)); do while ((controllerMenuInLoop)); do show_controllerMenu done editor_readKey "10" "0.02" >/dev/tty; [[ $editor_text == "exit" ]] && exit; echo -n $'\n'> /dev/tty; [[ $editor_text ]] && echo "$editor_text" done) 

rest is your work to finish this, how you want it.

8
  • but you scripts doesn't show the available devices... ❯ ./bluetoothmenu [sudo] password for fam007e: scanning for bluetooth devices: 10 seconds SetDiscoveryFilter success ./bluetoothmenu: line 68: kill: (279427) - No such process Commented Jul 2, 2024 at 10:54
  • @DarKnightz i tested this on my bluetooth notebook. first rofi menu scan devices. Then bluetoothctl scan on is started asynchron and writes to stdout, which goes to screen. During that, sleep 10 is executed. Does the scan on command list all available devices?. What happens then, does the second rofi menu show up? Commented Jul 2, 2024 at 17:38
  • It doesn't show the available devices. However running bluetoothctl works fine when done in terminal as standalone code... Commented Jul 4, 2024 at 0:17
  • @DarKnightz I rewrote the complete script, no asynchron bluetoothctl, i made the script attendant, try it, and let me know your feedback! read help above Commented Jul 12, 2024 at 20:44
  • I got these following error when I was scanning or trying to make bluetooth discoverable. Errors: scan on [bluetooth]# SetDiscoveryFilter failed: org.bluez.Error.NotReady [bluetooth]# Failed to start discovery: org.bluez.Error.NotReady [bluetooth]# scan off [bluetooth]# Failed to stop discovery: org.bluez.Error.NotReady [bluetooth]# discoverable on [bluetooth]# Failed to set discoverable on: org.bluez.Error.Failed Commented Jul 14, 2024 at 9:43

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.