Airplane-Mode for macOS (Wi-Fi & Bluetooth)

Hi!

I’m trying to make an “Airplane-Mode” script for macOS.

This script works and turns Wi-Fi & Bluetooth on and off.

-- Don't click [Open this Scriplet in your Editor:]
tell application "System Events" to tell process "SystemUIServer"
	
	-- Wi-Fi
	set wifi to (first menu bar item whose value of attribute "AXDescription" contains "Wi-Fi") of menu bar 1
	click wifi
	tell (first menu item whose title is "Turn Wi-Fi Off") of menu of wifi
		click
		click menu item "Turn Wi-Fi Off"
		tell (first menu item whose title is "Turn Wi-Fi On") of menu of wifi
			click
			click menu item "Turn Wi-Fi On"
		end tell
	end tell
	
	-- Bluetooth
	set bt to (first menu bar item whose value of attribute "AXDescription" contains "bluetooth") of menu bar 1
	click bt
	tell (first menu item whose title is "Turn Bluetooth Off") of menu of bt
		click
		click menu item "Turn Bluetooth Off"
		tell (first menu item whose title is "Turn Bluetooth On") of menu of bt
			click
			click menu item "Turn Bluetooth On"
		end tell
	end tell
end tell

The problem is (for example) when Wi-Fi is ON & Bluetooth Off the script turns Wi-Fi Off and turns Bluetooth ON.

But I want that Wi-Fi & Bluetooth turn Off at the same time (and On at the same time) like the Airplane-Mode (iOS, Android).

I tried to use if/else but I don’t get it so I guess I need a little bit help :slight_smile:

Thx

This sets state of Bluetooth to the state of Wi_fi:


tell application "System Events" to tell process "SystemUIServer"
	
	-- Wi-Fi old state
	try
		set wifi to (first menu bar item whose value of attribute "AXDescription" contains "not enabled") of menu bar 1
		set wifi_IsOn to false
	on error
		set wifi to (first menu bar item whose value of attribute "AXDescription" contains "Wi-Fi") of menu bar 1
		set wifi_IsOn to true
	end try
	
	-- click Wi-Fi menu item
	tell wifi
		click it
		if wifi_IsOn then
			click menu item "Turn Wi-Fi Off" of menu 1
			set bluetoothDescription to "Turn Bluetooth Off"
		else
			click menu item "Turn Wi-Fi On" of menu 1
			set bluetoothDescription to "Turn Bluetooth On"
		end if
	end tell
	
	-- Bluetooth
	set bt to (first menu bar item whose value of attribute "AXDescription" contains "bluetooth") of menu bar 1
	click bt
	click (first menu item whose title is bluetoothDescription) of menu 1 of bt
	
end tell

NOTE: It makes sense to use completely different approach . If the OP is intеrested, I can wrote script for this workaround (no need GUI scripting, uses AsObjC):

  1. script gets Bluetooth current state
  2. script then turns Wi-Fi on/off according to Bluetooth current state
  3. all code should be putted into the on idle handler
  4. script should be saved as stay-open application

I wrote it. Save this script as stay-open application, then run it:


-- Set Wi_Fi on/off according to Bluetooth state

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "IOBluetooth"
use framework "CoreWLAN"
use scripting additions
property CWInterface : a reference to current application's CWInterface
property IOBluetoothHostController : a reference to current application's IOBluetoothHostController


on idle -- comment to debug
	-- get Bluetooth current state
	set controller to IOBluetoothHostController's alloc()'s init()
	set Bluetooth_isOn to (controller's powerState()) as boolean
	-- turn Wi_Fi state according to Bluetooth current state
	if Bluetooth_isOn then
		powerControlEveryWiFiDevices(true) of me
	else
		powerControlEveryWiFiDevices(false) of me
	end if
	return 1
end idle -- comment to debug


on powerControlEveryWiFiDevices(aFlag as boolean)
	set allNames to CWInterface's interfaceNames()'s allObjects() as list
	if allNames = {} then return false
	set powerList to {}
	repeat with i in allNames
		set j to contents of i
		set aInterface to (CWInterface's interfaceWithName:j)
		set wRes to (aInterface's setPower:aFlag |error|:(missing value))
		-- Get Power state and check it
		set aPower to (aInterface's powerOn()) as boolean
		if aPower = (not aFlag) then
			if aFlag = true then
				set aStat to " on "
			else
				set aStat to " off "
			end if
			display notification "Error occured in power" & aStat & "an Wifi deviece ( " & j & " )...."
		end if
		set the end of powerList to aPower
	end repeat
	return ({aFlag} is in powerList)
end powerControlEveryWiFiDevices

Thanks for your help KniazidisR! :slight_smile:

Your first script works fine! I changed some parts (macOS in German) and created a shortcut with Automator (service) to run it system-wide.

macOS in German:


tell application "System Events" to tell process "SystemUIServer"	
	
	-- Wi-Fi old state
	try
		set wifi to (first menu bar item whose value of attribute "AXDescription" contains "nicht aktiviert") of menu bar 1
		set wifi_IsOn to false
	on error
		set wifi to (first menu bar item whose value of attribute "AXDescription" contains "WLAN") of menu bar 1
		set wifi_IsOn to true
	end try
	
	-- click Wi-Fi menu item
	tell wifi
		click it
		if wifi_IsOn then
			click menu item "WLAN deaktivieren" of menu 1
			set bluetoothDescription to "Bluetooth deaktivieren"
		else
			click menu item "WLAN aktivieren" of menu 1
			set bluetoothDescription to "Bluetooth aktivieren"
		end if
	end tell
	
	-- Bluetooth
	set bt to (first menu bar item whose value of attribute "AXDescription" contains "bluetooth") of menu bar 1
	click bt
	click (first menu item whose title is bluetoothDescription) of menu of bt	
end tell

It works well under macOS Catalina but unfortunately it doesn’t under Big Sur (new control center) :frowning:

Your second script just works if I turn on/off bluetooth manually and it changes wi-fi (on/off), as well…

I’m trying to figure out how to change your first script so that it works under Catalina & Big Sur. But I don’t get it together :confused:

I haven’t switched to Big Sur yet, so I can’t help with its GUI scripting. But here’s a version of my first script that uses as little GUI scripting as possible - only the Bluetooth part. It will be easier to tweak for Big Sur:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "CoreWLAN"
use scripting additions
property CWInterface : a reference to current application's CWInterface

-- Wi_Fi part. No need GUI scripting
if (do shell script "networksetup -getinfo Wi-Fi | grep Router") begins with "IPv6 Router: none" then
	powerControlEveryWiFiDevices(true) of me -- set WiFi on
	set bluetoothDescription to "Turn Bluetooth On"
else
	powerControlEveryWiFiDevices(false) of me -- set WiFi off
	set bluetoothDescription to "Turn Bluetooth Off"
end if

-- Bluetooth part. Uses GUI scripting.
tell application "System Events" to tell process "SystemUIServer" to tell menu bar 1
	tell (first menu bar item whose value of attribute "AXDescription" contains "bluetooth")
		click it
		click (first menu item whose title is bluetoothDescription) of menu 1
	end tell
end tell

-- Wi_Fi handler. No need GUI scripting 
on powerControlEveryWiFiDevices(aFlag as boolean)
	set allNames to CWInterface's interfaceNames()'s allObjects() as list
	if allNames = {} then return false
	set powerList to {}
	repeat with i in allNames
		set j to contents of i
		set aInterface to (CWInterface's interfaceWithName:j)
		set wRes to (aInterface's setPower:aFlag |error|:(missing value))
		-- Get Power state and check it
		set aPower to (aInterface's powerOn()) as boolean
		if aPower = (not aFlag) then
			if aFlag = true then
				set aStat to " on "
			else
				set aStat to " off "
			end if
			display notification "Error occured in power" & aStat & "an Wifi deviece ( " & j & " )...."
		end if
		set the end of powerList to aPower
	end repeat
	return ({aFlag} is in powerList)
end powerControlEveryWiFiDevices

Thanks KniazidisR & Fredrik71! :slight_smile:

With your help I wrote this scrip!


set status_wifi to do shell script "networksetup -getairportpower en0" -- get WiFi-status (Off or On)
set status_bt to do shell script "/usr/local/bin/blueutil --power" -- get Bluetooth-status (0 or 1)*

if status_wifi ends with "On" or status_bt ends with "1" then
	do shell script "networksetup -setairportpower en1 off" -- set WiFi off
	do shell script "/usr/local/bin/blueutil --power 0" -- set Bluetooth off*
else
	do shell script "networksetup -setairportpower en1 on" -- set WiFi on
	do shell script "/usr/local/bin/blueutil --power 1" -- set Bluetooth off*
end if

(*
	      * you need to download blueutil (https://github.com/toy/blueutil)
		and install it with Homebrew (https://brew.sh)
		then you can run it in Terminal (or as "do shell script" in AppleScript)
*)

Unfortunately I didn’t find a way to use a terminal command for Bluetooth…
Therefore I installed Blueutil so that there is no need (for me) to use GUI scripting.
The script works in Catalina & Big Sur if Blueutil (https://github.com/toy/blueutil) is installed on your Mac.

Maybe it’s not the most elegant way but it works for me.
Thx :slight_smile:

Of course, this is a matter of taste, but I find the method using SystemUIServer more elegant than opening the System Preferences window or installing a third-party utility. In any case, a wide choice of the 3 methods proposed here will not hurt anyone.