Skip to content

Instantly share code, notes, and snippets.

@zakes-it
Last active May 18, 2020 15:32
Show Gist options
  • Save zakes-it/bedbaa2a782ffb9bd97454b281b7262d to your computer and use it in GitHub Desktop.
Save zakes-it/bedbaa2a782ffb9bd97454b281b7262d to your computer and use it in GitHub Desktop.
Use with crankd turn off AirPort when on an Ethernet connection and turn AirPort back on when Ethernet disconnects
#! /usr/bin/python
import syslog
import objc
from Foundation import NSAppleScript, CFPreferencesCopyAppValue,\
CFPreferencesSetAppValue, CFPreferencesAppSynchronize
from SystemConfiguration import SCDynamicStoreCreate, SCDynamicStoreCopyValue
BUNDLE_ID = 'AirportKiller'
bundle_path = '/System/Library/Frameworks/CoreWLAN.framework'
objc.loadBundle('CoreWLAN', bundle_path=bundle_path, module_globals=globals())
iface = CWInterface.interface()
"""
Turn off wifi:
iface.setPower_error_(False, None)
Turn on wifi:
iface.setPower_error_(True, None)
"""
class LogHelper(object):
def __init__(self, consoleOn=False, guiOn=False):
self.console = consoleOn
self.gui = guiOn
self.msg = "AirportKiller: Network change detected"
def add(self, txt):
self.msg = self.msg + "\n" + txt
def submit(self):
if self.gui:
self.aScriptNotify()
if self.console:
self.consoleLog()
def consoleLog(self):
syslog.syslog(syslog.LOG_ALERT, self.msg)
def aScriptNotify(self):
script = '''tell application "System Events"
activate
display dialog "{}" with title "AirPort Killer" buttons {{"OK"}}
end tell
'''.format(self.msg)
s = NSAppleScript.alloc().initWithSource_(script)
_ = s.executeAndReturnError_(None)
def dumpState(state):
for key, val in state.items():
CFPreferencesSetAppValue(key, val, BUNDLE_ID)
CFPreferencesAppSynchronize(BUNDLE_ID)
def loadState():
state = {}
for key in ('AirportPower', 'AirportActive', 'EthernetActive'):
state[key] = CFPreferencesCopyAppValue(key, BUNDLE_ID)
return state
def getState():
net_config = SCDynamicStoreCreate(None, "net", None, None)
services = {}
key = "Setup:/Network/Global/IPv4"
service_uuids = SCDynamicStoreCopyValue(net_config, key)["ServiceOrder"]
for uuid in service_uuids:
key = "Setup:/Network/Service/{}/Interface".format(uuid)
service = SCDynamicStoreCopyValue(net_config, key)
if 'DeviceName' in service:
services[service['DeviceName']] = service
key = "State:/Network/Interface"
interfaces = SCDynamicStoreCopyValue(net_config, key)['Interfaces']
ether = {}
for i in interfaces:
# ignore devices not listed in network services
if i not in services:
continue
# ignore bluetooth interfaces
if i.startswith('awdl') or i.startswith('llw'):
continue
# ignore hidden interfaces like iPhone/iPad USB
if services[i].get('HiddenConfiguration', False):
continue
key = "State:/Network/Interface/{}/Link".format(i)
state = SCDynamicStoreCopyValue(net_config, key)
key = "State:/Network/Interface/{}/AirPort".format(i)
airport = SCDynamicStoreCopyValue(net_config, key)
if airport:
currentState = {'AirportPower': airport['Power Status'],
'AirportActive': state['Active']}
continue
if state:
ether[i] = state['Active']
currentState['EthernetActive'] = any(ether.values())
return currentState
syslog.openlog(ident="AirportKiller", facility=syslog.LOG_DAEMON)
log = LogHelper(consoleOn=True, guiOn=False)
currentState = getState()
log.add("Current network state: {}".format(currentState))
try:
previousState = loadState()
except:
previousState = None
if previousState:
log.add("Previous network state: {}".format(previousState))
else:
log.add("Previous network state unknown.")
if currentState['EthernetActive'] and currentState['AirportActive']:
log.add("System docked, previous network state unknown. Killing"
" Airport...")
iface.setPower_error_(False, None)
dumpState(getState())
log.submit()
quit()
if (previousState['EthernetActive'] and not currentState['EthernetActive'] and
not currentState['AirportActive']):
log.add("System undocked with AirPort off. Resurrecting AirPort...")
iface.setPower_error_(True, None)
elif (not previousState['EthernetActive'] and currentState['EthernetActive']
and currentState['AirportActive']):
log.add("System docked with AirPort on. Killing AirPort...")
iface.setPower_error_(False, None)
else:
log.add("No actionable network changes.")
dumpState(getState())
log.submit()
<?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>SystemConfiguration</key>
<dict>
<key>State:/Network/Global/IPv4</key>
<dict>
<key>command</key>
<string>/usr/bin/python '/usr/local/myorg/AirportKiller.py'</string>
</dict>
</dict>
</dict>
</plist>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment