1

This is a follow-up to a post made about 2 years ago to use Apple Script to switch between Wi-Fi networks via the menu bar:

Big Sur 11.1: AppleScript to Automatically Change Wi-Fi Networks

My previously-working script now just returns "missing value":

tell application "System Events" to click (every menu bar item of menu bar 1 of application process "ControlCenter" whose title starts with "Wi‑Fi")
tell application "System Events" to click (every UI element of scroll area of group 1 of window "Control Center" of application process "ControlCenter" whose title starts with "YOUR_SSID_HERE")

It seems macOS Ventura broke my previously solution pretty badly. I can't even use "get properties" to make any sense of of what the hierarchy of UI elements should be anymore. Eg:

tell application "System Events" to click (every menu bar item of menu bar 1 of application process "Control Center" whose description starts with "Wi‑Fi")
delay 3
tell application "System Events" to get properties of UI elements of scroll area 1 of group 1 of window "Control Center" of application process "Control Center"

Result:

{{minimum value:missing value, orientation:missing value, position:{2061, 76}, class:UI element, accessibility description:missing value, role description:"heading", focused:missing value, title:missing value, size:{102, 15}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXHeading", value:missing value, subrole:missing value, selected:missing value, name:missing value, description:"heading"}, {minimum value:missing value, orientation:missing value, position:{2052, 95}, class:checkbox, accessibility description:missing value, role description:"toggle button", focused:missing value, title:missing value, size:{288, 32}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXCheckBox", value:0, subrole:"AXToggle", selected:missing value, name:missing value, description:"toggle button"}, {minimum value:missing value, orientation:missing value, position:{2061, 141}, class:UI element, accessibility description:missing value, role description:"heading", focused:missing value, title:missing value, size:{99, 15}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXHeading", value:missing value, subrole:missing value, selected:missing value, name:missing value, description:"heading"}, {minimum value:missing value, orientation:missing value, position:{2052, 160}, class:checkbox, accessibility description:missing value, role description:"toggle button", focused:missing value, title:missing value, size:{288, 32}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXCheckBox", value:0, subrole:"AXToggle", selected:missing value, name:missing value, description:"toggle button"}, {minimum value:missing value, orientation:missing value, position:{2052, 192}, class:checkbox, accessibility description:missing value, role description:"toggle button", focused:missing value, title:missing value, size:{288, 32}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXCheckBox", value:1, subrole:"AXToggle", selected:missing value, name:missing value, description:"toggle button"}, {minimum value:missing value, orientation:missing value, position:{2052, 224}, class:checkbox, accessibility description:missing value, role description:"toggle button", focused:missing value, title:missing value, size:{288, 32}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXCheckBox", value:0, subrole:"AXToggle", selected:missing value, name:missing value, description:"toggle button"}, {minimum value:missing value, orientation:missing value, position:{2052, 267}, class:UI element, accessibility description:missing value, role description:"disclosure triangle", focused:false, title:missing value, size:{288, 22}, help:missing value, entire contents:{}, enabled:true, maximum value:missing value, role:"AXDisclosureTriangle", value:0, subrole:missing value, selected:missing value, name:missing value, description:"disclosure triangle"}}

This is total gibberish to me and doesn't list any clear UI elements with any name or description attributes that point to a specific Wi-Fi SSID. I also notice a ton of "missing value" strings. By comparison, using Accessibility Inspector and clicking on my desired SSID in the network list, it claims it has the below attributes:

<AXApplication: “Control Center”>
 <AXWindow: “Control Center”>
  <AXGroup>
   <AXScrollArea>
    <AXCheckBox>

Attributes:
   AXParent:  “<AXScrollArea>”
   AXRoleDescription:  “toggle button”
   AXChildren:  “<array of size 0>”
   AXIdentifier:  “wifi-network-MYSSID”
   AXPath:  “Path 0x600003016d90:
  ”
   AXAttributedDescription:  “MYSSID, secure network, 3 bars{
    AXATextAlignmentValue = 0;
    AXFont =     {
        AXFontFamily = ".AppleSystemUIFont";
        AXFontName = ".SFNS-Regular";
        AXFontSize = 13;
        AXVisibleName = "System Font Regular";
    };
    AXForegroundColor = <CGColor 0x6000026161c0> [<CGColorSpace 0x600002620ae0> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Generic RGB Profile)] ( 0 0 0 0.847059 );
}”
   AXEnabled:  “1”
   AXSubrole:  “AXToggle”
   AXRole:  “AXCheckBox”
   AXWindow:  “<AXWindow: “Control Center”>”
   AXValue:  “1”
   AXChildrenInNavigationOrder:  “<array of size 0>”

Actions:
   AXPress - press

I'm really truly stumped here and have been trying to work on this for hours. I just can't for the life of me figure out which child element within "scroll area 1" I should be using. Any ideas?

Thanks.

2 Answers 2

0

If you elect to hack the UI in this manner, your script will break each and every time something changes, which is more often than we might wish.

I don't plan to update to Ventura for at least six months or so, therefore I haven't actually tested the following script to ensure it will definitely work in the latest macOS. But, from El Capitan to Monterey, I haven't had to make any changes to it. This is an AppleScript handler that, when called with the name (SSID) of a WiFi network to which you wish to connect, together with the passkey, will disconnect from the current network and establish a connection to the new network, which can be a hidden network. The SSID is case sensitive, and if you don't supply a valid (correct) passkey, it will report an error but won't sever your present connection.

use framework "CoreWLAN"

to setWiFiNetwork to ssid as text given key:pass as text
        local ssid, pass
        local Networks, Network
        local successful, err
        
        set Networks to {}
        
        tell my CWWiFiClient's sharedWiFiClient()'s interface()
                setPower_error_(true, missing value)
                
                tell the cachedScanResults() to if it is not missing value ¬
                        then tell the filteredSetUsingPredicate_(get the ¬
                        predicateWithFormat_("ssid ==[c] %@", the ssid) of ¬
                        the current application's NSPredicate) to set the ¬
                        Networks to it

                if the number of Networks = 0 then set the Networks ¬
                        to its scanForNetworksWithName:(the ssid) ¬
                        includeHidden:yes |error|:(missing value)
                
                set Network to anyObject() in the Networks
                associateToNetwork_password_error_(Network, pass, reference)
        end tell
        
        set [successful, err] to the result
        if not successful then error err's ¬
                localizedDescription as text
        successful
end setWiFiNetwork

Example: To join the network whose SSID is "WiFiTit?" and passkey "HopeLess":

setWiFiNetwork to "WiFiTit?" given key:"HopeLess"
--> true
6
  • Thanks, but that script doesn't seem to work in Ventura. It returns this ---- The operation couldn’t be completed. (com.apple.wifi.apple80211API.error error -3924.)" number -2700 from «script» to item ---- In addition, it seems this script is very similar to what the built-in "networksetup" command in Terminal already does in the sense that it takes an SSID and password as inputs. The issue with that is you have to keep your password in plaintext on your computer, and it doesn't work with WPA2 Enterprise, which is stored in the keychain.
    – Joe
    Nov 6, 2022 at 0:46
  • @Joe Sorry for the late reply. ⓵ The error you got demonstrates the script does work as that error can only be returned if the script runs correctly, but in situations where either the SSID does not exist or an incorrect password was supplied. ⓶ networksetup is a shell wrapper around the functions that this script employs. So, in this regard, they are more-or-less the same, but this script will be faster. ⓷ A wifi password shouldn't be close to defining your network's notion of security. Anyone who can read the plaintext has access to your computer, and that'll have bigger implications.
    – CJK
    Nov 10, 2022 at 8:10
  • @Joe ⓸ The script above will only work for WEP, WPA Personal, and WPA2 Personal networks. I wasn't aware you were running an Enterprise network. This requires a different method to be invoked to establish the connection, since it uses IEEE 802.1X authentication that will require a client certificate to validate its identity.
    – CJK
    Nov 10, 2022 at 8:16
  • Thank you, that all makes sense. I might just have to wait a while until somebody devises a script that interacts with UI elements like I was trying before. Like you said, this will break constantly since Apple keeps changing the hierarchy of Control Center, especially since it's relatively new to macOS. It is what it is at this point, though.
    – Joe
    Dec 3, 2022 at 22:41
  • @Joe OK, I don't have Ventura, but I might be able to tell you how to get that UI reference you need for your WiFi menu. After all, I've only just noticed that I'm the one who wrote the UI script you've been using, and I followed your link from here and the one that links to as well, and I see I posted an ASObjC script pretty much identical to the one above all those years ago. So I hope I can get you at least closer to where you need to be for old time's sake. Like you, I can't really make heads or tails of that UI element hierarchy output. Cont'd...
    – CJK
    Dec 6, 2022 at 0:00
0

Disclaimer: This script has been written and tested in macOS 12.6.1 (Monterey), not Ventura. It is offered as a hint on how to approach scripting the UI, as Monterey and Ventura appear to have a similar UI hierarchical structure.


For your situation, and for anybody else for whom my first answer wouldn't be appropriate for whatever reason, the following represents a continuation of the previous scripts I've written and submitted for older macOS versions (see @Joe's link at the top of the page, and also the link in that page) that hack (script) the UI in order to switch Wi-Fi networks. This is a series of simulated mouse clicks that mimic what a user would normally have to do to access the Wi-Fi menu and select one of the available networks from the list.

I do not have Ventura but it appears that the changes that affect Ventura's menu bar UI hierarchy first appeared in Monterey, where the UI layout shows a drastic change from what it was previously in Big Sur, whilst seeming to appear to have remained more-or-less unchanged when comparing Monterey to Ventura.

to joinNetwork:SSID
        tell application id "com.apple.SystemEvents"
                tell process "ControlCenter" to script WiFiMenuExtra
                        property parent : it
                        property WiFiMenuExtra : me
                        property id : "com.apple.menuextra.wifi"
                        property menu bar : a reference to menu bar 1
                        property menu bar item : a reference to my menu bar's ¬
                                first menu bar item
                        property menu : a reference to (my menu bar item whose ¬
                                value of attribute "AXIdentifier" = my id)
                        
                        property window : a reference to window 1
                        property list : a reference to my window's scroll area 1
                        
                        script Networks
                                property parent : WiFiMenuExtra
                                property Networks : me
                                property all : a reference to my list's checkboxes
                                property selection : a reference to (all ¬
                                        whose name starts with the SSID)
                                property one : a reference to item 1 of the ¬
                                        selection's contents
                        end script
                        
                        property index : 0
                        property handler : a reference to my [success, ¬
                                failure]'s item (a reference to my index)
                        property outcome : missing value
                        
                        to wait for UIElement
                                tell the UIElement to repeat 20 times
                                        if it exists then return it
                                        delay 0.5
                                end repeat
                                
                                error "Timeout." from UIElement
                        end wait
                        
                        on failure()
                                click my menu --close the menu
                                false
                        end failure
                        
                        on success()
                                click my menu --close the menu
                                true
                        end success
                end script
                
                tell the WiFiMenuExtra
                        click its menu
                        wait for the all of its Networks
                        if not (exists its selection of ¬
                                Networks) then return ¬
                                failure()
                        tell one of its Networks
                                if the value ≠ 1 then click
                                get the value * 2 - 1
                        end tell
                        set the index to the result
                        set its outcome to its handler as script
                        its outcome()
                end tell
        end tell
end joinNetwork:

joinNetwork_("WiFiTit?")

A number of obstacles were encountered that necessitated some unconventional tactics in order to get the above script to work in Monterey, so I wouldn't be surprised if there are some similar unexpected errors that get thrown in Ventura initially. We can work through any that arise, although it will probably be quite a painful procedure given I don't have access to Venture myself and knowing how meticulous I had to be to implement the above well enough to be reliably stable. For instance, the reference to the menu bar item had to be filtered using the attribute "AXIdentifier" (which in Monterey is "com.apple.menuextra.wifi"—this might be different in Ventura), as for some reason, attempting to obtain the reference using its name kept throwing errors, but it took a bit of exploring to find another way in.

What This Script Does

Given an SSID, it will open the Wi-Fi menu (which has to be available from the menu bar), and look for a matching Wi-Fi network name under the list of preferred networks. These are the networks that have previously been joined and marked as networks that may be joined automatically.

What This Script Doesn't Do

If the network you want to join is not in the list of Preferred Networks, that's where the buck stops. There is no UI reference that points to the Other Networks menu item, so there's no way to access those other networks using AppleScript.

6
  • Thank you, but on Ventura, it seems to hit a timeout condition.
    – Joe
    Dec 13, 2022 at 2:03
  • @Joe Did you want some help sorting that out ? If so, you'll have to provide more information. If it's the timeout error implemented in the code, then it would have told you the reference of the UI element that it timed-out on. Did the WiFi menu open by this point or not ? And did you check to confirm that the AXIdentifier attribute for your WiFi menu bar icon is "com.apple.menuextra.wifi" ?
    – CJK
    Dec 13, 2022 at 11:05
  • Thanks. It did open the WiFi menubar item, but after a pause, it stops with this ----- error "Timeout." number -2700 from every «class chbx» of list of «class prcs» "ControlCenter" to item
    – Joe
    Dec 14, 2022 at 22:15
  • @Joe OK, I can see where the problem lies. In Monterey, when you click the Wi-Fi menu, the list of Wi-Fi networks that appear aren't menu items, but checkbox elements. This is the same in Ventura. However, in Monterey, each checkbox is named and its name property starts with the SSID of the network. In Ventura, they aren't named at all. However, the output you supplied from Accessibility Inspector shows their AXIdentifier attribute is set to something that relates to the SSID. Cont'd...
    – CJK
    Dec 16, 2022 at 16:28
  • ...Cont'd If you're able to take your last code snippet, which returned the list of properties you included in your question, and instead of get properties of UI elements of..., have it get value of attribute "AXIdentifier" of UI elements of... and share the output of that, this will provide the means to get us over this particular hurdle.
    – CJK
    Dec 16, 2022 at 16:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .