| Trees | Indices | Help | 
 | 
|---|
|  | 
  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2006 Denis Tonn <denis@tonn.ca> 
  4  ### Inspiration and code liberally adopted from: 
  5  ### Stephen A. Wood and Joe Pham 
  6  ### 
  7  ### This program is free software; you can redistribute it and/or modify 
  8  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  9  ### 
 10  ### $Id$ 
 11   
 12  """Communicate with a Samsung SPH-A840 from Telus Canada""" 
 13   
 14  # Notes:  
 15  # 
 16  # This Phone does not cleanly return from BREW mode, so everything is 
 17  # done through modem mode.  
 18  # 
 19  # Groups are handled differently on this phone, so I have hard coded 
 20  # them. In any event, there is no way to query them as part of a 
 21  # PBentry. Restoring data to a phone loses group assignments.   
 22  # at#pbgrw does not modify group names - seems to be ignored in phone 
 23  # even though it returns OK.    
 24  # 
 25  # Cannot query/extract Ringtones so that is hard coded. Built in tones 
 26  # are ok and can be changed through BitPim. 
 27  # 
 28  # Cannot query/extract Wallpaper so builtin values are hardcoded and 
 29  # everything else is maintained through a write to phone   
 30  # 
 31  # Special case email and url fields. If these are blank they MUST have 
 32  # a quoted space in the write or phone data is corrupted. 
 33  # 
 34  # uslot 1 should not be used. I have not done anything in this code to 
 35  # prevent it, but the phone will complain if you try and edit a 
 36  # pbentry with uslot = 1. Change the "location" on the phone and it's 
 37  # fine. 
 38  # 
 39  # There is no way of setting (or unsetting) a "secret" pbentry on this 
 40  # phone. 
 41  #  
 42  # Calendar writes fail (ERROR) unless start_time and end_time are the 
 43  # same.  
 44  # 
 45  # Occasionally the phone will have position 0 as the speeddial number, 
 46  # when it has only one number even when that number isn't in position 
 47  # 0. 
 48  # 
 49   
 50  import time 
 51  import datetime 
 52   
 53  import sha 
 54  import re 
 55  import struct 
 56   
 57  import bpcalendar 
 58  import prototypes 
 59  import todo 
 60  import memo 
 61   
 62  import common 
 63  import commport 
 64  import p_brew 
 65  import p_samsungspha840_telus 
 66  import com_brew 
 67  import com_phone 
 68  import com_samsung_packet 
 69  import helpids 
 70   
 71  numbertypetab=('home','office','cell','pager','fax') 
 72   
 73   
 74  ### Phone class  
 75   
 76  parentphone=com_samsung_packet.Phone 
 78      "Talk to a Samsung SPH-A840 phone (Telus)" 
 79   
 80      __read_timeout=0.5 
 81      __cal_end_datetime_value=None 
 82      __cal_alarm_values={0: -1, 1: 0, 2: 10, 3: 30, 4: 60 } 
 83      __cal_max_name_len=32 
 84      _cal_max_events_per_day=5 
 85   
 86      desc="SPH-A840 (Telus)" 
 87      helpid=helpids.ID_PHONE_SAMSUNGOTHERS 
 88      protocolclass=p_samsungspha840_telus 
 89      serialsname='spha840T' 
 90       
 91      builtin_ringtones=( 
 92          (0, ['Default Tone']), 
 93          (1, ['Ring %02d'%x for x in range(1, 6)]), 
 94          (6, ['Melody %02d'%x for x in range(1, 6)]), 
 95          ) 
 96   
 97  # wallpaper values  
 98  # 8 = no image 
 99  # 3 = human  1-20 
100  # 4 = animal 1-15 
101  # 5 = others 1-10 
102  # 2 = gallery - and wallpaper_pic_path points to the file name 
103  # 7 = image clips  
104   
105  # wallpaper_modifier    
106  # selects specific image in human, animal, others 
107  # selects offset in file for image clips 
108  # must be 0 if no image 
109  # value ignored if gallery image 
110   
111      builtin_pictures=( 
112          (0, ['Human %02d'%x for x in range(1, 21)]), 
113          (30, ['Animal %02d'%x for x in range(1, 16)]), 
114          (60, ['Other %02d'%x for x in range(1, 11)]), 
115          (80, ['On Phone Image']), 
116          ) 
117   
119          com_samsung_packet.Phone.__init__(self, logtarget, commport) 
120          self.numbertypetab=numbertypetab 
121          self.mode=self.MODENONE 
122   
124          """Gets information fundamental to interoperating with the phone and UI.""" 
125      # use a hash of ESN and other stuff (being paranoid) 
126          self.log("Retrieving fundamental phone information") 
127          self.log("Phone serial number") 
128          self.setmode(self.MODEMODEM) 
129          results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 
130      # getting ringtone-index 
131          self.log("Setting up Built in Ring Tone index") 
132          results['ringtone-index']=self._get_builtin_index(self.builtin_ringtones) 
133      # getting wallpaper-index 
134          self.log("Setting up Built in Wallpaper index") 
135          results['wallpaper-index']=self.get_wallpaper_index() 
136          self.log("Ignoring Corrupted Phone group information") 
137      # groups are hard coded since this phone corrupts the read of groups 
138          results['groups']=self.read_groups() 
139          self.log("Fundamentals retrieved") 
140          return results 
141           
143          _res={} 
144          for _starting_idx,_list in builtin_list: 
145              _idx=_starting_idx 
146              for _entry in _list: 
147                  _res[_idx]={ 'name': _entry, 
148                               'origin': 'builtin' } 
149                  _idx+=1 
150          return _res 
151   
155   
157          "Extend incomplete lines" 
158          nfields=24                      # Can we get this from packet def? 
159          ncommas=self.countcommas(line) 
160          if ncommas<0:                   # Un terminated quote 
161              line+='"' 
162              ncommas = -ncommas 
163          if nfields-ncommas>1: 
164              line=line+","*(nfields-ncommas-1) 
165          return line 
166   
168          inquote=False 
169          ncommas=0 
170          for c in line: 
171              if c == '"': 
172                  inquote = not inquote 
173              elif not inquote and c == ',': 
174                  ncommas+=1 
175          if inquote: 
176              ncommas = -ncommas 
177          return ncommas 
178           
181   
184           
187           
190   
192    # This phone returns crap for group names which causes an assertion error in the database 
193    # because of duplicate "blank" names in the groups.  
194    # so to bypass I have set the group names manually 
195          g={} 
196          g[0]={'name': "Friends"} 
197          g[1]={'name': "Business"} 
198          g[2]={'name': "Family"} 
199          g[3]={'name': "General"} 
200          g[4]={'name': "No Group"} 
201          return g 
202   
203   
205          """This phone doesn't save or read groups properly 
206          so we are skipping the save""" 
207          return 
208   
209   
210   
211  # 
212  # 
213  # Called by extractphonebookentry to extract wallpaper from phone entry data  
214  # 
215  # 
217  # wallpaper values  
218  # 8 = no image 
219  # 3 = human  1-20 res index 1-20 
220  # 4 = animal 1-15 res index 30-45 
221  # 5 = others 1-10 res index 60-70 
222  # 2 = gallery - and wallpaper_pic_path points to the file name 
223  # 6 or 7 = image clips - modifier selects offset in image file 
224  # 1 = I assume downloaded - never downloaded a wallpaper 
225  # gallery and images and downloads are treated separately as index 80 
226  #   - save just keeps whatever wallpaper data the phone already has and is not managed by bitpim 
227   
228  # wallpaper_modifier    
229  # selects specific image in human, animal, others 
230  # selects offset in file for image clips 
231  # must be 0 if no image 
232  # value ignored if gallery image file name 
233          _wp_index=fundamentals.get('wallpaper-index', {}) 
234          if entry.wallpaper==8: 
235              return # no image   
236          if entry.wallpaper in (0,1,2,6,7): 
237              wallpaper_index=80  # use "on phone" dict entry name in bitpim and recycle current phone data 
238          if entry.wallpaper==3: 
239              wallpaper_index=entry.wallpaper_modifier 
240          if entry.wallpaper==4: 
241              wallpaper_index=30+entry.wallpaper_modifier 
242          if entry.wallpaper==5: 
243              wallpaper_index=60+entry.wallpaper_modifier 
244     # get the name out of the dict   
245          _wp_name=_wp_index.get(wallpaper_index, {}).get('name', None) 
246          if _wp_name: 
247              res['wallpapers']=[{ 'wallpaper': _wp_name,  'use': 'call' }] 
248   
249  # 
250  # 
251  # Called by extractphonebookentry to extract ringtone from phone entry data  
252  # 
253  # 
255          _rt_index=fundamentals.get('ringtone-index', {}) 
256          _rt_name=_rt_index.get(entry.ringtone, {}).get('name', None) 
257          if _rt_name: 
258              res['ringtones']=[{ 'ringtone': _rt_name, 'use': 'call' }] 
259   
260  # 
261  # 
262  # Called by getphonebook to extract single phone entry data  
263  # 
264  # 
266          res={} 
267          res['serials']=[ {'sourcetype': self.serialsname, 
268                            'slot': entry.slot, 
269                            'sourceuniqueid': fundamentals['uniqueserial']} ] 
270      # only one name 
271          res['names']=[ {'full': entry.name} ] 
272      # only one category 
273          cat=fundamentals['groups'].get(entry.group, {'name': "Unassigned"})['name'] 
274          if cat!="Unassigned": 
275              res['categories']=[ {'category': cat} ] 
276      # only one email 
277          if len(entry.email): 
278              res['emails']=[ {'email': entry.email} ] 
279      # only one url 
280          if len(entry.url): 
281              res['urls']=[ {'url': entry.url} ] 
282          res['numbers']=[] 
283          secret=0 
284          speeddialtype=entry.speeddial 
285          speeddial_has_been_set=0 # need this for fix for bad phonebook data 
286          numberindex=0 
287          for type in self.numbertypetab: 
288              if len(entry.numbers[numberindex].number): 
289                  numhash={'number': entry.numbers[numberindex].number, 'type': type } 
290  #                if entry.numbers[numberindex].secret==1: 
291  #                    secret=1 
292                  self.log("ENTRY -speeddial : "+str(speeddialtype)+" -index : "+str(numberindex)+" -uslot : "+str(entry.slot)) 
293             # fix possible bad phone data - always force dial entry to first number as safety measure 
294             # Otherwise we will lose the uslot data completely 
295                  if speeddial_has_been_set==0: 
296                      numhash['speeddial']=entry.uslot 
297                      speeddial_has_been_set=1 
298                  if speeddialtype==numberindex: 
299                      numhash['speeddial']=entry.uslot 
300                      speeddial_has_been_set=1 
301                  self.log("EXIT -speeddial : "+str(speeddialtype)+" -index : "+str(numberindex)+" -uslot : "+str(entry.slot)) 
302                  res['numbers'].append(numhash) 
303              numberindex+=1 
304   
305      # Field after each number is secret flag.  Setting secret on 
306      # phone sets secret flag for every defined phone number 
307          res['flags']=[ {'secret': secret} ] 
308          _args=(res, entry, fundamentals) 
309          self._extract_wallpaper(*_args) 
310          self._extract_ringtone(*_args) 
311          return res 
312   
313  # 
314  # 
315  # Get the phone book 
316  # 
317  # 
319          """Read the phonebook data.""" 
320          pbook={} 
321          self.setmode(self.MODEPHONEBOOK) 
322   
323          count=0 
324          req=self.protocolclass.phonebookslotrequest() 
325          lastname="" 
326          for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): 
327              req.slot=slot 
328              res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair) 
329              if len(res) > 0: 
330                  lastname=res[0].entry.name 
331                  if len(lastname)>0:  
332                     self.log(`slot`+": "+lastname+" uSlot : "+str(res[0].entry.uslot)) 
333                  entry=self.extractphonebookentry(res[0].entry, result) 
334                  pbook[count]=entry 
335                  count+=1 
336              self.progress(slot, self.protocolclass.NUMPHONEBOOKENTRIES, lastname) 
337          result['phonebook']=pbook 
338          cats=[] 
339          for i in result['groups']: 
340              if result['groups'][i]['name']!='Unassigned': 
341                  cats.append(result['groups'][i]['name']) 
342          result['categories']=cats 
343          self.setmode(self.MODEMODEM) 
344          return pbook 
345   
346  # 
347  # 
348  # Called by savephonebook to create an initial phone book entry 
349  # 
350  # 
352          e=self.protocolclass.pbentry() 
353          for k in entry: 
354           # special treatment for lists 
355              if k=='numbertypes' or k=='secrets': 
356                  continue 
357           # get a phone index for the ring tone name 
358              if k=='ringtone': 
359                  _rt_index=data.get('ringtone-index', {}) 
360                  for rgt in _rt_index.keys(): 
361                    _rt_name=_rt_index.get(rgt, {}).get('name', None) 
362                    if _rt_name==entry['ringtone']: 
363                        e.ringtone=rgt # and save it for the phonebook entry  
364                  continue 
365          # get a a pair of indexs for the wallpaper name - wallpaper and wallpaper_modifier 
366              elif k=='wallpaper': 
367          # setup default values - no image 
368                  e.wallpaper=8 
369                  e.wallpaper_modifier=0 
370                  e.wallpaper_file="" 
371          # now see if we have anything stored in the database 
372                  _wp_index=data.get('wallpaper-index', {}) 
373                  for wpn in _wp_index.keys(): 
374                      _wp_name=_wp_index.get(wpn, {}).get('name', None) 
375                      if _wp_name==entry['wallpaper']: # found the name, now convert to a pair of phone indexes 
376                           if wpn<30:                   # built in Human type, modifier is wpn 
377                               e.wallpaper=3 
378                               e.wallpaper_modifier=wpn 
379                           if wpn>29 and wpn<60:        # built in Animals type, modifier is wpn-30 
380                               e.wallpaper=4 
381                               e.wallpaper_modifier=wpn-30 
382                           if wpn>59 and wpn<80:        # built in Others type, modifier is wpn-60 
383                               e.wallpaper=5 
384                               e.wallpaper_modifier=wpn-60 
385                           if wpn==80:                   # on phone - special processing in caller code 
386                               e.wallpaper=8 
387                               e.wallpaper_modifier=8   # will not be stored on phone just a sig 
388                  continue 
389              elif k=='numbers': 
390                  for numberindex in range(self.protocolclass.NUMPHONENUMBERS): 
391                      enpn=self.protocolclass.phonenumber() 
392                      e.numbers.append(enpn) 
393                  for i in range(len(entry[k])): 
394                      numberindex=entry['numbertypes'][i] 
395                      e.numbers[numberindex].number=entry[k][i] 
396  #                    e.numbers[numberindex].secret=entry['secrets'][i] 
397                      e.numbers[numberindex].secret=0 
398                  continue 
399            # everything else we just set 
400              setattr(e, k, entry[k]) 
401   
402          return e 
403   
404  # 
405  # 
406  # Save the phone book 
407  # 
408  # 
410          "Saves out the phonebook" 
411   
412          pb=data['phonebook'] 
413          keys=pb.keys() 
414          keys.sort() 
415          keys=keys[:self.protocolclass.NUMPHONEBOOKENTRIES] 
416   
417          self.setmode(self.MODEPHONEBOOK) 
418    # 1- Read the existing phonebook so that we cache birthday field and wallpaper file path and stuff 
419    # 2- Erase all entries.  
420          uslots={} 
421          names={} 
422          birthdays={} 
423          wallpaper_s={} 
424          wallpaper_files={} 
425          wallpaper_modifiers={} 
426          req=self.protocolclass.phonebookslotrequest() 
427   
428          self.log('Erasing '+self.desc+' phonebook') 
429          progressmax=self.protocolclass.NUMPHONEBOOKENTRIES+len(keys) 
430          for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): 
431              req.slot=slot 
432              self.progress(slot,progressmax,"Erasing  "+`slot`) 
433              try: 
434                  res=self.sendpbcommand(req,self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair) 
435                  if len(res) > 0: 
436                      self.log("Starting capture data for : "+res[0].entry.name) 
437                      names[slot]=res[0].entry.name 
438                      birthdays[slot]=res[0].entry.birthday 
439                      wallpaper_s[slot]=res[0].entry.wallpaper 
440                      wallpaper_files[slot]=res[0].entry.wallpaper_file 
441                      wallpaper_modifiers[slot]=res[0].entry.wallpaper_modifier 
442                      self.log("Captured data for : "+res[0].entry.name) 
443                      self.log("User Slot from phone: "+str(res[0].entry.uslot)) 
444                  else: 
445                      names[slot]="" 
446              except: 
447                  names[slot]="" 
448                  self.log("Slot "+`slot`+" Empty slot or read failed") 
449              reqerase=self.protocolclass.phonebooksloterase() 
450              reqerase.slot=slot 
451              self.sendpbcommand(reqerase, self.protocolclass.phonebookslotupdateresponse) 
452    # Skip this.. the group save commands don't work 
453    #      self.savegroups(data) 
454   
455          for i in range(len(keys)): 
456              slot=keys[i] 
457              req=self.protocolclass.phonebookslotupdaterequest() 
458              req.entry=self.makeentry(pb[slot],data) 
459    # groups are not handled through the phonebook data on this phone 
460              req.entry.group=self.protocolclass.DEFAULT_GROUP  
461   
462    # force a single space into email and url if they are zero length 
463              if len(req.entry.email)==0: 
464                  req.entry.email=" " 
465              if len(req.entry.url)==0:  
466                  req.entry.url=" " 
467    # restore the stuff we may not have in the database 
468              if names[slot]==req.entry.name: 
469                  req.entry.birthday=birthdays[slot] # no analogy on bitpim database, just keep with entry 
470              # test for internal sig and keep whatever wallpaper is already on the phone 
471                  if req.entry.wallpaper==self.protocolclass.DEFAULT_WALLPAPER and req.entry.wallpaper_modifier==8: # yup, keep previous stuff 
472                       req.entry.wallpaper=wallpaper_s[slot] 
473                       req.entry.wallpaper_file=wallpaper_files[slot] 
474                       req.entry.wallpaper_modifier=wallpaper_modifiers[slot] 
475    # make sure the default wallpaper has no modifier applied 
476              if req.entry.wallpaper==self.protocolclass.DEFAULT_WALLPAPER:   
477                  req.entry.wallpaper_modifier=self.protocolclass.DEFAULT_WALLPAPER_MODIFIER 
478              self.log('Writing entry '+`slot`+" - "+req.entry.name+" uSlot : "+str(req.entry.uslot)) 
479              self.log('Request: '+str(req)) 
480              self.progress(i+self.protocolclass.NUMPHONEBOOKENTRIES,progressmax,"Writing "+req.entry.name) 
481              self.sendpbcommand(req, self.protocolclass.phonebookslotupdateresponse) 
482          self.progress(progressmax+1,progressmax+1, "Phone book write completed") 
483          self.setmode(self.MODEMODEM) 
484          return data 
485  # 
486  # 
487  # Get the phone Calendar 
488  # 
489  # 
491          entries = {} 
492          self.log("Getting calendar entries") 
493          self.setmode(self.MODEPHONEBOOK) 
494          req=self.protocolclass.eventrequest() 
495          cal_cnt=0 
496          for slot in range(self.protocolclass.NUMCALENDAREVENTS): 
497              req.slot=slot 
498              res=self.sendpbcommand(req,self.protocolclass.eventresponse) 
499              if len(res) > 0: 
500                  self.progress(slot+1, self.protocolclass.NUMCALENDAREVENTS, 
501                                res[0].eventname) 
502                  # build a calendar entry 
503                  entry=bpcalendar.CalendarEntry() 
504                  # start time date 
505                  entry.start=res[0].start[0:5] 
506                  if res[0].end: 
507                      # valid end time 
508                      entry.end=res[0].start[0:5] 
509                  else: 
510                      entry.end=entry.start 
511                  # description 
512                  entry.description=res[0].eventname 
513                  try: 
514                      alarm=self.__cal_alarm_values[res[0].alarm] 
515                  except: 
516                      alarm=None 
517                  entry.alarm=alarm 
518                  # update calendar dict 
519                  entries[entry.id]=entry 
520                  cal_cnt += 1 
521          result['calendar']=entries 
522          self.setmode(self.MODEMODEM) 
523          return result 
524   
525  # 
526  # 
527  # Save the phone Calendar 
528  # 
529  # 
531          self.log("Sending calendar entries") 
532          cal=self.process_calendar(dict['calendar']) 
533          # testing 
534          if __debug__: 
535              print 'processed calendar: ', len(cal), ' items' 
536              for c in cal: 
537                  print c.description,':', c.start 
538          self.setmode(self.MODEPHONEBOOK) 
539          self.log("Saving calendar entries") 
540          cal_cnt=0 
541          req=self.protocolclass.eventupdaterequest() 
542          l = self.protocolclass.NUMCALENDAREVENTS 
543          for c in cal: 
544        # Save this entry to phone 
545              # self.log('Item %d' %k) 
546        # pos 
547              req.slot=cal_cnt 
548        # start date time 
549              # print "Start ",c.start 
550              req.start=list(c.start)+[0] 
551        # end date time must be the same as start time for this phone write 
552              req.end=req.start 
553        # time stamp 
554              req.timestamp=list(time.localtime(time.time())[0:6]) 
555              # print "Alarm ",c.alarm 
556              req.alarm=c.alarm 
557        # Name, check for bad char & proper length 
558              # name=c.description.replace('"', '') 
559              name=c.description 
560              if len(name)>self.__cal_max_name_len: 
561                  name=name[:self.__cal_max_name_len] 
562              req.eventname=name 
563        # and save it 
564              self.progress(cal_cnt+1, l, "Updating "+name) 
565              self.sendpbcommand(req,self.protocolclass.eventupdateresponse) 
566              cal_cnt += 1 
567        # delete the rest of the calendar slots 
568          self.log('Deleting unused entries') 
569          for k in range(cal_cnt, l): 
570              self.progress(k, l, "Deleting entry %d" % k) 
571              reqerase=self.protocolclass.eventsloterase() 
572              reqerase.slot=k 
573              self.sendpbcommand(reqerase, self.protocolclass.eventupdateresponse) 
574          self.setmode(self.MODEMODEM) 
575   
576          return dict 
577   
578  #### Profile class  
579    
580  parentprofile=com_samsung_packet.Profile 
582      deviceclasses=("modem",) 
583   
584      BP_Calendar_Version=3 
585      protocolclass=Phone.protocolclass 
586      serialsname=Phone.serialsname 
587      phone_manufacturer='SAMSUNG ELECTRONICS CO.,LTD.' 
588      phone_model='SCH-A850 /180' 
589   
593           
594      _supportedsyncs=( 
595          ('phonebook', 'read', None),  # all phonebook reading 
596          ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook 
597          ('calendar', 'read', None),   # all calendar reading 
598          ('calendar', 'write', 'OVERWRITE')   # only overwriting calendar 
599          ) 
600   
601      # fill in the list of ringtone/sound origins on your phone 
602      ringtoneorigins=('ringers') 
603      #e.g. 
604      #ringtoneorigins=('ringers', 'sounds') 
605   
607      _cal_alarm_values={ 
608          10: 2, 30: 3, 60: 4, -1: 0, 0: 1 } 
609       
611          self._start=self._end=self._alarm=self._desc=None 
612          self._extract_cal_info(calendar_entry, new_date) 
613   
615          s=cal_entry.start 
616          if new_date is not None: 
617              s=new_date[:3]+s[3:] 
618          self._start=s 
619          self._end=cal_entry.end 
620          self._desc=cal_entry.description 
621      # approximate the alarm value 
622          self._alarm=0 
623          alarm=cal_entry.alarm 
624          _keys=self._cal_alarm_values.keys() 
625          _keys.sort() 
626          _keys.reverse() 
627          for k in _keys: 
628              if alarm>=k: 
629                  self._alarm=self._cal_alarm_values[k] 
630                  break 
631   
644       
647      start=property(fget=_get_start) 
648   
651      end=property(fget=_get_end) 
652   
655      description=property(fget=_get_desc) 
656   
659      alarm=property(fget=_get_alarm) 
660   
| Trees | Indices | Help | 
 | 
|---|
| Generated by Epydoc 3.0.1 on Sun Jan 24 16:21:15 2010 | http://epydoc.sourceforge.net |