PyXR

c:\projects\bitpim\src \ phones \ com_samsung_packet.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2004 Joe Pham <djpham@netzero.com>
0004 ### Copyright (C) 2004-2006 Stephen Wood <saw@bitpim.org>
0005 ###
0006 ### This program is free software; you can redistribute it and/or modify
0007 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0008 ###
0009 ### $Id: com_samsung_packet.py 4470 2007-11-28 04:27:52Z djpham $
0010 
0011 """Communicate with a Samsung SCH-Axx phone using AT commands"""
0012 
0013 # standard modules
0014 import time
0015 import re
0016 import datetime
0017 
0018 # BitPim modules
0019 import bpcalendar
0020 import p_brew
0021 import com_brew
0022 import com_phone
0023 import prototypes
0024 import common
0025 import commport
0026 import todo
0027 import memo
0028 
0029 class Phone(com_phone.Phone, com_brew.BrewProtocol):
0030     "Talk to a Samsung phone using AT commands"
0031 
0032     desc="Samsung SPH-Axx phone"
0033 
0034     MODEPHONEBOOK="modephonebook"
0035 
0036     __read_timeout=0.1
0037     # Calendar class vars
0038     # if your phone does not support and end-datetime, set this to a default
0039     # value such as 19800106T000000
0040     # if it does support end-datetime, set this to None
0041     __cal_end_datetime_value=None
0042     __cal_alarm_values={0: 10, 1: 30, 2: 60, 3: -1, 4: 0 }
0043     __cal_max_name_len=32
0044     _cal_max_events_per_day=9
0045 
0046     builtinringtones=()
0047 
0048     builtinimages=()
0049     
0050     def __init__(self, logtarget, commport):
0051         "Call all the contructors and sets initial modes"
0052         com_phone.Phone.__init__(self, logtarget, commport)
0053         com_brew.BrewProtocol.__init__(self)
0054         self.mode=self.MODENONE
0055 
0056     def _setmodephonebooktobrew(self):
0057         self.log("_setmodephonebooktobrew")
0058         self.setmode(self.MODEMODEM)
0059         self.setmode(self.MODEBREW)
0060         return True
0061 
0062     def _setmodemodemtobrew(self):
0063         self.log("_setmodemodemtobrew")
0064         self.log('Switching from modem to BREW')
0065         try:
0066             self.comm.sendatcommand('$QCDMG')
0067             return True
0068         except commport.ATError:
0069             return False
0070 
0071     def _setmodebrewtomodem(self):
0072         self.log("_setmodebrewtomodem")
0073         self.log('Switching from BREW to modem')
0074         try:
0075             self.modemmoderequest()
0076             self.mode=self.MODEMODEM
0077             return True
0078         except:
0079             pass
0080         # give it a 2nd try
0081         try:
0082             self.modemmoderequest()
0083             self.mode=self.MODEMODEM
0084             return True
0085         except:
0086             return False
0087 
0088     def _setmodemodemtophonebook(self):
0089         self.log("_setmodemodemtophonebook")
0090         self.log('Switching from modem to phonebook')
0091         response=self.comm.sendatcommand("#PMODE=1")
0092         return True
0093 
0094     def _setmodemodem(self):
0095         self.log("_setmodemodem")
0096         req=p_brew.memoryconfigrequest()
0097         respc=p_brew.memoryconfigresponse
0098         
0099         # Just try waking phone up first
0100         try:
0101             self.comm.sendatcommand("Z")
0102             self.comm.sendatcommand('E0V1')
0103             return True
0104         except:
0105             pass
0106 
0107         # Now check to see if in diagnostic mode
0108         for baud in 0, 38400,115200:
0109             if baud:
0110                 if not self.comm.setbaudrate(baud):
0111                     continue
0112             try:
0113                 self.sendbrewcommand(req, respc, callsetmode=False)
0114                 self.log('In BREW mode, trying to switch to Modem mode')
0115                 # Infinite loop
0116                 if self._setmodebrewtomodem():
0117                     break
0118                 return False
0119             except com_brew.modeignoreerrortypes:
0120                 pass
0121         
0122         # Should be in modem mode.  Wake up the interface
0123         for baud in (0, 115200, 19200, 230400):
0124             self.log("Baud="+`baud`)
0125             if baud:
0126                 if not self.comm.setbaudrate(baud):
0127                     continue
0128 
0129             try:
0130                 self.comm.sendatcommand("Z")
0131                 self.comm.sendatcommand('E0V1')
0132                 return True
0133             except:
0134                 pass
0135 
0136         return False
0137 
0138     def _setmodephonebook(self):
0139         self.log("_setmodephonebook")
0140         self.setmode(self.MODEMODEM)
0141         self.setmode(self.MODEPHONEBOOK)
0142         return True
0143         
0144     def _setmodephonebooktomodem(self):
0145         self.log("_setmodephonebooktomodem")
0146         self.log('Switching from phonebook to modem')
0147         response=self.comm.sendatcommand("#PMODE=0")
0148         return True
0149         
0150     def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None):
0151         """Similar to the sendpbcommand in com_sanyo and com_lg, except that
0152         a list of responses is returned, one per line of information returned
0153         from the phone"""
0154 
0155         buffer=prototypes.buffer()
0156         
0157         request.writetobuffer(buffer, logtitle="Samsung phonebook request")
0158         data=buffer.getvalue()
0159 
0160         try:
0161             response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror)
0162         except commport.ATError:
0163             self.comm.success=False
0164             self.mode=self.MODENONE
0165             self.raisecommsdnaexception("manipulating the phonebook")
0166 
0167         self.comm.success=True
0168 
0169         reslist=[]
0170         for line in response_lines:
0171             if fixup:
0172                 line=fixup(line)
0173             res=responseclass()
0174             buffer=prototypes.buffer(line)
0175             res.readfrombuffer(buffer, logtitle="Samsung phonebook response")
0176             reslist.append(res)
0177 
0178         return reslist
0179         
0180     def get_esn(self):
0181         req=self.protocolclass.esnrequest()
0182         res=self.sendpbcommand(req, self.protocolclass.esnresponse)
0183         try:
0184             return res[0].esn
0185         except:
0186             pass
0187         return ''
0188     def get_model(self):
0189         req=self.protocolclass.modelreq()
0190         res=self.sendpbcommand(req, self.protocolclass.modelresp)
0191         try:
0192             return res[0].model
0193         except:
0194             return ''
0195     def get_manufacturer(self):
0196         req=self.protocolclass.manufacturerreq()
0197         res=self.sendpbcommand(req, self.protocolclass.manufacturerresp)
0198         try:
0199             return res[0].manufacturer
0200         except:
0201             return ''
0202     def get_battery_level(self):
0203         req=self.protocolclass.batterylevelreq()
0204         res=self.sendpbcommand(req, self.protocolclass.batterylevelresp)
0205         try:
0206             return res[0].levelstr
0207         except:
0208             return ''
0209     def read_groups(self):
0210 
0211         g={}
0212         # Don't crash if phone doesn't accept #PMODE=1 (Canadian phones)
0213         try:
0214             self.setmode(self.MODEPHONEBOOK)
0215         except:
0216             return g
0217         req=self.protocolclass.groupnamerequest()
0218         for i in range(self.protocolclass.NUMGROUPS+1):
0219             req.gid=i
0220             # Don't crash if phone doesn't support groups
0221             try:
0222                 res=self.sendpbcommand(req, self.protocolclass.groupnameresponse)
0223             except:
0224                 return g
0225             g[i]={'name': res[0].entry.groupname}
0226         return g
0227 
0228     def savegroups(self, data):
0229         """Write the groups, sending only those groups that have had
0230         a name change.  (So that ringers don't get messed up)"""
0231         groups=data['groups']
0232 
0233         groups_onphone=self.read_groups() # Get groups on phone
0234 
0235         # If groups read doesn't work, don't try to write groups
0236         if not groups_onphone:
0237             return
0238 
0239         keys=groups.keys()
0240         keys.sort()
0241 
0242         for k in keys:
0243             if groups[k]['name']!=groups_onphone[k]['name']:
0244                 if groups[k]['name']!="Unassigned":
0245                     req=self.protocolclass.groupnamesetrequest()
0246                     req.gid=k
0247                     req.groupname=groups[k]['name']
0248                     # Response will have ERROR, even though it works
0249                     self.sendpbcommand(req, self.protocolclass.unparsedresponse, ignoreerror=True)
0250         
0251     def pblinerepair(self, line):
0252         "Repair a line from a phone with broken firmware"
0253         return line
0254     
0255     def getphonebook(self, result):
0256         """Read the phonebook data."""
0257         pbook={}
0258         self.setmode(self.MODEPHONEBOOK)
0259 
0260         count=0
0261         req=self.protocolclass.phonebookslotrequest()
0262         lastname=""
0263         for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1):
0264             req.slot=slot
0265             res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair)
0266             if len(res) > 0:
0267                 lastname=res[0].entry.name
0268                 self.log(`slot`+": "+lastname)
0269                 entry=self.extractphonebookentry(res[0].entry, result)
0270                 pbook[count]=entry
0271                 count+=1
0272             else:
0273                 lastname=""
0274             self.progress(slot, self.protocolclass.NUMPHONEBOOKENTRIES,
0275                           'Reading entry %(slot)d: %(name)s'%{ 'slot': slot,
0276                                                                'name': lastname })
0277         
0278         result['phonebook']=pbook
0279         cats=[]
0280         for i in result['groups']:
0281             if result['groups'][i]['name']!='Unassigned':
0282                 cats.append(result['groups'][i]['name'])
0283         result['categories']=cats
0284         print "returning keys",result.keys()
0285         
0286         return pbook
0287 
0288     def _extractphonebook_numbers(self, entry, fundamentals, res):
0289         """Extract and build phone numbers"""
0290         res['numbers']=[]
0291         secret=0
0292 
0293         speeddialtype=entry.speeddial
0294         numberindex=0
0295         for type in self.numbertypetab:
0296             if len(entry.numbers[numberindex].number):
0297                 numhash={'number': entry.numbers[numberindex].number, 'type': type }
0298                 if entry.numbers[numberindex].secret==1:
0299                     secret=1
0300                 if speeddialtype==numberindex:
0301                     numhash['speeddial']=entry.uslot
0302                 res['numbers'].append(numhash)
0303                 
0304             numberindex+=1
0305             
0306         # Field after each number is secret flag.  Setting secret on
0307         # phone sets secret flag for every defined phone number
0308         res['flags']=[ {'secret': secret} ]
0309     def _extractphonebook_ringtone(self, entry, fundamentals, res):
0310         """Extract ringtone info"""
0311         if entry.ringtone != self.protocolclass.DEFAULT_RINGTONE:
0312             tone=self.serialsname+"Index_"+`entry.ringtone`
0313             res['ringtones']=[{'ringtone': tone, 'use': 'call'}]
0314     def _extractphonebook_wallpaper(self, entry, fundamentals, res):
0315         """Extract wallpaper info"""
0316         try:
0317             if entry.wallpaper != self.protocolclass.DEFAULT_WALLPAPER:
0318                 tone=self.serialsname+"Index_"+`entry.wallpaper`
0319                 res['wallpapers']=[{'wallpaper': tone, 'use': 'call'}]
0320         except:
0321             pass
0322 
0323     def extractphonebookentry(self, entry, fundamentals):
0324         res={}
0325 
0326         res['serials']=[ {'sourcetype': self.serialsname,
0327                           'slot': entry.slot,
0328                           'sourceuniqueid': fundamentals['uniqueserial']} ]
0329         # only one name
0330         res['names']=[ {'full': entry.name} ]
0331         # only one category
0332         cat=fundamentals['groups'].get(entry.group, {'name': "Unassigned"})['name']
0333         if cat!="Unassigned":
0334             res['categories']=[ {'category': cat} ]
0335         # only one email
0336         if len(entry.email):
0337             res['emails']=[ {'email': entry.email} ]
0338         # only one url
0339         if len(entry.url):
0340             res['urls']=[ {'url': entry.url} ]
0341         # separate the following processing into methods so subclass can
0342         # customize them
0343         self._extractphonebook_numbers(entry, fundamentals, res)
0344         self._extractphonebook_ringtone(entry, fundamentals, res)
0345         self._extractphonebook_wallpaper(entry, fundamentals, res)
0346 
0347         # We don't have a place to put these
0348         # print entry.name, entry.birthday
0349         # print entry.name, entry.timestamp
0350 
0351         return res
0352     
0353     def savephonebook(self, data):
0354         "Saves out the phonebook"
0355 
0356         pb=data['phonebook']
0357         keys=pb.keys()
0358         keys.sort()
0359         keys=keys[:self.protocolclass.NUMPHONEBOOKENTRIES]
0360 
0361         #
0362         # Read the existing phonebook so that we cache birthdays
0363         # Erase all entries, being carefull to modify entries with
0364         # with URL's first
0365         #
0366         uslots={}
0367         names={}
0368         birthdays={}
0369         req=self.protocolclass.phonebookslotrequest()
0370 
0371         self.log('Erasing '+self.desc+' phonebook')
0372         progressmax=self.protocolclass.NUMPHONEBOOKENTRIES+len(keys)
0373         for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1):
0374             req.slot=slot
0375             self.progress(slot,progressmax,"Erasing  "+`slot`)
0376             try:
0377                 res=self.sendpbcommand(req,self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair)
0378                 if len(res) > 0:
0379                     names[slot]=res[0].entry.name
0380                     birthdays[slot]=res[0].entry.birthday
0381                     if len(res[0].entry.url)>0:
0382                         reqhack=self.protocolclass.phonebookslotupdaterequest()
0383                         reqhack.entry=res[0].entry
0384                         reqhack.entry.url=""
0385                         reqhack.entry.ringtone=self.protocolclass.DEFAULT_RINGTONE
0386                         reqhack.entry.wallpaper=self.protocolclass.DEFAULT_WALLPAPER
0387                         reqhack.entry.timestamp=[1900,1,1,0,0,0]
0388                         self.sendpbcommand(reqhack, self.protocolclass.phonebookslotupdateresponse)
0389                 else:
0390                     names[slot]=""
0391             except:
0392                 names[slot]=""
0393                 self.log("Slot "+`slot`+" read failed")
0394             reqerase=self.protocolclass.phonebooksloterase()
0395             reqerase.slot=slot
0396             self.sendpbcommand(reqerase, self.protocolclass.phonebookslotupdateresponse)
0397                 
0398         self.savegroups(data)
0399 
0400         for i in range(len(keys)):
0401             slot=keys[i]
0402             req=self.protocolclass.phonebookslotupdaterequest()
0403             req.entry=self.makeentry(pb[slot],data)
0404             if names[slot]==req.entry.name:
0405                 req.entry.birthday=birthdays[slot]
0406             self.log('Writing entry '+`slot`+" - "+req.entry.name)
0407             self.progress(i+self.protocolclass.NUMPHONEBOOKENTRIES,progressmax,"Writing "+req.entry.name)
0408             self.sendpbcommand(req, self.protocolclass.phonebookslotupdateresponse)
0409         self.progress(progressmax+1,progressmax+1, "Phone book write completed")
0410         return data
0411         
0412     def makeentry(self, entry, data):
0413         e=self.protocolclass.pbentry()
0414 
0415         for k in entry:
0416             # special treatment for lists
0417             if k=='numbertypes' or k=='secrets':
0418                 continue
0419             if k=='ringtone':
0420             #    e.ringtone=self._findmediaindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone')
0421                 continue
0422             elif k=='wallpaper':
0423             #    e.wallpaper=self._findmediaindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper')
0424                 continue
0425             elif k=='numbers':
0426                 #l=getattr(e,k)
0427                 for numberindex in range(self.protocolclass.NUMPHONENUMBERS):
0428                     enpn=self.protocolclass.phonenumber()
0429                     # l.append(enpn)
0430                     e.numbers.append(enpn)
0431                 for i in range(len(entry[k])):
0432                     numberindex=entry['numbertypes'][i]
0433                     e.numbers[numberindex].number=entry[k][i]
0434                     e.numbers[numberindex].secret=entry['secrets'][i]
0435                 continue
0436             # everything else we just set
0437             setattr(e, k, entry[k])
0438         e.ringtone=self.protocolclass.DEFAULT_RINGTONE
0439         e.wallpaper=self.protocolclass.DEFAULT_WALLPAPER
0440         return e
0441 
0442     def getcalendar(self, result):
0443         entries = {}
0444         self.log("Getting calendar entries")
0445         self.setmode(self.MODEPHONEBOOK)
0446         req=self.protocolclass.eventrequest()
0447         cal_cnt=0
0448         for slot in range(self.protocolclass.NUMCALENDAREVENTS):
0449             req.slot=slot
0450             res=self.sendpbcommand(req,self.protocolclass.eventresponse)
0451             if len(res) > 0:
0452                 self.progress(slot+1, self.protocolclass.NUMCALENDAREVENTS,
0453                               res[0].eventname)
0454                 
0455                 # build a calendar entry
0456                 entry=bpcalendar.CalendarEntry()
0457 
0458                 # start time date
0459                 entry.start=res[0].start[0:5]
0460 
0461                 if res[0].end:
0462                     # valid end time
0463                     entry.end=res[0].end[0:5]
0464                 else:
0465                     entry.end=entry.start
0466 
0467                 # description[location]
0468                 entry.desc_loc=res[0].eventname
0469 
0470                 try:
0471                     alarm=self.__cal_alarm_values[res[0].alarm]
0472                 except:
0473                     alarm=None
0474                 entry.alarm=alarm
0475 
0476                 # update calendar dict
0477                 entries[entry.id]=entry
0478                 cal_cnt += 1
0479 
0480         result['calendar']=entries
0481         self.setmode(self.MODEMODEM)
0482         return result
0483 
0484     def _set_unused_calendar_fields(self, entry):
0485             entry['repeat']=None
0486             entry['changeserial']=1
0487             entry['snoozedelay']=0
0488             entry['daybitmap']=0
0489             entry['ringtone']=0
0490 
0491     def process_calendar(self, dict):
0492         """ Optimize and expand calendar data suitable for phone download
0493         """
0494         # first go thru the dict to organize events by date
0495         # and also determine the latest event date
0496         r={}
0497         rp=[]
0498         today=datetime.date.today()
0499         last_date=today
0500         if __debug__:
0501             print 'original calendar:'
0502         for k,e in dict.items():
0503             if __debug__:
0504                 print e.description,':',e.start
0505             sd=datetime.date(*e.start[:3])
0506             ed=datetime.date(*e.end[:3])
0507             if ed>last_date:
0508                 last_date=ed
0509             if e.repeat is None:
0510                 if sd>=today:
0511                     r.setdefault(e.start[:3], []).append(Samsung_Calendar(e))
0512             else:
0513                 if ed>=today:
0514                     rp.append(e)
0515         # go through and expand on the repeated events
0516         delta_1=datetime.timedelta(1)
0517         for n in rp:
0518             current_date=today
0519             end_date=datetime.date(*n.end[:3])
0520             cnt=0
0521             while current_date<=end_date:
0522                 if n.is_active(current_date.year, current_date.month,
0523                                current_date.day):
0524                     cd_l=(current_date.year, current_date.month,
0525                           current_date.day)
0526                     r.setdefault(cd_l, []).append(\
0527                                       Samsung_Calendar(n, cd_l))
0528                     cnt+=1
0529                     if cnt>self.protocolclass.NUMCALENDAREVENTS:
0530                         # enough for this one, breaking out
0531                         break
0532                 current_date+=delta_1
0533         # and put them all into a list
0534         res=[]
0535         keys=r.keys()
0536         # sort by date
0537         keys.sort()
0538         for k in keys:
0539             # sort by time within this date
0540             r[k].sort()
0541             # clip by max events/day
0542             if len(r[k])>self._cal_max_events_per_day:
0543                 res+=r[k][:self._cal_max_events_per_day]
0544             else:
0545                 res+=r[k]
0546         # clip by max events
0547         if len(res)>self.protocolclass.NUMCALENDAREVENTS:
0548             res=res[:self.protocolclass.NUMCALENDAREVENTS]
0549         return res
0550             
0551     def savecalendar(self, dict, merge):
0552         
0553         self.log("Sending calendar entries")
0554 
0555         cal=self.process_calendar(dict['calendar'])
0556         # testing
0557         if __debug__:
0558             print 'processed calendar: ', len(cal), ' items'
0559             for c in cal:
0560                 print c.description,':', c.start
0561 
0562         self.setmode(self.MODEPHONEBOOK)
0563         self.log("Saving calendar entries")
0564         cal_cnt=0
0565         req=self.protocolclass.eventupdaterequest()
0566         l = self.protocolclass.NUMCALENDAREVENTS
0567         for c in cal:
0568             # Save this entry to phone
0569             # self.log('Item %d' %k)
0570             
0571             # pos
0572             req.slot=cal_cnt
0573 
0574             # start date time
0575             #print "Start ",c.start
0576             req.start=list(c.start)+[0]
0577 
0578             # end date time
0579             if self.__cal_end_datetime_value is None:
0580                 # valid end-datetime
0581                 req.end=list(c.end)+[0]
0582                 #print "End ",c.end
0583             else:
0584                 # no end-datetime, set to start-datetime
0585                 req.end=req.start
0586 
0587             # time stamp
0588             req.timestamp=list(time.localtime(time.time())[0:6])
0589 
0590             #print "Alarm ",c.alarm
0591             req.alarm=c.alarm
0592 
0593             # Name, check for bad char & proper length
0594             #name=c.description.replace('"', '')
0595             name=c.desc_loc
0596             if len(name)>self.__cal_max_name_len:
0597                 name=name[:self.__cal_max_name_len]
0598             req.eventname=name
0599 
0600             # and save it
0601             self.progress(cal_cnt+1, l, "Updating "+name)
0602             self.sendpbcommand(req,self.protocolclass.eventupdateresponse)
0603             cal_cnt += 1
0604 
0605         # delete the rest of the calendar slots
0606         self.log('Deleting unused entries')
0607         for k in range(cal_cnt, l):
0608             self.progress(k, l, "Deleting entry %d" % k)
0609             reqerase=self.protocolclass.eventsloterase()
0610             reqerase.slot=k
0611             self.sendpbcommand(reqerase, self.protocolclass.eventupdateresponse)
0612         self.setmode(self.MODEMODEM)
0613 
0614         return dict
0615 
0616     def gettodo(self, result):
0617         todos = {}
0618         self.log("Getting todo entries")
0619         self.setmode(self.MODEPHONEBOOK)
0620         req=self.protocolclass.todorequest()
0621         for slot in range(self.protocolclass.NUMTODOENTRIES):
0622             req.slot=slot
0623             res=self.sendpbcommand(req,self.protocolclass.todoresponse)
0624             if len(res) > 0:
0625                 entry = todo.TodoEntry()
0626                 entry.summary=res[0].subject
0627                 # Convert back to formatted date string
0628                 # Shouldn't todo take dates as a list like
0629                 # other modules do?
0630                 entry.due_date='%4.4d%2.2d%2.2d'%(res[0].duedate[0],res[0].duedate[1],res[0].duedate[2])
0631                 if res[0].priority:
0632                     entry.priority=1
0633                 else:
0634                     entry.priority=10
0635                     
0636                 self.log("Todo "+`slot`+" "+entry.summary+" "+entry.due_date)
0637                 todos[entry.id]=entry
0638 
0639         result['todo']=todos
0640         return result
0641         
0642     def savetodo(self, dict, merge):
0643         self.setmode(self.MODEPHONEBOOK)
0644         todos=dict.get('todo', {})
0645         #todos=dict['todo']
0646         todos_len=len(todos)
0647         l=self.protocolclass.NUMTODOENTRIES
0648         if todos_len > l:
0649             self.log("The number of Todo entries (%d) exceeded the mamximum (%d)" % (cal_len, l))
0650         self.setmode(self.MODEPHONEBOOK)
0651         self.log("Saving todo entries")
0652         todo_cnt=0
0653         req=self.protocolclass.todoupdaterequest()
0654         for k in todos:
0655             todo=todos[k]
0656             print todo.__doc__
0657             if todo_cnt >= l:
0658                 break
0659             
0660             req.slot=todo_cnt
0661             if todo.priority is not None and todo.priority<5:
0662                 req.priority=1
0663             else:
0664                 req.priority=0
0665                 
0666             dd=todo.due_date
0667             req.duedate=(int(dd[:4]),int(dd[4:6]),int(dd[6:10]),0,0,0)
0668             req.timestamp=list(time.localtime(time.time())[0:6])
0669             req.subject=todo.summary
0670             self.sendpbcommand(req,self.protocolclass.todoupdateresponse)
0671             todo_cnt += 1
0672 
0673         req=self.protocolclass.todoerase()
0674         for slot in range(todo_cnt, self.protocolclass.NUMTODOENTRIES):
0675             req.slot=slot
0676             self.sendpbcommand(req,self.protocolclass.todoupdateresponse)
0677 
0678     def getmemo(self, result):
0679         memos = {}
0680         self.log("Getting memo entries")
0681         self.setmode(self.MODEPHONEBOOK)
0682         req=self.protocolclass.memorequest()
0683         for slot in range(self.protocolclass.NUMMEMOENTRIES):
0684             req.slot=slot
0685             res=self.sendpbcommand(req,self.protocolclass.memoresponse)
0686             if len(res) > 0:
0687                 entry=memo.MemoEntry()
0688                 entry.text=res[0].text
0689                 entry.set_date_isostr='%4.4d%2.2d%2.2dT%2.2d%2.2d%2.2d'%(res[0].timestamp[0],res[0].timestamp[1],res[0].timestamp[2],res[0].timestamp[3],res[0].timestamp[4],res[0].timestamp[5])
0690                 memos[entry.id]=entry
0691 
0692         result['memo']=memos
0693         return result
0694 
0695     def savememo(self, dict, merge):
0696         self.setmode(self.MODEPHONEBOOK)
0697         memos=dict.get('memo', {})
0698         memos_len=len(memos)
0699         l=self.protocolclass.NUMMEMOENTRIES
0700         if memos_len > l:
0701             self.log("The number of Memo entries (%d) exceeded the mamximum (%d)" % (cal_len, l))
0702         self.setmode(self.MODEPHONEBOOK)
0703         self.log("Saving memo entries")
0704         memo_cnt=0
0705         req=self.protocolclass.memoupdaterequest()
0706         for k in memos:
0707             memo=memos[k]
0708             if memo_cnt >= l:
0709                 break
0710             
0711             dd=memo.set_date_isostr
0712             req.timestamp=list(time.localtime(time.time())[0:6])
0713             req.text=memo.text
0714             req.slot=memo_cnt
0715             self.sendpbcommand(req,self.protocolclass.memoupdateresponse)
0716             memo_cnt += 1
0717 
0718         req=self.protocolclass.memoerase()
0719         for slot in range(memo_cnt, self.protocolclass.NUMMEMOENTRIES):
0720             req.slot=slot
0721             self.sendpbcommand(req,self.protocolclass.memoupdateresponse)
0722 
0723     # return some basic info about this phone
0724     def getbasicinfo(self, phoneinfo):
0725         self.log('Getting Basic Phone Info')
0726         for _key,_ in phoneinfo.standard_keys:
0727             _val=getattr(self, 'get_'+_key,
0728                          lambda *_: '')()
0729             if _val:
0730                 setattr(phoneinfo, _key, _val)
0731     getphoneinfo=getbasicinfo        
0732     getcallhistory=None
0733         
0734 class Profile(com_phone.Profile):
0735 
0736     BP_Calendar_Version=3
0737     
0738     usbids=( ( 0x04e8, 0x6601, 1),  # Samsung internal USB interface
0739         )
0740 
0741     # which device classes we are.
0742     deviceclasses=("modem","serial")
0743     WALLPAPER_WIDTH=128
0744     WALLPAPER_HEIGHT=118
0745     OVERSIZE_PERCENTAGE=100
0746     MAX_WALLPAPER_BASENAME_LENGTH=19
0747     WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'"
0748     WALLPAPER_CONVERT_FORMAT="png"
0749     
0750     MAX_RINGTONE_BASENAME_LENGTH=19
0751     RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'"
0752 
0753     _supportedsyncs=()
0754 
0755     def __init__(self):
0756         com_phone.Profile.__init__(self)
0757 
0758     def _getgroup(self, name, groups):
0759         for key in groups:
0760             if groups[key]['name']==name:
0761                 return key,groups[key]
0762         return None,None
0763         
0764 
0765     def normalisegroups(self, helper, data):
0766         "Assigns groups based on category data"
0767 
0768         pad=[]
0769         keys=data['groups'].keys()
0770         keys.sort()
0771         for k in keys:
0772             if k==self.protocolclass.NUMGROUPS: # ignore key 4 which is 'Unassigned'
0773                 name=data['groups'][k]['name']
0774                 pad.append(name)
0775 
0776         groups=helper.getmostpopularcategories(self.protocolclass.NUMGROUPS, data['phonebook'], ["Unassigned"], 12, pad)
0777 
0778         # alpha sort
0779         groups.sort()
0780 
0781         # newgroups
0782         newgroups={}
0783 
0784         # Unassigned in 5th group
0785         newgroups[self.protocolclass.NUMGROUPS]={'name': 'Unassigned'}
0786 
0787         # populate
0788         for name in groups:
0789             # existing entries keep same key
0790             if name=="Unassigned": continue
0791             key,value=self._getgroup(name, data['groups'])
0792             if key is not None:
0793                 newgroups[key]=value
0794         # new entries get whatever numbers are free
0795         for name in groups:
0796             key,value=self._getgroup(name, newgroups)
0797             if key is None:
0798                 for key in range(self.protocolclass.NUMGROUPS):
0799                     if key not in newgroups:
0800                         newgroups[key]={'name': name, 'icon': 1}
0801                         break
0802                        
0803         # yay, done
0804         if data['groups']!=newgroups:
0805             data['groups']=newgroups
0806 
0807     def convertphonebooktophone(self, helper, data):
0808         """Converts the data to what will be used by the phone
0809 
0810         @param data: contains the dict returned by getfundamentals
0811                      as well as where the results go"""
0812 
0813         self.normalisegroups(helper, data)
0814         results={}
0815 
0816         # find which entries are already known to this phone
0817         pb=data['phonebook']
0818         # decorate list with (slot, pbkey) tuples
0819         slots=[ (helper.getserial(pb[pbentry].get("serials", []), self.serialsname, data['uniqueserial'], "slot", None), pbentry)
0820                 for pbentry in pb]
0821         slots.sort() # numeric order
0822         # make two lists - one contains known slots, one doesn't
0823         newones=[(pbentry,slot) for slot,pbentry in slots if slot is None]
0824         existing=[(pbentry,slot) for slot,pbentry in slots if slot is not None]
0825         
0826         uslotsused={}
0827 
0828         tempslot=0 # Temporarily just pick slots and speed dial in order
0829         for pbentry,slot in existing+newones:
0830 
0831             if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES:
0832                 break
0833 
0834             try:
0835 
0836                 e={} # entry out
0837 
0838                 entry=data['phonebook'][pbentry]
0839 
0840                 secret=helper.getflag(entry.get('flags', []), 'secret', False)
0841                 if secret:
0842                     secret=1
0843                 else:
0844                     secret=0
0845             
0846                 # name
0847                 e['name']=helper.getfullname(entry.get('names', []),1,1,20)[0]
0848 
0849                 cat=helper.makeone(helper.getcategory(entry.get('categories',[]),0,1,12), None)
0850                 if cat is None:
0851                     e['group']=self.protocolclass.NUMGROUPS # Unassigned group
0852                 else:
0853                     key,value=self._getgroup(cat, data['groups'])
0854                     if key is not None:
0855                         e['group']=key
0856                     else:
0857                         # Sorry no space for this category
0858                         e['group']=self.protocolclass.NUMGROUPS # Unassigned
0859 
0860                 # email addresses
0861                 e['email']=helper.makeone(helper.getemails(entry.get('emails', []), 0,1,32), "")
0862                 # url
0863                 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,32), "")
0864                                          
0865                 # phone numbers
0866                 # there must be at least one phone number
0867                 minnumbers=1
0868                 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS)
0869                 e['numbertypes']=[]
0870                 e['numbers']=[]
0871                 e['secrets']=[]
0872                 unusednumbers=[] # Hold duplicate types here
0873                 typesused={}
0874                 defaulttypenum=0
0875                 for num in numbers:
0876                     typename=num['type']
0877                     if typesused.has_key(typename):
0878                         unusednumbers.append(num)
0879                         continue
0880                     typesused[typename]=1
0881                     for typenum,tnsearch in enumerate(self.numbertypetab):
0882                         if typename==tnsearch:
0883                             if defaulttypenum==0:
0884                                 defaulttypenum=typenum
0885                             number=self.phonize(num['number'])
0886                             if len(number)>self.protocolclass.MAXNUMBERLEN:
0887                                 # :: TODO:: number is too long and we have to either truncate it or ignore it?
0888                                 number=number[:self.protocolclass.MAXNUMBERLEN]
0889                             e['numbers'].append(number)
0890                             if(num.has_key('speeddial')):
0891                                 # Only one number per name can be a speed dial
0892                                 # Should make speed dial be the first that
0893                                 # we come accross
0894                                 e['speeddial']=typenum
0895                                 tryuslot = num['speeddial']
0896                             e['numbertypes'].append(typenum)
0897                             e['secrets'].append(secret)
0898 
0899                             break
0900 
0901                 # Should print to log when a requested speed dial slot is
0902                 # not available
0903                 if e.has_key('speeddial'):
0904                     if tryuslot>=1 and tryuslot<=self.protocolclass.NUMPHONEBOOKENTRIES and not uslotsused.has_key(tryuslot):
0905                         uslotsused[tryuslot]=1
0906                         e['uslot']=tryuslot
0907                 else:
0908                     e['speeddial']=defaulttypenum
0909 
0910                 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None)
0911                 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None)
0912 
0913                 # find the right slot
0914                 if slot is None or slot<1 or slot>self.protocolclass.NUMPHONEBOOKENTRIES or slot in results:
0915                     for i in range(1,100000):
0916                         if i not in results:
0917                             slot=i
0918                             break
0919 
0920                 e['slot']=slot
0921 
0922                 e['timestamp']=list(time.localtime(time.time())[0:6])
0923 
0924                 results[slot]=e
0925             except helper.ConversionFailed:
0926                 continue
0927 
0928         # Fill in uslot for entries that don't have it.
0929         
0930         tryuslot=1
0931         for slot in results.keys():
0932 
0933             e=results[slot]
0934             if not e.has_key('uslot'):
0935                 while tryuslot<self.protocolclass.NUMPHONEBOOKENTRIES and uslotsused.has_key(tryuslot):
0936                     tryuslot += 1
0937                 uslotsused[tryuslot]=1
0938                 e['uslot'] = tryuslot
0939                 results[slot] = e
0940 
0941         data['phonebook']=results
0942         return data
0943 
0944     def phonize(self,str):
0945         """Convert the phone number into something the phone understands
0946         All digits, P, T, * and # are kept, everything else is removed"""
0947 
0948         return re.sub("[^0-9PT#*]", "", str)[:self.protocolclass.MAXNUMBERLEN]
0949 
0950 class Samsung_Calendar:
0951     _cal_alarm_values={
0952         10: 0, 30: 1, 60: 2, -1: 3, 0: 4 }
0953     
0954     def __init__(self, calendar_entry, new_date=None):
0955         self._start=self._end=self._alarm=self._desc=self._desc_loc=None
0956         self._extract_cal_info(calendar_entry, new_date)
0957 
0958     def _extract_cal_info(self, cal_entry, new_date):
0959         s=cal_entry.start
0960         if new_date is not None:
0961             s=new_date[:3]+s[3:]
0962         self._start=s
0963         self._end=cal_entry.end
0964         self._desc=cal_entry.description
0965         self._desc_loc=cal_entry.desc_loc
0966         # approximate the alarm value
0967         self._alarm=0
0968         alarm=cal_entry.alarm
0969         _keys=self._cal_alarm_values.keys()
0970         _keys.sort()
0971         _keys.reverse()
0972         for k in _keys:
0973             if alarm>=k:
0974                 self._alarm=self._cal_alarm_values[k]
0975                 break
0976 
0977     def __lt__(self, rhs):
0978         return self.start<rhs.start
0979     def __le__(self, rhs):
0980         return self.start<=rhs.start
0981     def __eq__(self, rhs):
0982         return self.start==rhs.start
0983     def __ne__(self, rhs):
0984         return self.start!=rhs.start
0985     def __gt__(self, rhs):
0986         return self.start>rhs.start
0987     def __ge__(self, rhs):
0988         return self.start>=rhs.start
0989     
0990     def _get_start(self):
0991         return self._start
0992     start=property(fget=_get_start)
0993 
0994     def _get_end(self):
0995         return self._end
0996     end=property(fget=_get_end)
0997 
0998     def _get_desc(self):
0999         return self._desc
1000     description=property(fget=_get_desc)
1001 
1002     def _get_desc_loc(self):
1003         return self._desc_loc
1004     desc_loc=property(fget=_get_desc_loc)
1005 
1006     def _get_alarm(self):
1007         return self._alarm
1008     alarm=property(fget=_get_alarm)
1009 
1010 
1011     
1012 
1013 

Generated by PyXR 0.9.4