0

Apple used to provide a binary called airport at the following location -

/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport

This was able to scan for and produce a list of available SSIDs which would look like the following.

                            SSID BSSID             RSSI CHANNEL HT CC SECURITY (auth/unicast/group)
                  vodafone20645C                   -89  6       Y  -- RSN(PSK/AES/AES) 
                  vodafone20645C                   -89  60      Y  -- RSN(PSK/AES/AES) 
                        ALCL-HGU                   -88  1       Y  -- RSN(PSK/AES/AES) 
                        SKY69QVQ                   -84  11      Y  -- RSN(PSK/AES/AES)

Unfortunately as of macOS Sonoma 14.4 this tool is now deprecated and if executed only displays the following error message.

WARNING: The airport command line tool is deprecated and will be removed in a future release. For diagnosing Wi-Fi related issues, use the Wireless Diagnostics app or wdutil command line tool.

It goes without saying that neither of the referred to tools is a suitable substitute.

I have found a very helpful article on stackoverflow -

How to enable location services for CoreWLAN pyobjc wrapper to get bssid?

Using this as a starting point and by adding the following to it -

    for i in networks:
        if i.ssid() is None:
            continue
        print({'SSID_STR': i.ssid(), 'BSSID': i.bssid(), 'RSSI': i.rssiValue(), 'CHANNEL': i.channel()})

I have already been able to get a result like the following

{'SSID_STR': 'vodafone20645C', 'BSSID': None, 'RSSI': -89, 'CHANNEL': 6}
{'SSID_STR': 'vodafone20645C', 'BSSID': None, 'RSSI': -89, 'CHANNEL': 60}
{'SSID_STR': 'ALCL-HGU', 'BSSID': None, 'RSSI': -88, 'CHANNEL': 1}
{'SSID_STR': 'SKY69QVQ', 'BSSID': None, 'RSSI': -84, 'CHANNEL': 11}

I am however whilst an experienced shell script author very much a novice for Python and was hoping for help in getting this Python output to look exactly like the output from the Apple airport command. The format of the output from the airport command is used by many other existing tools. This would then allow the intended python script to be used as a replacement for the airport tool.

This will involve - if possible adding additional fields e.g. relating to the security settings, and in particular formatting the output to be the same layout as that formerly produced by the airport tool. You will note the righthand alignment of the SSID values in the airport output.

I could I feel already take the above and feed it back to a shell script and use awk, sed, etc. to extract the fields for my own requirement, however in the interest of generating a solution that would be directly usable by anyone (like me) who has previously relied on the airport command, it would be more useful to produce an identical format output.

1 Answer 1

0

Here is a quick and dirty example using f-strings:

  import CoreWLAN
  import re

  print(f"{'SSID' : >32} {'BSSID' : <17} RSSI CHANNEL HT CC SECURITY")

  wifi_interface = CoreWLAN.CWInterface.interface()
  networks, error = wifi_interface.scanForNetworksWithName_error_(None, None)

  for i in networks:
        supportsHT = 'Y' if i.fastestSupportedPHYMode() >= CoreWLAN.kCWPHYMode11n else 'N'
        security = re.search('security=(.*?),', str(i)).group(1)
        print(f"{i.ssid() : >32} {i.bssid() or '' : <17} {i.rssiValue() : <4} {i.channel() : <6}  {supportsHT : <2} {i.countryCode() or '--' : <2} {security}")

Note the f-strings require Python 3.6 or greater.

See https://www.geeksforgeeks.org/string-alignment-in-python-f-string/ for examples on how to use f strings for alignment.

The above uses the same spacing as the airport command and should be safe to use those hardcoded values as SSIDs cannot exceed 32 characters and BSSID is always 17 characters.

For more complex tables I would recommend using either pandas or prettytable.

If you need any of the other data it will be more complicated to get. A starting point would be to check the developer documentation: https://developer.apple.com/documentation/corewlan?language=objc

Also if you want the BSSID you need to grant location permissions to Python. I posted a solution to get that working in the post that you referenced: https://stackoverflow.com/a/75843844/648788

Edit: I have added HT, country, and security. This is a simplified version of security that does not match the output of the airport command but is probably what most people want anyways. To make it match the previous output would require a lot of mapping and knowing all the possible outputs of the command.

Example output with all identifying information changed:

                        SSID BSSID             RSSI CHANNEL HT CC SECURITY
                    example1 XX:XX:XX:XX:XX:XX -34  153     Y  -- WPA2 Personal
                    example2 XX:XX:XX:XX:XX:XX -56  44      Y  US WPA2 Personal
                    example3 XX:XX:XX:XX:XX:XX -48  6       Y  US WPA2 Personal
                    example4 XX:XX:XX:XX:XX:XX -74  149     Y  US WPA2 Personal
                    example5 XX:XX:XX:XX:XX:XX -84  36      Y  US WPA2 Personal
                    example6 XX:XX:XX:XX:XX:XX -65  6       Y  US WPA2 Enterprise
                    example7 XX:XX:XX:XX:XX:XX -32  1       Y  US WPA2/WPA3 Personal
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks @thewade I had not (yet) received an email notification of your answer but I had independently managed to get to a similar stage using rjust and ljust. My main outstanding issue is being able to extract and include the WiFi security information. Whilst some fields like channels, ssid and BSSID were obvious it does not appear to be simply security as I initially guessed.
I have updated the answer to include security, it will return values like WPA2 Personal, WPA2 Enterprise, open, OWE instead of the previous output. If you need something specific let me know, but duplicating the previous output would be time consuming and require lots of experimenting.
Many thanks @thewade I have marked yours as the answer.
For your information under macOS Sonoma I am finding it fails to return the countrycode and I do NOT regard this as an error in your code as the airport binary also failed to include this as you will see from my original example above. My final version is available at github.com/jelockwood/pinpoint/blob/master/pinpoint_scan.py
I was not convinced SSID needs location services, however I have run your script for this, then gone in to security and privacy and enabled location services for Python. An additional step is to do it in a new Terminal window. This HAS fixed both SSID, countrycode and BSSID fields! Many thanks, I will add this as requirements to my own solution in my Wiki doc.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.