Skip to content

Instantly share code, notes, and snippets.

@albertbori
Last active March 28, 2024 13:29
Show Gist options
  • Save albertbori/1798d88a93175b9da00b to your computer and use it in GitHub Desktop.
Save albertbori/1798d88a93175b9da00b to your computer and use it in GitHub Desktop.
Automatically disable Wifi when an Ethernet connection (cable) is plugged in on a Mac
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.asb.toggleairport</string>
<key>OnDemand</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/Library/Scripts/toggleAirport.sh</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/SystemConfiguration</string>
</array>
</dict>
</plist>

Overview

This is a bash script that will automatically turn your wifi off if you connect your computer to an ethernet connection and turn wifi back on when you unplug your ethernet cable/adapter. If you decide to turn wifi on for whatever reason, it will remember that choice. This was improvised from this mac hint to work with Yosemite, and without hard-coding the adapter names. It's supposed to support growl, but I didn't check that part. I did, however, add OSX notification center support. Feel free to fork and fix any issues you encounter.

Most the credit for these changes go to Dave Holland.

Requirements

  • Mac OSX 10+
  • Administrator privileges

Installation Instructions

  • Copy toggleAirport.sh to /Library/Scripts/
  • Run chmod 755 /Library/Scripts/toggleAirport.sh
  • Copy com.mine.toggleairport.plist to /Library/LaunchAgents/
  • Run chmod 600 /Library/LaunchAgents/com.mine.toggleairport.plist
  • Run sudo launchctl load /Library/LaunchAgents/com.mine.toggleairport.plist to start the watcher

Uninstall Instructions

  • Run sudo launchctl unload /Library/LaunchAgents/com.mine.toggleairport.plist to stop the watcher
  • Delete /Library/Scripts/toggleAirport.sh
  • Delete /Library/LaunchAgents/com.mine.toggleairport.plist
  • Delete /private/var/tmp/prev_eth_on
  • Delete /private/var/tmp/prev_air_on

Misc

To debug, just run: sudo /Library/Scripts/toggleAirport.sh and add echo's wherever you'd like

#!/bin/bash
function set_airport {
new_status=$1
if [ $new_status = "On" ]; then
/usr/sbin/networksetup -setairportpower $air_name on
touch /var/tmp/prev_air_on
else
/usr/sbin/networksetup -setairportpower $air_name off
if [ -f "/var/tmp/prev_air_on" ]; then
rm /var/tmp/prev_air_on
fi
fi
}
function growl {
# Checks whether Growl is installed
if [ -f "/usr/local/bin/growlnotify" ]; then
/usr/local/bin/growlnotify -m "$1" -a "AirPort Utility.app"
else
osascript -e "display notification \"$1\" with title \"Wifi Toggle\" sound name \"Hero\""
fi
}
# Set default values
prev_eth_status="Off"
prev_air_status="Off"
eth_status="Off"
# Grab the names of the adapters. We assume here that any ethernet connection name ends in "Ethernet"
eth_names=`networksetup -listnetworkserviceorder | sed -En 's|^\(Hardware Port: .*Ethernet, Device: (en.)\)$|\1|p'`
air_name=`networksetup -listnetworkserviceorder | sed -En 's/^\(Hardware Port: (Wi-Fi|AirPort), Device: (en.)\)$/\2/p'`
# Determine previous ethernet status
# If file prev_eth_on exists, ethernet was active last time we checked
if [ -f "/var/tmp/prev_eth_on" ]; then
prev_eth_status="On"
fi
# Determine same for AirPort status
# File is prev_air_on
if [ -f "/var/tmp/prev_air_on" ]; then
prev_air_status="On"
fi
# Check actual current ethernet status
for eth_name in ${eth_names}; do
if ([ "$eth_name" != "" ] && [ "`ifconfig $eth_name | grep "status: active"`" != "" ]); then
eth_status="On"
fi
done
# And actual current AirPort status
air_status=`/usr/sbin/networksetup -getairportpower $air_name | awk '{ print $4 }'`
# If any change has occured. Run external script (if it exists)
if [ "$prev_air_status" != "$air_status" ] || [ "$prev_eth_status" != "$eth_status" ]; then
if [ -f "./statusChanged.sh" ]; then
"./statusChanged.sh" "$eth_status" "$air_status" &
fi
fi
# Determine whether ethernet status changed
if [ "$prev_eth_status" != "$eth_status" ]; then
if [ "$eth_status" = "On" ]; then
set_airport "Off"
growl "Wired network detected. Turning AirPort off."
else
set_airport "On"
growl "No wired network detected. Turning AirPort on."
fi
# If ethernet did not change
else
# Check whether AirPort status changed
# If so it was done manually by user
if [ "$prev_air_status" != "$air_status" ]; then
set_airport $air_status
if [ "$air_status" = "On" ]; then
growl "AirPort manually turned on."
else
growl "AirPort manually turned off."
fi
fi
fi
# Update ethernet status
if [ "$eth_status" == "On" ]; then
touch /var/tmp/prev_eth_on
else
if [ -f "/var/tmp/prev_eth_on" ]; then
rm /var/tmp/prev_eth_on
fi
fi
exit 0
@kMikaZu
Copy link

kMikaZu commented Jan 24, 2023

I found a different solution that I can confirm is working on macOS 12.4 Monterey. The script will enable or disable Wi-Fi depending on the status of the Ethernet port.

A few things to note…

  • Modify the ETHERNET_DEVICE with your respective device found in ifconfig. Mine happens to be en7, which is the Belkin USB-C LAN connection to my 2021 MBP.
  • Save the script to the ~/.local/bin folder and make it executable.
  • The script will not run automatically by default. I have it running every minute with crontab (crontab -e), but it can be adjusted accordingly depending on your needs. I'll include my crontab job below.
#!/bin/bash

ETHERNET_DEVICE=en7

NETWORKSETUP=/usr/sbin/networksetup
IFCONFIG=/sbin/ifconfig

WiFi=$($NETWORKSETUP -listallhardwareports | grep -A1 "Wi-Fi" | grep Device | cut -f2 -d' ')

echo "Run $0"
echo "Wifi interface $WiFi"
echo "Ethernet interface $ETHERNET_DEVICE"

EthernetStatus=$($IFCONFIG | grep -A5 $ETHERNET_DEVICE | grep status | cut -f2 -d' ')
echo "Ethernet is currently $EthernetStatus"

if [ "$EthernetStatus" != "active" ]; 
then 
	$NETWORKSETUP -setairportpower $WiFi on
	echo "Wi-Fi set on"
else
	$NETWORKSETUP -setairportpower $WiFi off
	echo "Wi-Fi set off"
fi

Crontab is below…

* * * * * ~/.local/bin/set_wifi_based_on_ethernet.sh >/dev/null 2>&1

Doesn't seem to work when the USB adapter is being removed @davelevine

@kMikaZu
Copy link

kMikaZu commented Jan 25, 2023

Found it, changed: sed -En 's/^\(Hardware Port: (.*Ethernet|USB 10.*), Device: (en.)\)$/\2/p'

@lxy1992
Copy link

lxy1992 commented Feb 20, 2023

Base on this script, I fix those problems and build a command tool to setup this function in one command. It works on Macbook Pro 14. Welcome to use it: https://github.com/lxy1992/mac-network-switch

@nilsou
Copy link

nilsou commented Feb 21, 2023

When I try to install this script on macOS 12.3.1 I get the following error. Can someone tell me how to resolve this the right way?

$ sudo launchctl load /Library/LaunchAgents/com.mine.toggleairport.plist

Warning: Expecting a LaunchDaemons path since the command was ran as root. Got LaunchAgents instead.
`launchctl bootstrap` is a recommended alternative.
/Library/LaunchAgents/com.mine.toggleairport.plist: Path had bad ownership/permissions
Load failed: 122: Path had bad ownership/permissions

Changing the folder did not resolve the issue. launchctl bootstrap load /Library/LaunchAgents/com.mine.toggleairport.plist did also not work...

Reading the error message i think you still need to do:

chmod 600 /Library/LaunchAgents/com.mine.toggleairport.plist

I have the same problem, and the problem is not solved after entering CHMOD 600. But after I typed launchctl load /Library/LaunchAgents/com.mine.toggleairport.plist, I was notified by the script editor that it had been run. But after I disconnected and reconnected the Ethernet, it didn't work...

After thoroughly reading through this https://gist.github.com/masklinn/a532dfe55bdeab3d60ab8e46ccc38a68 I have successfully ran this command to get this to work on any macOS 12.3.x or higher systems
sudo launchctl bootout system /Library/LaunchAgents/com.mine.toggleairport.plist; sudo launchctl bootstrap system /Library/LaunchAgents/com.mine.toggleairport.plist; sudo ifconfig en0 down; sudo ifconfig en0 up; sudo sleep 15;

I'm on 12.6 macOS and not getting the above change to launchctl to work. I run the following: sudo launchctl bootout system /Library/LaunchAgents/com.mine.toggleairport.plist sudo launchctl bootstrap system /Library/LaunchAgents/com.mine.toggleairport.plist sudo ifconfig en0 down sudo ifconfig en0 up

I don't see any errors, but if I do a launchctl list|grep -i 'com.mine' nothing shows. I am assuming that means it isn't running. Any suggestions on what I'm missing here?

After a reboot I tried the following again from above: sudo launchctl bootout system /Library/LaunchAgents/com.mine.toggleairport.plist; sudo launchctl bootstrap system /Library/LaunchAgents/com.mine.toggleairport.plist; sudo ifconfig en0 down; sudo ifconfig en0 up; sudo sleep 15;

Here is what I'm getting: % sudo launchctl bootout system /Library/LaunchAgents/com.mine.toggleairport.plist; sudo launchctl bootstrap system /Library/LaunchAgents/com.mine.toggleairport.plist; sudo ifconfig en0 down; sudo ifconfig en0 up; sudo sleep 15; /Library/LaunchAgents/com.mine.toggleairport.plist: Could not find specified service Boot-out failed: 113: Unknown error: 113 Could not find service.

When it says could not find specified service is the the toggleAirport.sh script listed in the .plist file or something else?

I was able to fix this issue by making the .plist owned by root and wheel:
sudo chown root:wheel /Library/LaunchAgents/com.mine.toggleairport.plist

and then running sudo launchctl load /Library/LaunchAgents/com.mine.toggleairport.plist again.

@bouzou4
Copy link

bouzou4 commented Jun 27, 2023

@albertbori I've fake-forked this into a repo with some small modifications for Ventura support and a simple automation script for install. Thanks for this awesome script!

@ezekiel747
Copy link

ezekiel747 commented Feb 2, 2024

i recommend this repo, which seems to have been inspired by this gist: https://github.com/Calvin-LL/toggleairport
I can confirm it works ok on Sonoma.
Great work, many thanks!

@adamshand
Copy link

adamshand commented Feb 7, 2024

Thanks to this thread for the ideas. I've just created a new version of this in case anyone is interested. Tested on macOS 14.3.

Download and save in your path as wifi-toggle.sh (any name will work).

You need to open the script and change the ETHERNET_REGEX variable to match the name of your ethernet device. You can find the name of your device by running networksetup -listnetworkserviceorder. Your ethernet device name is the text after number in parenthesis (eg. (2) CalDigit TS3).

Run wifi-toggle.sh on and it will install a launchd service in ~/Library/LaunchAgents.

Now ...

  • If your ethernet is active, your wifi will automatically turn off
  • If your ethernet is inactive, your wifi will automatically turn on.

If you want to stop the automatic toggle, run wifi-toggle.sh off.

❯ wifi-toggle.sh help
Automatically toggle macOS Wi-Fi based on ethernet status (uses launchd)

Usage: wifi-toggle.sh [ on | off | help ]
   on - start automatically toggling Wi-Fi (install launchd service)
  off - stop automatically toggling Wi-Fi (uninstall launchd service)
  run - Toggle Wi-Fi status (run by launchd)

@joshuataylor
Copy link

Thanks @adamshand , that worked perfectly on MacOS 14.3.1. I love that it waits until both the ethernet AND the wifi is up. It's been bugging me for ages that sometimes the network doesn't switch properly, and this solves it!

Fantastic combined effort, love gists sometimes, was really hoping I didn't have to kludge with grep matching interfaces today :).

@adamshand
Copy link

Nice, glad it's working!

@kMikaZu
Copy link

kMikaZu commented Mar 7, 2024

@adamshand I will give it a try and have no doubt it will work. In that case, I think it deserves it proper repo or own page :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment