+
+#
+# launcher plugin for watching for new sms messages.
+#
+# We watch /var/run/gsm-state/newsms and if that changes,
+# run "gsm-getsms -n"
+# When that completes, or after a timer, load new messages
+# and display from/time
+# Allow 'run sendsms -n'
+
+import dnotify
+from subprocess import Popen
+from storesms import SMSstore
+import gobject
+import sys,os,fcntl
+import re, time
+
+global gsmstate
+gsmstate = {};
+#
+# lastxt : time stamp of mos recent text to arrive, so we know if
+# current new text is actually new.
+gsmstate['lastxt'] = 0
+# watchsms: watch on 'newsms' file
+# watchmsg: watch on 'newmesg' file
+# watchcall: watch on 'incoming' file
+# store: the sms store
+# value: BUSY / name of incoming /
+# oncall: Boolean if making or receiving call
+gsmstate['oncall'] = False
+# tonestr: last string from which we had touch-tone chars
+# tonesent: list of touch-tone chars sent
+# channel: channel to GSMd for make/answer call. Holds 'call' object
+# book: phone book
+# newname
+# fullname
+# num
+# from
+# suspend-lock
+# buzz
+gsmstate['carriers'] = []
+gsmstate['carriers_valid'] = 0
+gsmstate['searching'] = False
+
+def suspend_lock():
+ try:
+ f = open("/var/run/suspend_disabled", "w+")
+ except:
+ f = None
+ if f:
+ try:
+ fcntl.lockf(f, fcntl.LOCK_SH | fcntl.LOCK_NB)
+ except:
+ f.close()
+ f = None
+ return f
+def suspend_unlock(f):
+ if f:
+ fcntl.lockf(f, fcntl.LOCK_UN)
+ f.close()
+ return None
+
+
+def sysfs_write(file, val):
+ try:
+ f = open(file, "w")
+ f.write(val)
+ f.close()
+ except:
+ pass
+
+def buzz_in(msec):
+ return gobject.timeout_add(msec, buzz_on)
+def buzz_off(ev):
+ if (ev):
+ gobject.source_remove(ev)
+ return None
+
+def buzz_on():
+ vib = "/sys/class/leds/neo1973:vibrator"
+ sysfs_write(vib+"/trigger", "none")
+ sysfs_write(vib+"/trigger", "timer")
+ sysfs_write(vib+"/delay_on", "50")
+ sysfs_write(vib+"/delay_off", "100")
+ vib_timer = gobject.timeout_add(1000, buzz_cancel)
+ return False
+def buzz_cancel():
+ vib = "/sys/class/leds/neo1973:vibrator"
+ sysfs_write(vib+"/trigger", "none")
+ return False
+
+
+def newmesg(cmd, obj):
+ global gsmstate
+ if len(obj.state) == 0:
+ init(obj)
+ if cmd == '_name':
+ return ('cmd', gsmstate['name'])
+ if cmd == '_options':
+ return obj.state['cmd'].options()
+ return obj.state['cmd'].event(cmd)
+
+def init(obj):
+ global gsmstate
+ obj.state['cmd'] = sys.modules['__main__'].CmdTask(',/usr/local/bin/sendsms -n,SendSMS')
+ gsmstate['name'] = '-'
+ d = dnotify.dir('/var/run/gsm-state')
+ w = d.watch('newsms', lambda f : got_sms(obj))
+ gsmstate['watchsms'] = w
+ for p in ['/media/card','/media/disk','/var/tmp']:
+ if os.path.exists(p):
+ pth = p+"/SMS"
+ break
+ d = dnotify.dir(pth)
+ w = d.watch('newmesg', lambda f : load_new(obj))
+ gsmstate['watchmsg'] = w
+ s = SMSstore(pth)
+ gsmstate['store'] = s
+ load_new(obj)
+
+def wrap(txt):
+ ln = 0
+ n = ''
+ w = ''
+ for l in txt:
+ if l != ' ' and l != '\n':
+ # still in word
+ w += l
+ continue
+ if ln + len(w) < 22:
+ # this word fits
+ if ln:
+ n += ' ' + w
+ else:
+ n += w
+ ln = 1
+ ln += len(w)
+ w = ''
+ continue
+ if ln == 0:
+ # line otherwise empty
+ n += w + '\n'
+ w = ''
+ continue
+ n += '\n' + w;
+ ln = len(w) + 1
+ w = ''
+ if w:
+ if ln:
+ n += ' ' + w
+ else:
+ n += w
+ return n
+
+def protect(txt):
+ txt = txt.replace('&', '&')
+ txt = txt.replace('<', '<')
+ txt = txt.replace('>', '>')
+ return txt
+
+def get_book():
+ global gsmstate
+ if not 'book' in gsmstate:
+ gsmstate['book'] = load_book("/media/card/address-book")
+ gsmstate['bookstamp'] = time.time()
+ return gsmstate['book']
+
+def load_new(obj):
+ global gsmstate
+ s = gsmstate['store']
+ (next, l) = s.lookup(None, 'NEW')
+ if len(l) == 0:
+ if gsmstate['name'] != '-':
+ obj.refresh(False)
+ gsmstate['name'] = '-'
+ gsmstate['lastxt'] = 0
+ return
+ cor = book_name(get_book(), l[0].correspondent)
+ if not cor:
+ cor = l[0].correspondent
+ else:
+ cor = cor[0]
+ txt = cor
+ if len(l) > 1:
+ txt += " (+%d)"% (len(l)-1)
+ txt += ': ' + l[0].text
+ txt = wrap(txt)
+ txt = protect(txt)
+ gsmstate['name'] = '<span size="10000">'+txt+'</span>'
+
+ wake = (l[0].stamp > gsmstate['lastxt'])
+ if wake:
+ gsmstate['lastxt'] = l[0].stamp
+ obj.refresh(wake)
+ return False
+
+def got_sms(obj):
+ Popen('gsm-getsms -n', shell=True, close_fds = True)
+ #the watcher does this.. gobject.timeout_add(1000, lambda *a : (load_new(obj)))
+ try:
+ f = open("/var/run/alert/sms", "w")
+ f.write("new")
+ f.close()
+ except:
+ pass
+
+##
+## incoming and outgoing calls
+##
+
+import atchan
+
+class Phone(atchan.AtChannel):
+ def __init__(self, refresh):
+ self.refresh = refresh
+ self.action = ''
+ atchan.AtChannel.__init__(self, master = False)
+ self.atconnect()
+
+ def power_done(self, line = None):
+ if line == "OK":
+ self.do_cmd()
+
+ def do_cmd(self):
+ if self.action == 'disconnect':
+ self.disconnect()
+ self.action = ''
+ if self.action == 'answer':
+ self.atcmd('%N0187;A')
+ self.action = ''
+ if self.action[0:5] == 'call:':
+ self.atcmd('%N0187;D'+self.action[5:]+';')
+ self.action = ''
+ if self.action == 'hangup':
+ self.atcmd('H')
+ self.action = 'disconnect'
+ if self.action[0:5] == 'tone:':
+ self.atcmd('+VTS='+self.action[5:])
+ self.action = ''
+ if self.action == 'rq-clear':
+ self.atcmd('+CUSD=0;H')
+ self.action = ''
+ if self.action[0:3] == 'rq ':
+ pre = ''
+ if self.action[3] == 'not0':
+ pre = "+CUSD=0;H;"
+ self.atcmd(pre + '+CUSD=' + self.action[3:])
+ self.action = ''
+
+ if self.action == 'search':
+ self.atcmd('+COPS=?', timeout = 40000)
+ self.action = ''
+
+ if self.action[0:7] == 'select:':
+ c = self.action[7:]
+ self.atcmd('+COPS=4,2,"%s"'%c, timeout=15000)
+ self.action = ''
+
+
+ def takeline(self, line):
+ global gsmstate
+
+ if line == 'AT-Command Interpreter ready':
+ return False
+
+ gsmstate['searching'] = False
+ if line == 'OK':
+ if self.pending:
+ self.pending = False
+ gobject.source_remove(self.timer)
+ self.timer = None
+ self.do_cmd()
+ return False
+
+ if line == 'BUSY':
+ gsmstate['value'] = 'BUSY'
+ self.refresh()
+ if line == 'NO CARRIER':
+ if gsmstate['oncall']:
+ reject()
+
+ if line[0:7] == "+CUSD: ":
+ m = re.match('^\+CUSD: ([012]),"(.*)"(,[0-9]+)?$', line)
+ if m:
+ if m.group(1) == '0':
+ gsmstate['request'] = 3
+ else:
+ gsmstate['request'] = 2
+ gsmstate['rqmsg'] = m.group(2)
+ self.refresh()
+
+ if line[0:7] == "+COPS: ":
+ gsmstate['carriers'] = re.findall('\((\d+),"([^"]*)","([^"]*)","([^"]*)"\)',line[7:])
+ gsmstate['carriers_valid'] = time.time()
+ self.refresh()
+
+ return False
+
+
+ def act(self, cmd):
+ self.action = cmd
+ if not self.pending and not self.command_pending:
+ self.do_cmd()
+
+
+def load_book(file):
+ try:
+ f = open(file)
+ except:
+ f = open("/home/neilb/home/mobile-numbers-jan-08")
+ rv = []
+ for l in f:
+ x = l.split(';')
+ rv.append([x[0],x[1]])
+ rv.sort(lambda x,y: cmp(x[0],y[0]))
+ return rv
+
+def book_lookup(book, name, num):
+ st=[]; mid=[]
+ for l in book:
+ if name.lower() == l[0][0:len(name)].lower():
+ st.append(l)
+ elif l[0].lower().find(name.lower()) >= 0:
+ mid.append(l)
+ st += mid
+ if len(st) == 0:
+ return [None, None]
+ if num >= len(st):
+ num = -1
+ return st[num]
+
+def book_speed(book, sym):
+ i = book_lookup(book, sym, 0)
+ if i[0] == None or i[0] != sym:
+ return None
+ j = book_lookup(book, i[1], 0)
+ if j[0] == None:
+ return (i[1], i[0])
+ return (j[1], j[0])
+
+def book_name(book, num):
+ if len(num) < 8:
+ return None
+ for ad in book:
+ if len(ad[1]) >= 8 and num[-8:] == ad[1][-8:]:
+ return ad
+ return None
+
+incoming = []
+def incoming_flush(start, end, number):
+ global incoming
+ if start:
+ incoming.append((start, end, number))
+def load_incoming():
+ global incoming
+ incoming = []
+
+ f = open("/var/log/incoming")
+ start = None; end=None; number=None
+ for l in f:
+ w = l.split()
+ if len(w) != 3:
+ continue
+ if w[2] == '-call-':
+ incoming_flush(start, end, number)
+ start = (w[0], w[1])
+ number = None; end = None
+ elif w[2] == '-end-':
+ end = (w[0], w[1])
+ incoming_flush(start, end, number)
+ start = None; end = None; number = None
+ else:
+ number = w[2]
+ if not start:
+ start = (w[0], w[1])
+ f.close()
+
+outgoing = []
+def outgoing_flush(start, end, number):
+ global outgoing
+ if start:
+ outgoing.append((start, end, number))
+def load_outgoing():
+ global outgoing
+ outgoing = []
+
+ f = open("/var/log/outgoing")
+ start = None; end=None; number=None
+ for l in f:
+ w = l.split()
+ if len(w) != 3:
+ continue
+ if w[2] == '-end-':
+ end = (w[0], w[1])
+ outgoing_flush(start, end, number)
+ start = None; end = None; number = None
+ else:
+ outgoing_flush(start, end, number)
+ start = (w[0], w[1])
+ number = w[2]
+ f.close()
+
+incoming_map = []
+def incoming_lookup(num):
+ global incoming, incoming_map
+ if num == 1:
+ load_incoming()
+ ln = 0
+ a = {}
+ for start, end, number in incoming:
+ if number == None:
+ continue
+ ln += 1
+ a[number] = ln
+ b = {}
+ for x in a:
+ b[a[x]] = x
+ b = b.keys()
+ k.sort()
+ incoming_map = []
+ for n in k:
+ incoming_map.append(b[n])
+ if num > len(incoming_map):
+ num = len(incoming_map)
+ return ["Caller %d" %num, incoming_map[-num]]
+
+def name_lookup(book, str):
+ # We need to report
+ # - a number - to dial
+ # - optionally a name that is associated with that number
+ # - optionally a new name to save the number as
+ # The name is normally alpha, but can be a single digit for
+ # speed-dial
+ # Dots following a name allow us to stop through multiple matches.
+ # So input can be:
+ # A single symbol.
+ # This is a speed dial. It maps to name, then number
+ # A string of >1 digits
+ # This is a literal number, we look up name if we can
+ # A string of dots
+ # This is a look up against recent incoming calls
+ # We look up name in phone book
+ # A string starting with alpha, possibly ending with dots
+ # This is a regular lookup in the phone book
+ # A number followed by a string
+ # This provides the string as a new name for saving
+ # A string of dots followed by a string
+ # This also provides the string as a newname
+ # An alpha string, with dots, followed by '+'then a single symbol
+ # This saves the match as a speed dial
+ #
+ # We return a triple of (number,oldname,newname)
+ if re.match('^[A-Za-z0-9]$', str):
+ # Speed dial lookup
+ s = book_speed(book, str)
+ print "speed", str, s
+ if s:
+ return (s[0], s[1], None)
+ return None
+ m = re.match('^(\+?\d+|[*#][*#0-9]*)([A-Za-z][A-Za-z0-9 ]*)?$', str)
+ if m:
+ # Number and possible newname
+ s = book_name(book, m.group(1))
+ if s:
+ return (m.group(1), s[0], m.group(2))
+ else:
+ return (m.group(1), None, m.group(2))
+ m = re.match('^(\.+)([A-Za-z][A-Za-z0-9 ]*)?$', str)
+ if m:
+ # dots and possible newname
+ i = incoming_lookup(len(m.group(1)))
+ s = book_name(book, i[1])
+ if s:
+ return (i[1], s[0], m.group(2))
+ else:
+ return (i[1], i[0], m.group(2))
+ m = re.match('^([A-Za-z][A-Za-z0-9 ]*)(\.*)(\+[A-Za-z0-9])?$', str)
+ if m:
+ # name and dots
+ speed = None
+ if m.group(3):
+ speed = m.group(3)[1]
+ i = book_lookup(book, m.group(1), len(m.group(2)))
+ if i[0]:
+ return (i[1], i[0], speed)
+ return None
+
+def encode(str):
+ return re.sub("&", "&", str)
+
+def incoming(cmd, obj):
+ global gsmstate
+
+ if len(obj.state) == 0:
+ init_incoming(obj)
+ gsmstate['obj'] = obj
+ havename = False; havenum = False
+ if gsmstate['oncall']:
+ # any new characters get sent via GSM if valid
+ ts = gsmstate['tonestr']
+ if obj.current_input[0:len(ts)] != ts:
+ # something has changed, reset
+ ts = obj.current_input
+
+ xtra = obj.current_input[len(ts):]
+ for c in xtra:
+ if c in "0123456789#*":
+ gsmstate['channel'].act('tone:' + c)
+ gsmstate['tonesent'] += c
+ gsmstate['tonestr'] = obj.current_input
+
+ x = name_lookup(gsmstate['book'], obj.current_input)
+ if x:
+ havenum = x[0]
+ havename= x[1]
+ gsmstate['newname'] = x[2]
+ gsmstate['fullname'] = havename
+ gsmstate['num'] = havenum
+ else:
+ havenum = False
+
+ if cmd == '_name':
+ if 'from' in gsmstate:
+ v = gsmstate['from']
+ elif 'value' in gsmstate:
+ v = gsmstate['value']
+ else:
+ v = False
+ if gsmstate['oncall'] and gsmstate['tonesent']:
+ v = 'Sent:' + gsmstate['tonesent']
+ if not v and gsmstate['request'] > 0:
+ if gsmstate['request'] == 1:
+ v = "... awaiting response ..."
+ else:
+ v = protect(wrap(gsmstate['rqmsg']))
+ if not v and havenum:
+ if gsmstate['newname']:
+ v = 'Save: ' + encode(gsmstate['newname'])
+ elif havename:
+ v = 'Call: ' + encode(havename)
+ else:
+ v = 'Call: ' + encode(havenum)
+ print 'now v = ', v
+ if not v and gsmstate['lastcaller']:
+ v = '(' + gsmstate['lastcaller'] + ')'
+ return ('cmd', v)
+ if cmd == '_options':
+ if gsmstate['oncall']:
+ o = ["Mute", "Hang Up"]
+ elif gsmstate['value']:
+ o = ["Answer", "Reject"]
+ elif gsmstate['request'] > 0:
+ if gsmstate['request'] == 2:
+ o = ["Quit", "Send Answer (%s)" % havenum]
+ else:
+ o = ["Quit"]
+ elif havenum:
+ if gsmstate['newname']:
+ o = ['Save ' + havenum ]
+ elif re.match('^[*#][*#0-9]*#$', havenum):
+ o = ["Request " + havenum ]
+ else:
+ o = ["Call " + havenum ]
+ elif gsmstate['lastcallnum']:
+ o = [ "Call-back", "Discard" ]
+ else:
+ o = ['Speed\nDial', 'Received\ncalls', 'Dialed\nNumbers']
+ return o
+ if gsmstate['oncall']:
+ if cmd == 0:
+ # Mute
+ return
+ if cmd == 1:
+ # Hang up
+ reject()
+ return
+ elif gsmstate['value']:
+ # incoming
+ gsmstate['lastcaller'] = ''
+ gsmstate['lastcallnum'] = ''
+ if cmd == 0:
+ answer(obj)
+ elif cmd == 1:
+ reject()
+ elif gsmstate['request'] > 0:
+ if cmd == 0:
+ # quit - abort and forget
+ gsmstate['request'] = 0
+ gsmstate['rqmsg'] = ''
+ if 'channel' in gsmstate:
+ chan = gsmstate['channel']
+ if chan:
+ chan.act('rq-clear')
+ elif cmd == 1:
+ # send update message
+ if 'channel' in gsmstate:
+ chan = gsmstate['channel']
+ if chan:
+ chan.act('rq 1,"%s"' % havenum)
+ gsmstate['request'] = 1
+
+ elif havenum and gsmstate['newname']:
+ f = open('/media/card/address-book', 'a')
+ nn = gsmstate['newname']
+ if len(nn) == 1 and havename:
+ # new speed dial
+ f.write(nn + ';' + havename + ';\n')
+ else:
+ f.write(nn + ';' + havenum + ';\n')
+ f.close()
+ gsmstate['book'] = load_book("/media/card/address-book")
+ gsmstate['bookstamp'] = time.time()
+ elif havenum:
+ if cmd == 0:
+ num = gsmstate['num']
+ if re.match('^[*#][*#0-9]*#$', num):
+ place_request(obj, num)
+ else:
+ place_call(num, lambda: obj.refresh(False), obj.current_input)
+ elif gsmstate['lastcallnum']:
+ if cmd == 0:
+ place_call(gsmstate['lastcallnum'],
+ lambda: obj.refresh(False), obj.current_input)
+ elif cmd == 1:
+ gsmstate['lastcaller'] = ''
+ gsmstate['lastcallnum'] = ''
+ obj.refresh(False)
+
+ elif cmd == 0:
+ obj.set_tasks(tasklist_speed_dial(), 0)
+ elif cmd == 1:
+ obj.set_tasks(tasklist_received(True), 0)
+ elif cmd == 2:
+ obj.set_tasks(tasklist_received(False), 0)
+
+
+def init_incoming(obj):
+ global gsmstate
+ print "init incoming"
+ obj.state['cmd'] = None
+ gsmstate['value'] = ''
+ gsmstate['num'] = False
+ gsmstate['name'] = False
+ gsmstate['suspend-lock'] = False
+ gsmstate['buzz'] = False
+ gsmstate['request'] = 0
+ gsmstate['lastcaller'] = ''
+ gsmstate['lastcallnum'] = ''
+ # request values:
+ # 1 == waiting reply
+ # 2 == have intermediate reply
+ # 3 == have final reply
+ gsmstate['rqmsg'] = ''
+ get_book()
+ d = dnotify.dir('/var/run/gsm-state')
+ w = d.watch('incoming', lambda f : got_incoming_later(obj))
+ gsmstate['watchcall'] = w
+
+def got_incoming_later(obj):
+ gobject.idle_add(lambda : got_incoming(obj))
+
+def got_incoming(obj):
+ global gsmstate
+ try:
+ f = open("/var/run/gsm-state/incoming")
+ l = f.readline().strip()
+ f.close()
+ except:
+ l = ""
+ print 'got l=', l
+ if l != gsmstate['value']:
+ obj.refresh(not not l)
+ gsmstate['value'] = l
+ if l:
+ try:
+ f = open("/var/run/alert/ring", "w")
+ f.write("ring")
+ f.close()
+ except:
+ pass
+ if 'channel' not in gsmstate:
+ try:
+ chan = Phone(lambda : obj.refresh(False))
+ gsmstate['channel'] = chan
+ except:
+ gsmstate['channel'] = None
+ else:
+ chan = gsmstate['channel']
+ else:
+ try:
+ os.unlink("/var/run/alert/ring")
+ except:
+ pass
+ reject()
+ fromnum = book_name(gsmstate['book'], l)
+ if fromnum == None:
+ gsmstate['from'] = l
+ else:
+ gsmstate['from'] = fromnum[0]
+ if len(gsmstate['from']) > 1:
+ gsmstate['lastcaller'] = gsmstate['from']
+ gsmstate['lastcallnum'] = l
+ return False
+
+def answer(obj):
+ # Need to
+ # lock against suspend
+ # alsactl -f /root/usr/share/openmoko/scenarios/gsmhandset.state restore
+ # silence 'sound'
+ # send command AT%N0187
+ # send command ATA
+ # set flag that we are on a call ['oncall'] = True
+ # pop up a touch-tone thing
+ # listen for carrier to drop
+ global gsmstate
+ try:
+ os.unlink("/var/run/alert/ring")
+ except:
+ pass
+ gsmstate['suspend-lock'] = suspend_lock()
+ Popen(['alsactl', '-f', '/root/usr/share/openmoko/scenarios/gsmhandset.state',
+ 'restore' ], shell=False, close_fds = True)
+ if 'channel' in gsmstate:
+ chan = gsmstate['channel']
+ if chan:
+ gsmstate['buzz'] = buzz_in(105000)
+ chan.act('answer')
+ gsmstate['oncall'] = True
+ gsmstate['tonestr'] = obj.current_input
+ gsmstate['tonesent'] = ''
+
+def calllog(key, msg):
+ f = open('/var/log/' + key, 'a')
+ now = time.strftime("%Y-%m-%d %H:%M:%S")
+ f.write(now + ' ' + msg + "\n")
+ f.close()
+
+def place_call(num, refresh, input):
+ # Need to
+ # lock against suspend
+ # alsactl -f /root/usr/share/openmoko/scenarios/gsmhandset.state restore
+ # silence 'sound'
+ # send command AT%N0187
+ # send command ATDwhatever;
+ # set flag that we are on a call ['oncall'] = True
+ # pop up a touch-tone thing
+ # listen for carrier to drop
+ global gsmstate
+ gsmstate['suspend-lock'] = suspend_lock()
+ Popen(['alsactl', '-f', '/root/usr/share/openmoko/scenarios/gsmhandset.state',
+ 'restore' ], shell=False, close_fds = True)
+ if 'channel' not in gsmstate:
+ chan = Phone(refresh)
+ gsmstate['channel'] = chan
+
+ gsmstate['buzz'] = buzz_in(105000)
+ chan = gsmstate['channel']
+ gsmstate['oncall'] = True
+ gsmstate['tonestr'] = input
+ gsmstate['tonesent'] = ''
+ chan.act('call:'+ num)
+ calllog('outgoing',num)
+
+def place_request(obj, num):
+ global gsmstate
+ if 'channel' not in gsmstate:
+ chan = Phone(lambda : obj.refresh(False))
+ gsmstate['channel'] = chan
+ chan = gsmstate['channel']
+ gsmstate['request'] = 1
+ chan.act('rq 0,"%s"'% num)
+
+
+
+def reject():
+ # Need to
+ # alsactl -f /root/usr/share/openmoko/scenarios/stereoout.state restore
+ # remove silence
+ # sent ATH
+ # remove touchtone thing
+ global gsmstate
+ if 'channel' in gsmstate:
+ chan = gsmstate['channel']
+ if chan:
+ chan.act('hangup')
+ del gsmstate['channel']
+ Popen(['alsactl', '-f', '/root/usr/share/openmoko/scenarios/stereoout.state',
+ 'restore' ], shell=False, close_fds = True)
+ calllog('outgoing','-end-')
+ gsmstate['oncall'] = False
+ gsmstate['value'] = ''
+ gsmstate['suspend-lock'] = suspend_unlock(gsmstate['suspend-lock'])
+ gsmstate['buzz'] = buzz_off(gsmstate['buzz'])
+
+
+
+def speed(n):
+ return lambda cmd, obj: speed_dial(cmd, obj, n)
+
+def empty(cmd, obj):
+ if cmd == '_name':
+ return ('cmd', '')
+ if cmd == '_options':
+ return []
+
+def speed_dial(cmd, obj, n):
+ global gsmstate
+ if 'stamp' not in obj.state or obj.state['stamp'] != gsmstate['bookstamp']:
+ s = book_speed(get_book(), n)
+ obj.state['num'] = s
+ obj.state['stamp'] = gsmstate['bookstamp']
+ else:
+ s = obj.state['num']
+ if s == None:
+ return empty(cmd, obj)
+ (num, name) = s
+ if cmd == '_name':
+ return ('cmd', n+': '+name)
+ if cmd == '_options':
+ if gsmstate['oncall']:
+ return ['Mute', 'Hang Up']
+ return [ 'Call ' + num]
+ if gsmstate['oncall']:
+ if cmd == 0:
+ # Mute
+ return
+ if cmd == 1:
+ # Hang up
+ reject()
+ return
+ if cmd == 0:
+ place_call(num, lambda: obj.refresh(False), obj.current_input)
+
+
+############################################
+# operator selecting
+# Display current operator (from /var/run/gsm-state/carrier)
+# If we know options, then one button for each carrier.
+# If we don't, then one button for 'search carriers'
+
+def carrier(cmd, obj):
+ global gsmstate
+
+ if len(obj.state) == 0:
+ obj.state['carrier_name'] = ''
+
+ if cmd == '_name' :
+ if gsmstate['searching']:
+ return ('cmd', '(searching)')
+ else:
+ try:
+ f = open("/var/run/gsm-state/carrier")
+ l = f.readline().strip()
+ f.close()
+ except OSError:
+ l = ''
+ if not l:
+ l = '(unknown)'
+ return ('cmd', l)
+
+ if cmd == '_options':
+ if gsmstate['carriers_valid'] + 5*60 < time.time():
+ return [ 'Search Carriers' ]
+ rv = []
+ for v in gsmstate['carriers']:
+ rv.append(v[2])
+ return rv
+
+ if 'channel' not in gsmstate:
+ try:
+ chan = Phone(lambda : obj.refresh(False))
+ gsmstate['channel'] = chan
+ except:
+ gsmstate['channel'] = None
+ else:
+ chan = gsmstate['channel']
+
+ if gsmstate['carriers_valid'] + 5*60 < time.time():
+ chan.act('search')
+ gsmstate['searching'] = True
+ return
+ if cmd >= len(gsmstate['carriers']):
+ return
+ c = gsmstate['carriers'][cmd]
+ chan.act('select:' + c[3])
+
+############################################
+# 'tasklist' for received calls
+class tasklist_received(sys.modules['__main__'].tasklist):
+ def __init__(self, income):
+ sys.modules['__main__'].tasklist.__init__(self)
+ self.incoming = income
+ if income:
+ self.name = 'Received Calls'
+ else:
+ self.name = 'Dialled Numbers'
+
+ def start_refresh(self):
+ global incoming, outgoing
+ if self.incoming:
+ load_incoming()
+ self.list = incoming
+ else:
+ load_outgoing()
+ self.list = outgoing
+
+ def info(self, n):
+ global incoming, gsmstate
+ (start, end, number) = self.list[-1-n]
+ if number == None:
+ number = "-private-number-"
+ else:
+ s = book_name(gsmstate['book'], number)
+ if s != None:
+ number = s[0]
+ return 'cmd', ('<span size="12000">%s</span>\n<span size="10000">%s %s</span>'
+ % (number, start[0], start[1]))
+ def options(self, n):
+ global gsmstate
+ if gsmstate['oncall']:
+ return []
+ (start, end, number) = self.list[-1-n]
+ if number == None:
+ return []
+ return ['Call back %s' % number]
+ def event(self, n, ev):
+ global gsmstate
+ if gsmstate['oncall']:
+ if ev == 0:
+ # Mute
+ return
+ if ev == 1:
+ reject()
+ if ev == 0:
+ (start, end, number) = self.list[-1-n]
+ gsmstate['lastcallnum'] = number
+ gsmstate['lastcaller'] = number
+ gsmstate['obj'].refresh(True)
+
+
+
+class tasklist_speed_dial(sys.modules['__main__'].tasklist):
+ def __init__(self):
+ sys.modules['__main__'].tasklist.__init__(self)
+ self.name = 'Speed Dials'
+
+ def start_refresh(self):
+ self.list = []
+ b = gsmstate['book']
+ print "Start Refresh"
+ for i in range(32,96):
+ i = book_speed(b, chr(i))
+ if i == None:
+ continue
+ self.list.append(i)
+
+ def info(self, n):
+ num, name = self.list[n]
+ return 'cmd', name
+
+ def options(self, n):
+ num, name = self.list[n]
+ return ['Call ' + num]
+ def event(self, n, ev):
+ if ev == 0:
+ global gsmstate
+ num, name = self.list[n]
+ gsmstate['lastcallnum'] = num
+ gsmstate['lastcaller'] = name
+ gsmstate['obj'].refresh(True)