source: trunk/LocationDo/LocationDo.py @ 678

Last change on this file since 678 was 213, checked in by Nicholas Riley, 14 years ago

LocationDo?.py: Another place in Siebel.

action.py: ensureKerberosPrincipalsValid now understands both
"Expired" and "Not Valid" as requiring renewal. I bet these get
localized, too...

File size: 10.6 KB
Line 
1#!/usr/bin/pythonw
2# -*- coding: utf-8 -*-
3
4from AppKit import NSApplication, NSBeep, NSSystemDefined, NSURL, NSWorkspace
5from Foundation import NSDistributedNotificationCenter, NSLog
6from PyObjCTools import AppHelper
7from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
8from SystemConfiguration import *
9from appscript import app
10import re
11import sets
12
13import action
14
15GROWL_APP_NAME = 'LocationDo'
16NOTIFICATION_LOCATION = 'Location'
17NOTIFICATION_ARRIVE = 'Arrive'
18NOTIFICATION_DEPART = 'Depart'
19NOTIFICATION_CURRENT = 'Current location'
20NOTIFICATION_UNKNOWN = 'Unknown location'
21NOTIFICATION_READY = 'Ready'
22NOTIFICATIONS_ALL = [NOTIFICATION_LOCATION, NOTIFICATION_ARRIVE,
23                     NOTIFICATION_DEPART, NOTIFICATION_CURRENT,
24                     NOTIFICATION_UNKNOWN, NOTIFICATION_READY]
25
26DEBUG = True
27
28kEventHotKeyPressedSubtype = 6
29kEventHotKeyReleasedSubtype = 9
30
31RE_AIRPORT_STATUS = '/'.join([kSCDynamicStoreDomainState, kSCCompNetwork, kSCCompInterface, kSCCompAnyRegex, kSCEntNetAirPort])
32
33# XXX switch to Cocoa interface for Growl
34growl = app('GrowlHelperApp')
35
36growl.register(
37    as_application=GROWL_APP_NAME,
38    all_notifications=NOTIFICATIONS_ALL,
39    default_notifications=NOTIFICATIONS_ALL)
40
41def growlNotify(name, title, description, **kw):
42    params = dict(with_name=name,
43                  title=title,
44                  description=unicode(description),
45                  application_name=GROWL_APP_NAME,
46                  image_from_location='/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericNetworkIcon.icns')
47    params.update(kw)
48    if not params.get('sticky', False):
49        params['identifier'] = name
50    if DEBUG:
51        NSLog("%s: %s" % (title, description))
52    growl.notify(**params)
53
54class Location(sets.Set):
55    def __str__(self):
56        if not self: return '(unknown)'
57        return ', '.join(self)
58
59currentLocation = Location()
60
61# XXX attach actions to location tag; something like:
62# locations['Siebel'].onArrive.ensureKerberosPrincipalsValid(...)
63
64def arriveAtLocation(location):
65    growlNotify(NOTIFICATION_ARRIVE, 'Arriving at location', location)
66    if 'Siebel' in location:
67        action.ensureKerberosPrincipalsValid(['njriley@ACM.UIUC.EDU',
68                                              'njriley@AD.UIUC.EDU'])
69        action.setVolumePercent(0.15)
70    if '1115 SC' in location:
71        action.openInBackground('~/Documents/CS 423/CS 423 to do.oo3')
72        action.setDisplayBrightnessPercent(0.2)
73        action.setAdiumStatus('Working in 1115 SC')
74    if '1404 SC' in location:
75        action.setDisplayBrightnessPercent(0.4)
76        action.setVolumePercent(0)
77        action.setAdiumStatus('In 1404 SC')
78    if '4111 SC' in location:
79        action.openInBackground('~/Documents/Research/Research progress.oo3')
80        # XXX complain that NX has no creator nor bundle ID
81        action.openApplicationWithName('NX Client for OSX')
82        # XXX make these changes dependent on screen resolution
83        action.moveWindow('iTunes', 'iTunes', (2123, 1534), ifSizeMatches=(129, 65))
84        action.moveWindow('Adium', 'Contacts', (1280, 1553))
85        action.moveWindow('Adium', 'AOL System Msg', (1448, 1320))
86        # XXX need better way to do this
87        action.moveWindow('OmniOutliner Pro', 'Research progress', (1634, 847))
88        action.moveWindow('OmniOutliner Pro', '◇ Research progress', (1634, 847))
89        # XXX wait for yt to be accessible and Kerberos, then open the terminal window
90        # action.terminalDo('kswitch -p njriley@ACM.UIUC.EDU;(sleep 10;kswitch -p njriley@AD.UIUC.EDU)&! ;clear;ssh yt')
91        action.setDisplayBrightnessPercent(0.9)
92        action.setAdiumStatus('Working in 4111 SC')
93    if 'ACM office' in location:
94        action.setDisplayBrightnessPercent(0.2)
95        action.setVolumePercent(0.5)
96        action.setAdiumStatus('At ACM')
97    if 'Champaign' in location:
98        action.ensureKerberosPrincipalsValid(['njriley@ACM.UIUC.EDU',
99                                              'njriley@AD.UIUC.EDU'])
100        action.openInBackground('~/Documents/To do.oo3')
101        action.setDisplayBrightnessPercent(0.2)
102        action.setVolumePercent(0.5)
103        action.setAdiumStatus('At home')
104    if '13[12]0 DCL' in location:
105        action.setDisplayBrightnessPercent(0.2)
106        action.setVolumePercent(0)
107        action.setAdiumStatus('In 13[12]0 DCL')
108    if 'UIUCnet' in location:
109        action.startVPNC('UIUCnet-oncampus')
110    if 'CSL' in location:
111        action.setDisplayBrightnessPercent(0.1)
112        action.setVolumePercent(0)
113        action.setAdiumStatus('At CSL')
114        action.openURL('http://wireless.csl.uiuc.edu/authenticate.html')
115    growlNotify(NOTIFICATION_ARRIVE, 'Arrived at location',
116                location)
117
118def departLocation(location):
119    growlNotify(NOTIFICATION_DEPART, 'Departing location', location)
120    if 'UIUCnet' in location:
121        # XXX should handle this more symmetrically
122        action.stopVPNC()
123    growlNotify(NOTIFICATION_DEPART, 'Departed location', location)
124
125def locationChanged(location):
126    if not location: return # unknown
127    global currentLocation
128    oldLocation = currentLocation - location
129    newLocation = location - currentLocation
130    if DEBUG:
131        NSLog("new %s | was %s | departing %s | arriving %s" % \
132              (location, currentLocation, oldLocation, newLocation))
133    if oldLocation:
134        departLocation(oldLocation)
135        currentLocation = location
136    if newLocation:
137        currentLocation = location
138        arriveAtLocation(newLocation)
139
140def formatMACAddress(data):
141    return ':'.join(re.findall('..', data.bytes()[:].encode('hex')))
142
143# XXX watch battery status, too, for display brightness
144# XXX look at display configuration to tell difference between 4111 and 4124
145# XXX need concept of ambiguity, ask user which location they're in (ACM/1115)
146
147airPortStatus = {'BSSID': None, 'SSID': None}
148
149class SCWatcher(NSObject):
150    def keysChanged_inDynamicStore_(self, changed, store):
151        location = Location()
152        for key in changed:
153            if re.match(RE_AIRPORT_STATUS, key):
154                st = store.valueForKey_(key)
155                bssid = formatMACAddress(st['BSSID'])
156                if airPortStatus['BSSID'] != bssid:
157                    airPortStatus['BSSID'] = bssid
158                    if bssid in ['00:0e:83:05:77:22', '00:0e:83:05:77:20',
159                                 '00:0e:83:05:75:d2']:
160                        location.add('4111 SC')
161                    elif bssid in ['00:0e:83:05:75:a2', '00:0e:83:05:75:a0']:
162                        location.add('1404 SC')
163                    elif bssid in ['00:0e:83:05:76:82', '00:0e:83:05:76:80']:
164                        location.add('ACM office')
165                    elif bssid == '00:11:24:0f:23:69':
166                        location.add('Champaign')
167                    elif bssid == '00:13:c4:ce:66:a0':
168                        location.add('13[12]0 DCL')
169                    elif bssid in ['00:0e:83:05:78:02', '00:0e:83:05:77:42']:
170                        location.add('1304 SC')
171                    elif bssid == '00:0e:83:05:78:a2':
172                        location.add('3124 SC')
173                    elif bssid == '00:0e:83:05:78:92':
174                        location.add('2405 SC')
175                    elif bssid == '00:0e:83:05:76:b2':
176                        location.add('Siebel 2nd floor atrium')
177                    elif bssid in ['00:14:1c:ad:5c:80', '00:14:1c:ad:64:50']:
178                        location.add('MEB')
179                    elif bssid == '00:02:2d:2c:cb:34':
180                        location.add('B02 CSL')
181                    elif bssid == '00:11:5c:fe:31:00':
182                        location.add('CAB')
183                    elif bssid == '00:13:c4:78:e1:a0':
184                        location.add('Engineering Hall')
185                    elif bssid in ['00:0c:30:d4:b7:2d', '00:0c:30:d4:b6:70']:
186                        location.add('Illini Union')
187                    elif bssid == '00:0d:93:ed:e0:bc':
188                        location.add('340 Marlborough St.')
189                    elif bssid == '00:40:96:5a:5e:0f':
190                        location.add('BMI baggage claim')
191                    elif bssid == '00:12:0e:10:1f:83':
192                        location.add('Waterville Valley')
193                    elif bssid == '44:44:44:44:44:44':
194                        location.add('No AirPort network')
195                    else:
196                        growlNotify(NOTIFICATION_UNKNOWN, 'Unknown BSSID', bssid,
197                                    sticky=True)
198                        NSLog('Unknown BSSID %s' % bssid)
199                ssid = st['SSID']
200                if airPortStatus['SSID'] != ssid:
201                    airPortStatus['SSID'] = ssid
202                    if ssid == '':
203                        pass
204                    elif ssid == 'dcs-wpa':
205                        location.add('Siebel')
206                    elif ssid == 'DCSnet':
207                        location.add('Siebel')
208                        location.add('UIUCnet')
209                    elif ssid == 'UIUCnet':
210                        location.add('UIUCnet')
211                    elif ssid == 'ACME Acres':
212                        location.add('Champaign')
213                    elif ssid == 'CSL Wireless':
214                        location.add('CSL')
215                    elif ssid == 'Wackyland':
216                        location.add('340 Marlborough St.')
217                    elif ssid == '05B408706826':
218                        location.add('Waterville Valley')
219                    elif ssid == 'CIRA':
220                        location.add('BMI')
221                    else:
222                        growlNotify(NOTIFICATION_UNKNOWN, 'Unknown SSID', ssid,
223                                    sticky=True)
224                locationChanged(location)
225
226class LocationDo(NSApplication):
227
228    hotKeyRef = None
229
230    def displayLocation(self):
231        growlNotify(NOTIFICATION_CURRENT, 'Current location', currentLocation)
232
233    def finishLaunching(self):
234        super(LocationDo, self).finishLaunching()
235        hotKeyRef = RegisterEventHotKey(98, 0, (0, 0),
236                                        GetApplicationEventTarget(), 0) # F7
237        growlNotify(NOTIFICATION_READY, 'LocationDo ready', '')
238
239    def sendEvent_(self, theEvent):
240        if theEvent.type() == NSSystemDefined and \
241               theEvent.subtype() == kEventHotKeyPressedSubtype:
242            self.displayLocation()
243        super(LocationDo, self).sendEvent_(theEvent)
244
245store = None
246
247if __name__ == "__main__":
248    store = UNSystemConfigurationDynamicStore.alloc().initWithName_('LocationDo')
249    watcher = SCWatcher.alloc().init()
250    store.setDelegate_(watcher)
251    store.notifyValuesForKeys_matchingPatterns_(None, [RE_AIRPORT_STATUS])
252    store.addToCurrentRunLoop()
253    AppHelper.runEventLoop()
Note: See TracBrowser for help on using the repository browser.