(* Author: Ike Nassi (nassi@nassi.com)
2022-11-02
Written because 13.0 (Ventura) re-wrote System Preferences broke several earlier control panels
Special shout-out to ccstone, whose macro
Window Analysis Tool for System Events v1.50
was essential in figuring out what new the UI hierarchy looks like
*)
tell application "Keyboard Maestro Engine"
set myInput to getvariable "InputDevice"
set myOutput to getvariable "OutputDevice"
end tell
(*
-- temporary test harness:
set myOutput to "USB Sound Device"
set myInput to "Internal Microphone"
*)
tell application "System Settings"
activate
delay 1.0
tell application "System Events"
tell process "System Settings"
set theWindow to first window
delay 0.5
end tell
-- select row mySoundIndex of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of theWindow
-- use this method since the list of panes can change from machine to machine, but the name does not
-- Is there a better way to do this?
keystroke "Sound"
delay 1.5
tell application process "System Settings"
tell radio button 1 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
click
end tell
delay 1
--
-- Now select the output device
--
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 aRow in theRows
-- display dialog "Try this one " & (name of first item of static text of group 1 of UI element 1 of aRow)
try
-- why are there multiple items in this group? I dont' know
--
if (name of first item of static text of group 1 of UI element 1 of aRow) starts with myOutput then
set selected of aRow to true
-- display dialog "Output set to " & myOutput
exit repeat
end if
on error
display dialog "Error setting output sound"
end try
end repeat
--
-- Next, Select the input device
--
tell radio button 2 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
click
end tell
delay 1
-- Note: the input pane is different from the output pane, so we use "group 2" now instead of "group 1" used in the output pane
--
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 aRow in theRows
-- display dialog "Try this one " & (name of first item of static text of group 1 of UI element 1 of aRow)
try
if (name of first item of static text of group 1 of UI element 1 of aRow) starts with myInput then
set selected of aRow to true
-- display dialog "Input set to " & myInput
exit repeat
end if
on error
display dialog "Error setting input sound"
end try
end repeat
end tell
end tell
quit
end tell
Hey Ike, for macros that use AppleScript actions, the bulk of the script ends up hidden in the screenshot.
To help us better understand what the macro (and by extension, AppleScript does), you can paste the AppleScript itself into the post, surround by three tick (`) marks (like I did with your AppleScript). This will automatically format it for easy legibility.
After that, you can also āhide detailsā to reduce the visible length of the post if youād like.
On another note... have you ever heard of the Homebrew package switchaudio-osx? I use it to change audio input and output and itās phenomenal. Itās reported to work on Ventura with Apple Silicon devices; Iām not certain about Intel devices since on the ones I have I am still running Monterey.
-Chris
AppleScript (click to expand/collapse)
(* Author: Ike Nassi (nassi@nassi.com)
2022-11-02
Written because 13.0 (Ventura) re-wrote System Preferences broke several earlier control panels
Special shout-out to ccstone, whose macro
Window Analysis Tool for System Events v1.50
was essential in figuring out what new the UI hierarchy looks like
*)
tell application "Keyboard Maestro Engine"
set myInput to getvariable "InputDevice"
set myOutput to getvariable "OutputDevice"
end tell
(*
-- temporary test harness:
set myOutput to "USB Sound Device"
set myInput to "Internal Microphone"
*)
tell application "System Settings"
activate
delay 0.5
tell application "System Events"
tell process "System Settings"
set theWindow to first window
delay 0.5
end tell
-- select row mySoundIndex of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of theWindow
-- use this method since the list of panes can change from machine to machine, but the name does not
-- Is there a better way to do this?
keystroke "Sound"
delay 1.5
tell application process "System Settings"
tell radio button 1 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
click
end tell
delay 1
--
-- Now select the output device
--
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 aRow in theRows
-- display dialog "Try this one " & (name of first item of static text of group 1 of UI element 1 of aRow)
try
-- why are there multiple items in this group? I dont' know
--
if (name of first item of static text of group 1 of UI element 1 of aRow) starts with myOutput then
set selected of aRow to true
-- display dialog "Output set to " & myOutput
exit repeat
end if
on error
display dialog "Error setting output sound"
end try
end repeat
--
-- Next, Select the input device
--
tell radio button 2 of tab group 1 of group 2 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window "Sound"
click
end tell
delay 1
-- Note: the input pane is different from the output pane, so we use "group 2" now instead of "group 1" used in the output pane
--
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 aRow in theRows
-- display dialog "Try this one " & (name of first item of static text of group 1 of UI element 1 of aRow)
try
if (name of first item of static text of group 1 of UI element 1 of aRow) starts with myInput then
set selected of aRow to true
-- display dialog "Input set to " & myInput
exit repeat
end if
on error
display dialog "Error setting input sound"
end try
end repeat
end tell
end tell
quit
end tell
Haha yea it baffles me why itās advertised one way but the package itself has a different name. Makes it impossible for me to remember which is which.
Ikeās iPhone Microphone
Built-in Microphone
Microsoft Teams Audio
System Audio Recorder
Built-in Output
USB Sound Device
Microsoft Teams Audio
input audio device set to "Built-in Microphone"
Could not find an audio device named "USB Sound Device" of type output. Nothing was changed.
Built-in Output
Woah thatās odd, but Iām glad you got it figured out!
For several years I used to use AppleScript to change audio input and output until I discovered the Homebrew package. Itās so fast, reliable and 100% in the background that Iāve never gone back.
The one thing it doesnāt do is connect Bluetooth devices. For instance, AirPods. It can switch to them if theyāre already connected to the Mac, but it canāt connect to them otherwise. But I donāt use Bluetooth headphones/speakers enough with my Macs to worry about it.
I agree. Pretty slick. I wrapped its use in a KM macro, since Iāve got multiple Macs, multiple locations, with multiple sound systems. I use the same KM macro to manage the complicated setup. I havenāt tried bluetooth on this, but I use airplay extensively at home, so it will be interested to try it out on that.
I use this AppleScript to connect to my AirPods Max, and it has been very reliable:
use framework "IOBluetooth"
use scripting additions
set AirPodsName to "Max"
on getFirstMatchingDevice(deviceName)
repeat with device in (current application's IOBluetoothDevice's pairedDevices() as list)
if (device's nameOrAddress as string) contains deviceName then return device
end repeat
end getFirstMatchingDevice
on toggleDevice(device)
if not (device's isConnected as boolean) then
device's openConnection()
return "Connecting " & (device's nameOrAddress as string)
else
device's closeConnection()
return "Disconnecting " & (device's nameOrAddress as string)
end if
end toggleDevice
return toggleDevice(getFirstMatchingDevice(AirPodsName))
first of all, thank you Jim for providing this very handy little AppleScript for toggling the connection to a Bluetooth Device.
for all others:
There is something I've read somewhere in the WWW (World Wide Web) out there when it comes to scripting with the handling of Information in a process of considering using ASObjC. Maybe I am not completely right with this but who knows - I am not perfect and still learning a lot each day - taking your Script @Jim as an example here is a good one to use with this new Information (if it wasn't known before).
Your Script is written using device's nameOrAddress as string. The less knowledge I have is that this will not compile because the Information you are asking for is in the handling of constants (device's nameOrAddress....) and not a class where this eventually might be possible to ask for a "string". In this case you will have to use device's nameOrAddress as text - otherwise you'll get the error code 2741 when debugging the code with Script Debugger.
Maybe Apple has changed something significant to this in AppleScript but all I am aware of is that they didn't.
I've revised the code and migrated it to use properties for a little possible performance boost and readability.
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
property ptyScriptName : "Toggle Connection to AirPods Max"
property ptyScriptAuthor : "Jim Krenz (KMF=> @Jim)" -- lifting/mod by T.Zittlau (KMF=> @Nr.5-need_input)
property ptyScriptDate : "unknown"
property ptyScriptModDate : "November, 13th 2022 -- GMT+1"
property ptyScriptVer : "1.1"
property ptyExternalLibsUsed : "None"
property ptyOsaxenUsed : "None"
property ptyScriptLang : "AppleScriptObjective-C"
property ptyScriptMigToProp : "YES"
property ptyFrameworksInUse : "IOBluetooth"
property ptyUsedOS : "macOS High Sierra (10.13.6)"
property ptyUsedMac : "MacBook Pro 7.1 13inch 2010"
property ptyUsedAppl : "Script Debugger by LateNight Software Ltd. -- Ver 7.0.13 (7A125)"
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "IOBluetooth"
use scripting additions
(*
NOTE:
Script was changed due to a compile Error in getting the "IOBluetoothDevice's nameOrAddress"
where "as string" couldn't get compiled because of handling with constants.
Script was also migrated to use properties for a little possible performance boost and better
readability.
*)
-- classes, constants, and enums used
property IOBluetoothDevice : a reference to current application's IOBluetoothDevice
set AirPodsName to "Max"
on getFirstMatchingDevice(deviceName)
repeat with device in (IOBluetoothDevice's pairedDevices() as list)
if (device's nameOrAddress as text) contains deviceName then return device
end repeat
end getFirstMatchingDevice
on toggleDevice(device)
if not (device's isConnected as boolean) then
device's openConnection()
return "Connecting " & (device's nameOrAddress as text)
else
device's closeConnection()
return "Disconnecting " & (device's nameOrAddress as text)
end if
end toggleDevice
return toggleDevice(getFirstMatchingDevice(AirPodsName))
This is perfect I just updated one of my machines to Ventura and tried to use a macro i Had to set speakers on the prior OS's and realized it wouldn't work but this post saved me time dong any other research THANK YOU!