deweller/switchaudio-osx

unable to switch to AirPlay output 'devices'

arkarkark opened this issue Β· 56 comments

I'm unable to switch to my airplay 'speakers'

image

$ SwitchAudioSource -a
Built-in Microphone (input)
Built-in Output (output)
$ SwitchAudioSource -t output  -s "Garage"
Could not find an audio device named "Garage" of type output.  Nothing was changed.

+1

this used to work... +1

But: It still let's you switch if you call -s and the exact name of the device! (use "\ " for spaces)
Just switch to your airplay device using syspreferences and run -c and it will also tell you the current device
Edit: This strangly works just as long as system prefercenses are open...

I don't think it has anything to do with System Preferences being open. At first I thought I had it working for a second too, but really what I see now (with macOS Sierra) is:

  • SwitchAudioSource -c will report the AirPlay device if it was manually selected in macOS.
  • Trying to switch to it (SwitchAudioSource -s "Downstairs Speakers") won't work (Could not find an audio device named "Downstairs Speakers" of type output. Nothing was changed.)
  • However if I already have an AirPlay device selected manually, switching away from that device (SwitchAudioSource -s "Built-in Output") actually keeps the AirPlay device selected but then also enables* the second device, at least according to the screenshot below.
  • Then it appears that switching to an AirPlay device works (SwitchAudioSource -s "Downstairs Speakers" now yields output audio device set to "Downstairs Speakers") but in reality the AirPlay device was already selected.

screenshot 2016-12-27 16 17 47

  • Note that when playing audio with this configuration, it does not come out of both devices. As far as I can tell, there's no other way to achieve this UI state in macOS, so this may be a macOS bug?

Any thoughts @deweller?

I don't use AirPlay speakers, so naturally I haven't encountered this bug.

I'd be happy to review a patch if anyone is able to fix it.

Also not working for AirPlay devices for me.
OSX El Capitan + latest SwitchAudio version from brew (as of today).

+1

Any news on this? 😟

Same problem here on High Sierra.
Any news?
Looks like AirPlay outputs are treated differently.
Just to confirm: if my AirPlay receiver has been selected using the menu, I can run -c AirMusic-Box and it confirms, but when I run the same command with any other device selected it claims the device is unknown.
Unfortunately I am a newbie to XCode and it has been a while for me to program in C but I will try insert some diagnostic code and find out...
Maybe the airport receivers have a different device type than "output"...

Whats more so interesting is that say -a '?' also does not show airplay speakers (at least with my configuration using shairport-sync). Therefore, Apple themselves also seem to have not avoided this problem.

Even say --audio-device=? does not find available airplay devices on Mac OS [High] Sierra. One has to connect them manually to become available. Once connected and selected, streaming interactively with say to an airplay speaker doesn't work as expected.

Same issue here :( can't use this with AIrplay speakers

Same here! This project doesn't seem supported anymore, but is anyone created a patch?

As @deweller said, the developers don't use AirPlay. Someone needs to write the code.

Not the greatest solution, but a possible workaround with AppleScript. I combined 2 AppleScripts, based on this tutorial: http://apetronix.com/switch-audio-outputs-with-a-keyboard-shortcut-on-os-x/
HDMI and AirSpeaker are stand-ins.

Set the currentAudioSource to (do shell script "/usr/local/Cellar/switchaudio-osx/1.0.0/SwitchAudioSource -c")
if currentAudioSource is equal to "HDMI" then
tell application "System Events"
tell process "SystemUIServer"
click (menu bar item 1 of menu bar 1 whose description contains "Volume")
set Airplay to menu 1 of result
click ((menu item 1 where its name starts with "AirSpeaker") of Airplay)
end tell
end tell
else
do shell script "/usr/local/Cellar/switchaudio-osx/1.0.0/SwitchAudioSource -s "HDMI""
end if

+1

+1 BIG SUR 11.0.1 (20B29)

The great workaround from @timonvanhasselt sadly don't work under Big Sur.

I don't use AirPlay speakers, so naturally I haven't encountered this bug.

I'd be happy to review a patch if anyone is able to fix it.

I will have a try....

Thanks @alanhg!

I was originally gonna use switchaudio-osx but then AppleScript can do it natively:

set HomePod to "Bedroom"
tell application "System Events"
	tell application process "ControlCenter"
		set soundMenu to menu bar item "Sound" of menu bar 1
		tell soundMenu to click
		set soundCheckbox to checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title contains HomePod
		set soundCheckboxValue to value of soundCheckbox
		tell soundCheckbox to click
		tell soundMenu to click
	end tell
end tell

Great, thank you @demyxco

Attention:
The Window and menu bar items are localized. In German I had to change Sound and Control Center:

set HomePod to "Bedroom"
tell application "System Events"
	tell application process "ControlCenter"
		set soundMenu to menu bar item "Ton" of menu bar 1
		tell soundMenu to click
		set soundCheckbox to checkbox 1 of scroll area 1 of group 1 of window "Kontrollzentrum" whose title contains HomePod
		set soundCheckboxValue to value of soundCheckbox
		tell soundCheckbox to click
		tell soundMenu to click
	end tell
end tell

Here is another one that uses System Preferences but you only changed the "Bedroom" string:

tell application "System Preferences" 
    reveal anchor "output" of pane id "com.apple.preference.sound" 
end tell 

tell application "System Events" to tell process "System Preferences" 
    tell table 1 of scroll area 1 of tab group 1 of window 1 
        select (row 1 where value of text field 1 is "Bedroom") 
    end tell 
end tell 

quit application "System Preferences" 

I found that I didn't have the energy to learn C and how to release the new package, so I decided to use AppleScript and Shell.

it works.

image

Any user who uses Alfred can download this workflow. Easy to switch to any audio include airplay.

https://github.com/alanhg/alfred-workflows/tree/master/switch-audio

kbd commented

@alanhg is the source for that available?

I took the script folks here have shared and enhanced it to make it more reliable to wait for the volume menu to open before switching to the airplay speaker.

I also attached it to a hotkey so I can simply press ⌘ + ' to trigger the switch.

set soundOutputName to "Dining Room"

tell application "System Events"
	tell application process "ControlCenter"
		-- Open Sound menu
		set soundMenu to menu bar item "Sound" of menu bar 1
		tell soundMenu to click
		
		-- Select specified output
		repeat until exists checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title is equal to soundOutputName
		end repeat
		
		set soundCheckbox to checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title is equal to soundOutputName
		tell soundCheckbox to click
		
		-- Close Sound menu
		tell soundMenu to click
	end tell
end tell

https://github.com/alphabt/macos-automations/tree/master/Set%20Sound%20Output has more detail on how to setup the hotkey using macOS's Automator workflow.

@alanhg is the source for that available?

of course

kbd commented

@alanhg is the source for that available?

of course

Where? I only see the alfredworkflow file.

@alanhg is the source for that available?

of course

Where? I only see the alfredworkflow file.
Sorry, Alfred workflow is an archive file, only works in alfred.

For Apple Script, you can view the readme. I have posted link.

https://gist.github.com/alanhg/21f7fd110e0bdac1d0cce66ca40e78ea

I have similar issue with my SDAC. Figured out, what actual name is "SDAC ", with space, for some reason.

Have someone a solution in Monterey?

@Suplanus download Hammerspoon and use my key bind. You can change the hotkey and the string Bedroom since this is what I named my HomePod mini.

hs.hotkey.bind({'ctrl'}, 'Home', function()
    hs.applescript([[
        quit application "System Preferences"

        do shell script "networksetup -setairportpower en0 off"
        do shell script "networksetup -setairportpower en0 on"
        
        do shell script "sleep 2"

        tell application "System Preferences"
            reveal anchor "output" of pane id "com.apple.preference.sound"
        end tell

        do shell script "sleep 2"

        tell application "System Events" to tell process "System Preferences"
            tell table 1 of scroll area 1 of tab group 1 of window 1
                select (row 1 where value of text field 1 is "Bedroom")
            end tell
        end tell

        quit application "System Preferences"
    ]])
end)

Thank you @demyxco.
Hammerspoon is not needed. But I used your apple script inside :)

@demyxco @Suplanus
Oh yeah! Monterey broke the script I used before, but the apple Script works great from your post. Thanks a lot!

@Technofrikus no problem, I've made several revisions since then but here's the current one lol:

quit application "System Preferences"

do shell script "networksetup -setairportpower en0 off"
do shell script "networksetup -setairportpower en0 on"

set wifi to false
repeat until wifi is true
    try
        do shell script "ping -o -t 2 1.1.1.1"
        set wifi to true
    on error
        say "Connecting"
        delay 1
    end try
end repeat

if (wifi = true) then

    tell application "System Preferences"
        reveal anchor "output" of pane id "com.apple.preference.sound"
    end tell

    delay 1

    tell application "System Events" to tell process "System Preferences"
        tell table 1 of scroll area 1 of tab group 1 of window 1
            select (row 1 where value of text field 1 is "Bedroom")
        end tell
    end tell

    quit application "System Preferences"

end if

@demyxco Not sure why you are disabling wifi in the beginning? But if I delete the first part, the rest works still great, thanks!

Sometimes I have a problem to connect to the HomePod, but changing Wifi doesn't help for me. Restarting the HomePod does though.

@Technofrikus sometimes my Mac has trouble connecting to the HomePod but this rarely happens. Glad I can help out!

Was just trying to use this software to so airplay speakers too. +1

@demyxco thanks for this little script:

set HomePod to "Bedroom"
tell application "System Events"
	tell application process "ControlCenter"
		set soundMenu to menu bar item "Sound" of menu bar 1
		tell soundMenu to click
		set soundCheckbox to checkbox 1 of scroll area 1 of group 1 of window "Control Center" whose title contains HomePod
		set soundCheckboxValue to value of soundCheckbox
		tell soundCheckbox to click
		tell soundMenu to click
	end tell
end tell

I'm trying to use it on macOS Ventura but it is giving this error:

Can’t get menu bar item "Sound" of menu bar 1 of application process "ControlCenter".

Any ideas how to update it?

@jdsimcoe As far as I can tell, the new System Settings dont work with AppleScript anymore? I am using the app Keysmith now. Works via simulated mouse clicking, which is not so great, but still better than doing it by hand.

@Technofrikus thanks for the suggestion! I tried to create a macro to set the sound output to my HomePod but all it did is this, which doesn't do anything:
image
It's so frustrating that ο£Ώ breaks AppleScript support for things without a clear path forward.

@jdsimcoe Yes via Control Center it doesnt seem to work, just tried it. I do it via SystemSettings.
CleanShot 2022-11-22 at 19 14 30@2x

@jdsimcoe here's a more recent/updated one I have but I haven't used it in Ventura yet since I switched to a bluetooth speaker:

tell application "System Preferences"
    reveal anchor "output" of pane id "com.apple.preference.sound"
end tell

delay 1

tell application "System Events" to tell process "System Preferences"
    tell table 1 of scroll area 1 of tab group 1 of window 1
        select (row 1 where value of text field 1 is "Mac Studio Speakers")
    end tell
end tell

delay 2

tell application "System Events" to tell process "System Preferences"
    tell table 1 of scroll area 1 of tab group 1 of window 1
        select (row 1 where value of text field 1 is "HomePod mini")
    end tell
end tell

quit application "System Preferences"

Let me know how it goes.

Thanks for sharing, @demyxco. I tried your new one but it also doesn't work. I think Ventura just broke a ton of Applescript API stuff.

@jdsimcoe yeah most likely. When I get more time, I'll try to test it out.

@noonchen has developed BTAudioSwitch, which is supposed to toggle i.a. previously paired Bluetooth/AirPlay devices. It does detect my HomePod, even when it's not set as the output device, but sadly doesn't toggle it (using the -toggleSwitch argument). But maybe there's something in the code that can give @deweller an idea how to proceed in this matter.

EDIT (fyi): Rogue Amoeba's SoundSource is also unable to detect, let alone switch to, my HomePod.

I don't have an AirPlay device handy. Did the latest change which replaces the deprecated audio devices help anything?

Sadly didn't change anything, i.e. SwitchAudioSource prints "AirPlay" after manually switching to the HomePod, but once you use SwitchAudioSource -s or -n, it's not possible anymore to switch back to "AirPlay", and the HomePod has been dropped from the output of -a.

However, all the other programs I've tested that allegedly support Bluetooth pairing, switching & connecting incl. audio devices (BTAudioSwitch, blueutil and BluetoothConnector) aren't working either. So it looks more like something in macOS has changed (or is even broken).

But I assume that the approach to setting a Bluetooth audio device as audio output on the command-line is a lot different under-the-hood than with wired or virtual devices… pair first, then connect & establish whether it's even an audio device in the first place, and if yes, select as audio device. (?)

Addendum: macOS isn't able to handle this either. When I use the system utility Audio MIDI Setup to create an aggregate or multi-output device, e.g. one that includes both "External Headphones" and "AirPlay", macOS will list them in the utility and in the system's sound preferences (devices list), and SwitchAudioSource -a also has them. But when activating them, only the non-AirPlay part of the multi/aggregate device will work because macOS has obviously "lost track" of the HomePod. It does seem that something is broken in macOS, or that Apple developers haven't really thought this through.

This AppleScript seems to work for me on Ventura… combined from online sources. (Change the name of your AirPlay device in line 1, if necessary.) However, unlike before, the whole new UI scripting with scroll area, splitter group etc. doesn't seem to work anymore with System Settings launched hidden, ergo the activate in line 4. If someone finds a way to tweak this, so it works hidden (or at least in the background), please post your solution. πŸ™

set myDevices to {"HomePod"}

tell application "System Settings"
	activate
	reveal anchor "output" of pane id "com.apple.Sound-Settings.extension"
end tell

delay 2

tell application "System Events"
	tell application process "System Settings"
		set theRows to (every row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound")
		repeat with myDevice in myDevices
			set theDevice to myDevice as string
			repeat with aRow in theRows
				if name of first item of static text of group 1 of UI element 1 of aRow is equal to theDevice then
					set selected of aRow to true
					exit repeat
				end if
			end repeat
		end repeat
	end tell
end tell

quit application "System Settings"

You can prepend a second audio device comma-delimited in line 1, e.g. "External Headphones", and then the script will first enable the headphones, and then the AirPlay device, which might be a workaround if your AirPlay device is disconnected & doesn't reconnect.

If someone finds a way to tweak this, so it works hidden (or at least in the background), please post your solution.

I didn't find a way to do this in the background, but you can replace the delay with a loop waiting until the Sound window exists.

Here is the diff

--- a/a.scpt
+++ b/b.scpt
@@ -5,10 +5,11 @@ tell application "System Settings"
 	reveal anchor "output" of pane id "com.apple.Sound-Settings.extension"
 end tell
 
-delay 2
-
 tell application "System Events"
 	tell application process "System Settings"
+		-- Wait until Sound window is available to interact with
+		repeat until exists window "Sound"
+		end repeat
+
 		set theRows to (every row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound")
 		repeat with myDevice in myDevices
 			set theDevice to myDevice as string

And here is the full updated script.

set myDevices to {"HomePod"}

tell application "System Settings"
	activate
	reveal anchor "output" of pane id "com.apple.Sound-Settings.extension"
end tell

tell application "System Events"
	tell application process "System Settings"
		-- Wait until Sound window is available to interact with
		repeat until exists window "Sound"
		end repeat
		
		set theRows to (every row of table 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound")
		repeat with myDevice in myDevices
			set theDevice to myDevice as string
			repeat with aRow in theRows
				if name of first item of static text of group 1 of UI element 1 of aRow is equal to theDevice then
					set selected of aRow to true
					exit repeat
				end if
			end repeat
		end repeat
	end tell
end tell

quit application "System Settings"

Oh nice! This is great!

Thank you! πŸ‘ fyi, also for @deweller: @alanhg has forked this repo and is working on a solution to include AirPlay support. See here: https://github.com/alanhg/switchaudio-osx 🀞

Update for for Sonoma RC

set myDevices to {"HomePod"}

tell application "System Settings"
	activate
	delay 1
	reveal anchor "output" of pane id "com.apple.Sound-Settings.extension"
end tell

tell application "System Events"
	tell application process "System Settings"
		repeat until exists window "Sound"
		end repeat
		delay 1
		set theRows to (every row of outline 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of list 2 of splitter group 1 of list 1 of window "Sound")
		repeat with myDevice in myDevices
			set theDevice to myDevice as string
			repeat with aRow in theRows
				if name of first item of static text of group 1 of UI element 1 of aRow is equal to theDevice then
					set selected of aRow to true
					exit repeat
				end if
			end repeat
		end repeat
	end tell
end tell
quit application "System Settings"

I used an app called UI Builder to fetch the UI path. Warm recommendation.

hjoelh commented

Following this stackoverflow answer & script

I got this slightly modified version to work on my m2 macbook

airplay.applescript (you probably just need to update property airPlayDevice : "Bedroom"

property airPlayDevice : "Bedroom"
property localDevice : "MacBook Pro Speakers"

global initialApplication, initialWindow, currentAudioSource, availableAudioSources

set the currentAudioSource to (do shell script "/opt/homebrew/bin/SwitchAudioSource -c")
set the availableAudioSources to paragraphs of (do shell script "/opt/homebrew/bin/SwitchAudioSource -a")

if currentAudioSource is equal to localDevice then
	if availableAudioSources contains "Airplay" then
		switchToDevice("Airplay")
	else
		connectToAirPlayDevice()
	end if
else
	switchToDevice(localDevice)
end if

-- Switch the current device using switchaudio-osx.
on switchToDevice(device)
	do shell script "/opt/homebrew/bin/SwitchAudioSource -s " & quoted form of device
end switchToDevice


-- Connect to the desired AirPlay device.
on connectToAirPlayDevice()
	openAudioDevices()
	tell application "System Events" to tell application process "Audio MIDI Setup"
		click menu button 1 of splitter group 1 of window "Audio Devices"
		click menu item "Connect AirPlay Device" of menu 1 of menu button 1 of splitter group 1 of window "Audio Devices"
		set the availableDevice to "No AirPlay devices available"
		repeat while availableDevice is "No AirPlay devices available"
			set availableDevice to name of first menu item of menu 1 of menu item "Connect AirPlay Device" of menu 1 of menu button 1 of splitter group 1 of window "Audio Devices"
		end repeat
		click (menu item airPlayDevice of menu 1 of menu item "Connect AirPlay Device" of menu 1 of menu button 1 of splitter group 1 of window "Audio Devices")
	end tell
	tell application "Audio MIDI Setup" to quit
end connectToAirPlayDevice


-- Open and focus Audio Devices
on openAudioDevices()
	tell application "Audio MIDI Setup"
		reopen
		activate
	end tell
	tell application "System Events"
		repeat until (exists window "Audio Devices" of application process "Audio MIDI Setup")
			delay 0.01
		end repeat
	end tell
end openAudioDevices

You can run this like so

osascript airplay.applescript

Update for for Sonoma RC

set myDevices to {"HomePod"}

tell application "System Settings"
	activate
	delay 1
	reveal anchor "output" of pane id "com.apple.Sound-Settings.extension"
end tell

tell application "System Events"
	tell application process "System Settings"
		repeat until exists window "Sound"
		end repeat
		delay 1
		set theRows to (every row of outline 1 of scroll area 1 of group 2 of scroll area 1 of group 1 of list 2 of splitter group 1 of list 1 of window "Sound")
		repeat with myDevice in myDevices
			set theDevice to myDevice as string
			repeat with aRow in theRows
				if name of first item of static text of group 1 of UI element 1 of aRow is equal to theDevice then
					set selected of aRow to true
					exit repeat
				end if
			end repeat
		end repeat
	end tell
end tell
quit application "System Settings"

I used an app called UI Builder to fetch the UI path. Warm recommendation.

Hi @davidaberg,

This is no longer working in Sonoma. Could you help to fix it? Thanks!