PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2004 Joe Pham <djpham@netzero.com>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: com_samsungscha310.py 4365 2007-08-17 21:11:59Z djpham $
0009 
0010 """Communicate with a Samsung SCH-A310"""
0011 
0012 # lib modules
0013 import sha
0014 
0015 # my modules
0016 import common
0017 import commport
0018 import com_brew
0019 import com_phone
0020 import com_samsung
0021 
0022 
0023 class Phone(com_samsung.Phone):
0024 
0025     "Talk to the Samsung SCH-A310 Cell Phone"
0026 
0027     desc="SCH-A310"
0028     serialsname='scha310'
0029 
0030     __groups_range=xrange(5)
0031     __phone_entries_range=xrange(1,501)
0032     __pb_max_entries=23
0033     __pb_max_speeddials=500
0034     __pb_entry=0
0035     __pb_mem_loc=1
0036     __pb_group=2
0037     __pb_ringtone=3
0038     __pb_name=4
0039     __pb_speed_dial=5
0040     __pb_secret=6
0041     __pb_home_num=7
0042     __pb_office_num=9
0043     __pb_mobile_num=11
0044     __pb_pager_num=13
0045     __pb_fax_num=15
0046     __pb_no_label_num=17
0047     __pb_blanks=(19, 20)
0048     __pb_email=21
0049     __pb_date_time_stamp=22
0050     __pb_numbers= ({'home': __pb_home_num},
0051                     {'office': __pb_office_num},
0052                     {'cell': __pb_mobile_num},
0053                     {'pager': __pb_pager_num},
0054                     {'fax': __pb_fax_num},
0055                     {'none': __pb_no_label_num})
0056     __pb_max_name_len=12
0057     __pb_max_number_len=32
0058     __pb_max_emails=1
0059     builtinringtones=( 'Inactive',
0060                        'Bell 1', 'Bell 2', 'Bell 3', 'Bell 4', 'Bell 5',
0061                        'Melody 1', 'Melody 2', 'Melody 3', 'Melody 4', 'Melody 5',
0062                        'Melody 6', 'Melody 7', 'Melody 8', 'Melody 9', 'Melody 10',
0063                        'Melody 11', 'Melody 12', 'Melody 13', 'Melody 14', 'Melody 15',
0064                        'Melody 16')
0065 
0066     __cal_end_datetime_value=None
0067 
0068     def __init__(self, logtarget, commport):
0069         "Calls all the constructors and sets initial modes"
0070         com_samsung.Phone.__init__(self, logtarget, commport)
0071         self.mode=self.MODENONE
0072 
0073     def getfundamentals(self, results):
0074         """Gets information fundamental to interopating with the phone and UI.
0075         Currently this is:
0076 
0077           - 'uniqueserial'     a unique serial number representing the phone
0078           - 'groups'           the phonebook groups
0079           - 'wallpaper-index'  map index numbers to names
0080           - 'ringtone-index'   map index numbers to ringtone names
0081 
0082         This method is called before we read the phonebook data or before we
0083         write phonebook data.
0084         """
0085 
0086         if not self.is_online():
0087             self.log("Failed to talk to phone")
0088             return results
0089 
0090         self.setmode(self.MODEPHONEBOOK)
0091 
0092         # use a hash of ESN and other stuff (being paranoid)
0093         self.log("Retrieving fundamental phone information")
0094         self.log("Reading phone serial number")
0095         results['uniqueserial']=sha.new(self.get_esn()).hexdigest()
0096 
0097         # now read groups
0098         self.log("Reading group information")
0099         g=self.get_groups(self.__groups_range)
0100         groups={}
0101 
0102         for i in range(len(g)):
0103             if len(g[i]):
0104                 groups[i]={ 'name': g[i] }
0105         results['groups']=groups
0106 
0107         # get the ringtones
0108         self.log('Reading ringtone index')
0109         results['ringtone-index']=self.get_ringtone_index()
0110         self.setmode(self.MODEMODEM)
0111         self.log("Fundamentals retrieved")
0112         return results
0113 
0114     def get_ringtone_index(self):
0115         try:
0116             s=self.comm.sendatcommand('#PUGSN?')
0117             if len(s)==0:
0118                 return {}
0119         except commport.ATError:
0120             return {}
0121             
0122         r={}
0123         for k in s[1:]:
0124             s3=k.split(',')
0125             r[int(s3[0])]={ 'name': s3[2], 'origin': 'builtin' }
0126         return r
0127 
0128     def _get_phonebook(self, result, show_progress=True):
0129         """Reads the phonebook data.  The L{getfundamentals} information will
0130         already be in result."""
0131         self.setmode(self.MODEPHONEBOOK)
0132         c=len(self.__phone_entries_range)
0133         k=0
0134         pb_book={}
0135         for j in self.__phone_entries_range:
0136             pb_entry=self.get_phone_entry(j);
0137             if len(pb_entry)==self.__pb_max_entries:
0138                 pb_book[k]=self._extract_phone_entry(pb_entry, result)
0139                 if show_progress:
0140                     self.progress(j, c, 'Reading '+pb_entry[self.__pb_name])
0141                 k+=1
0142             else:
0143                 if show_progress:
0144                     self.progress(j, c, 'Blank entry: %d' % j)
0145         self.setmode(self.MODEMODEM)
0146         return pb_book
0147         
0148     def getphonebook(self,result):
0149         """Reads the phonebook data.  The L{getfundamentals} information will
0150         already be in result."""
0151         if not self.is_online():
0152             self.log("Failed to talk to phone")
0153             return {}
0154         pb_book=self._get_phonebook(result)
0155         result['phonebook']=pb_book
0156         return pb_book
0157 
0158     def _extract_phone_entry(self, entry, fundamentals):
0159 
0160         res={}
0161 
0162         # serials
0163         res['serials']=[ {'sourcetype': self.serialsname,
0164                           'sourceuniqueid': fundamentals['uniqueserial'],
0165                           'serial1': entry[self.__pb_entry],
0166                           'serial2': entry[self.__pb_mem_loc] }]
0167 
0168         # only one name
0169         res['names']=[ {'full': entry[self.__pb_name].strip('"') } ]
0170 
0171         # only one category
0172         g=fundamentals['groups']
0173         i=int(entry[self.__pb_group])
0174         res['categories']=[ {'category': g[i]['name'] } ]
0175 
0176         # emails
0177         s=entry[self.__pb_email].strip('"')
0178         if len(s):
0179                res['emails']=[ { 'email': s } ]
0180 
0181         # urls
0182         # private
0183         res['flags']=[ { 'secret': entry[self.__pb_secret]=='1' } ]
0184 
0185         # memos
0186         # wallpapers
0187         # ringtones
0188         r=fundamentals['ringtone-index']
0189         try:
0190             ringtone_name=r[int(entry[self.__pb_ringtone])]['name']
0191         except:
0192             ringtone_name=entry[self.__pb_ringtone]
0193         res['ringtones']=[ { 'ringtone': ringtone_name,
0194                              'use': 'call' } ]
0195 
0196         # numbers
0197         speed_dial=int(entry[self.__pb_speed_dial])
0198         res['numbers']=[]
0199         for k in range(len(self.__pb_numbers)):
0200             n=self.__pb_numbers[k]
0201             for key in n:
0202                 if len(entry[n[key]]):
0203                     if speed_dial==k:
0204                         res['numbers'].append({ 'number': entry[n[key]],
0205                                         'type': key,
0206                                         'speeddial': int(entry[self.__pb_mem_loc])})
0207                     else:
0208                         res['numbers'].append({ 'number': entry[n[key]],
0209                                         'type': key })
0210         return res
0211 
0212     def savephonebook(self, data):
0213         "Saves out the phonebook"
0214         if not self.is_online():
0215             self.log("Failed to talk to phone")
0216             return data
0217 
0218         pb_book=data['phonebook']
0219         pb_groups=data['groups']
0220         self.log('Validating phonebook entries.')
0221         del_entries=[]
0222         for k in pb_book:
0223             if not self.__validate_entry(pb_book[k], pb_groups):
0224                 self.log('Invalid entry, entry will be not be sent.')
0225                 del_entries.append(k)
0226         for k in del_entries:
0227             self.log('Deleting entry '+pb_book[k]['names'][0]['full'])
0228             del pb_book[k]
0229         self._has_duplicate_speeddial(pb_book)
0230         self.log('All entries validated')
0231 
0232         pb_locs=[False]*(len(self.__phone_entries_range)+1)
0233         pb_mem=[False]*len(pb_locs)
0234 
0235         # get existing phonebook from the phone
0236         self.log("Getting current phonebook from the phone")
0237         current_pb=self._get_phonebook(data, True)
0238 
0239         # check and adjust for speeddial changes
0240         self.log("Processing speeddial data")
0241         for k in pb_book:
0242             self._update_speeddial(pb_book[k])
0243 
0244         # check for deleted entries and delete them
0245         self.setmode(self.MODEPHONEBOOK)
0246         self.log("Processing deleted entries")
0247         for k1 in current_pb:
0248             s1=current_pb[k1]['serials'][0]['serial1']
0249             found=False
0250             for k2 in pb_book:
0251                 if self._same_serial1(s1, pb_book[k2]):
0252                     found=True
0253                     break
0254             if found:
0255                 pb_locs[int(current_pb[k1]['serials'][0]['serial1'])]=True
0256                 pb_mem[int(current_pb[k1]['serials'][0]['serial2'])]=True
0257             else:
0258                 self.log("Deleted item: "+current_pb[k1]['names'][0]['full'])
0259                 # delete the entries from data and the phone
0260                 self.progress(0, 10, "Deleting "+current_pb[k1]['names'][0]['full'])
0261                 self._del_phone_entry(current_pb[k1])
0262         mem_idx, loc_idx = self.__pb_max_speeddials, 1
0263 
0264         # check for new entries & update serials
0265         self.log("Processing new & updated entries")
0266         serials_update=[]
0267         progresscur, progressmax=1,len(pb_book)
0268         ringtone_index=data['ringtone-index']
0269         for k in pb_book:
0270             if progresscur>len(self.__phone_entries_range):
0271                 self.log('Max phone entries exceeded: '+str(progresscur))
0272                 break
0273             e=pb_book[k]
0274             if not self._has_serial1(e):
0275                 while pb_locs[loc_idx]:
0276                     loc_idx += 1
0277                 pb_locs[loc_idx]=True
0278                 sd=self._get_speeddial(e)
0279                 if sd:
0280                     mem_index=sd
0281                     pb_mem[sd]=True
0282                 else:
0283                     while pb_mem[mem_idx]:
0284                         mem_idx -= 1
0285                     pb_mem[mem_idx]=True
0286                     mem_index=mem_idx
0287                     self._set_speeddial(e, mem_idx)
0288                 s1={ 'sourcetype': self.serialsname,
0289                           'sourceuniqueid': data['uniqueserial'],
0290                           'serial1': `loc_idx`,
0291                           'serial2': `mem_index` }
0292 
0293                 e['serials'].append(s1)
0294                 self.log("New entries: Name: "+e['names'][0]['full']+", s1: "+`loc_idx`+", s2: "+`mem_index`)
0295                 serials_update.append((self._bitpim_serials(e), s1))
0296             self.progress(progresscur, progressmax, "Updating "+e['names'][0]['full'])
0297             if not self._write_phone_entry(e, pb_groups, ringtone_index):
0298                 self.log("Failed to save entry: "+e['names'][0]['full'])
0299             progresscur += 1
0300 
0301         # update existing and new entries
0302         data["serialupdates"]=serials_update
0303         self.log("Done")
0304         self.setmode(self.MODEMODEM)
0305 
0306         return data
0307 
0308 
0309     # validate a phonebook entry, return True if good, False otherwise
0310     def __validate_entry(self, pb_entry, pb_groups):
0311         try:
0312             # validate name & alias
0313             name=pb_entry['names'][0]['full'].replace('"', '')
0314             if len(name)>self.__pb_max_name_len:
0315                 name=name[:self.__pb_max_name_len]
0316             if pb_entry['names'][0]['full']!=name:
0317                 pb_entry['names'][0]['full']=name
0318             # validate numbers
0319             has_number_or_email=False
0320             if pb_entry.has_key('numbers'):
0321                 for n in pb_entry['numbers']:
0322                     num=self.phonize(n['number'])
0323                     if len(num)>self.__pb_max_number_len:
0324                         num=num[:self.__pb_max_number_len]
0325                     if num != n['number']:
0326                         self.log('Updating number from '+n['number']+' to '+num)
0327                         n['number']=num
0328                     try:
0329                         self._get_number_type(n['type'])
0330                     except:
0331                         self.log(n['number']+': setting type to home.')
0332                         n['type']='home'
0333                     has_number_or_email=True
0334             # validate emails
0335             if pb_entry.has_key('emails'):
0336                 if len(pb_entry['emails'])>self.__pb_max_emails:
0337                     self.log(name+': Each entry can only have %s emails.  The rest will be ignored.'%str(self.__pb_max_emails))
0338                 email=pb_entry['emails'][0]['email']
0339                 email.replace('"', '')
0340                 if len(email)>self.__pb_max_number_len:
0341                     email=email[:self.__pb_max_number_len]
0342                 if email!=pb_entry['emails'][0]['email']:
0343                     pb_entry['emails'][0]['email']=email
0344                 has_number_or_email=True
0345             if not has_number_or_email:
0346                 self.log(name+': Entry has no numbers or emails')
0347                 # return False so this entry can be deleted from the dict
0348                 return False
0349             # validate groups
0350             found=False
0351             if pb_entry.has_key('categories') and len(pb_entry['categories']):
0352                 pb_cat=pb_entry['categories'][0]['category']
0353                 for k in pb_groups:
0354                     if pb_groups[k]['name']==pb_cat:
0355                         found=True
0356                         break
0357             if not found:
0358                 self.log(name+': category set to '+pb_groups[0]['name'])
0359                 pb_entry['categories']=[{'category': pb_groups[0]['name']}]
0360             # validate ringtones
0361             found=False
0362             if pb_entry.has_key('ringtones') and len(pb_entry['ringtones']):
0363                 pb_rt=pb_entry['ringtones'][0]['ringtone']
0364                 # can only set to builtin-ringtone
0365                 for k in self.builtinringtones:
0366                     if k==pb_rt:
0367                         found=True
0368                         break
0369             if not found:
0370                 self.log(name+': ringtone set to '+self.builtinringtones[0])
0371                 pb_entry['ringtones']=[{'ringtone': self.builtinringtones[0],
0372                                         'use': 'call' }]
0373             # everything's cool
0374             return True
0375         except:
0376             raise
0377         
0378     def _has_duplicate_speeddial(self, pb_book):
0379         b=[False]*(self.__pb_max_speeddials+1)
0380         for k in pb_book:
0381             try:
0382                 for  k1, kk in enumerate(pb_book[k]['numbers']):
0383                     sd=kk['speeddial']
0384                     if sd and b[sd]:
0385                         # speed dial is in used, remove this one
0386                         del pb_book[k]['numbers'][k1]['speeddial']
0387                         self.log('speeddial %d exists, deleted'%sd)
0388                     else:
0389                         b[sd]=True
0390             except:
0391                 pass
0392         return False
0393 
0394     def _update_speeddial(self, pb_entry):
0395         try:
0396             s=self._my_serials(pb_entry)
0397             s1=int(s['serial2'])
0398             sd=self._get_speeddial(pb_entry)
0399             if not sd:
0400                 # speed dial not set, set it to current mem slot
0401                 self._set_speeddial(pb_entry, s1)
0402             elif sd!=s1:
0403                 # speed dial set to a different slot, mark it
0404                 self._del_my_serials(pb_entry)
0405         except:
0406             pass
0407 
0408     def _get_speeddial(self, pb_entry):
0409         n=pb_entry.get('numbers', [])
0410         for k in n:
0411             try:
0412                if k['speeddial']:
0413                    return k['speeddial']
0414             except:
0415                 pass
0416         return 0
0417 
0418     def _set_speeddial(self, pb_entry, sd):
0419         if not pb_entry.has_key('numbers'):
0420             # no numbers key, just return
0421             return
0422         for k in pb_entry['numbers']:
0423             if k.has_key('speeddial'):
0424                 k['speeddial']=sd
0425                 return
0426         pb_entry['numbers'][0]['speeddial']=sd
0427 
0428     def _del_phone_entry(self, pb_entry):
0429         try:
0430             return self.save_phone_entry(self._my_serials(pb_entry)['serial1'])
0431         except:
0432             return False
0433 
0434     def _same_serial1(self, s1, pb_entry):
0435         for k in pb_entry['serials']:
0436             if k['sourcetype']==self.serialsname and k.has_key('serial1'):
0437                 return k['serial1']==s1
0438         return False
0439 
0440     def _has_serial1(self, pb_entry):
0441         for k in pb_entry['serials']:
0442             if k['sourcetype']==self.serialsname and k.has_key('serial1'):
0443                 return True
0444         return False
0445 
0446     def _bitpim_serials(self, pb_entry):
0447         for k in pb_entry['serials']:
0448             if k['sourcetype']=="bitpim":
0449                 return k
0450         return {}
0451 
0452     def _del_my_serials(self, pb_entry):
0453         for k in range(len(pb_entry['serials'])):
0454             if pb_entry['serials'][k]['sourcetype']==self.serialsname:
0455                 del pb_entry['serials'][k]
0456                 return
0457 
0458     def _my_serials(self, pb_entry):
0459         for k in pb_entry['serials']:
0460             if k['sourcetype']==self.serialsname:
0461                 return k
0462         return {}
0463 
0464     def _get_number_type(self, type):
0465         n=self.__pb_numbers
0466         for k in range(len(n)):
0467             if n[k].has_key(type):
0468                 return k, n[k][type]
0469         raise common.IntegrityCheckFailed(self.desc, "Invalid Number Type")
0470 
0471     def _write_phone_entry(self, pb_entry, groups, ringtone_index):
0472         # setting up a list to send to the phone, all fields preset to '0'
0473         e=['0']*self.__pb_max_entries
0474         # setting the entry # and memory location #
0475         serials=self._my_serials(pb_entry)
0476         e[self.__pb_entry]=serials['serial1']
0477         e[self.__pb_mem_loc]=serials['serial2']
0478         # groups/categories
0479         grp=0
0480         try:
0481             grp_name=pb_entry['categories'][0]['category']
0482             for k in range(len(groups)):
0483                 if groups[k]['name']==grp_name:
0484                     grp=k
0485                     break
0486         except:
0487             # invalid group or no group specified, default to group 0
0488             grp, pb_entry['categories']=0, [{'category': groups[0]['name']}]
0489         e[self.__pb_group]=`grp`
0490 
0491         # ringtones
0492         e[self.__pb_ringtone]=None
0493         try:
0494             rt=pb_entry['ringtones'][0]['ringtone']
0495             for k, n in enumerate(self.builtinringtones):
0496                 if n==rt:
0497                     e[self.__pb_ringtone]=`k`
0498                     break
0499         except:
0500             pass
0501         if e[self.__pb_ringtone] is None:
0502             e[self.__pb_ringtone]='0'
0503             pb_entry['ringtones']=[ { 'ringtone': self.builtinringtones[0],
0504                                       'use': 'call' } ]
0505 
0506         # name
0507         e[self.__pb_name]='"'+pb_entry['names'][0]['full']+'"'
0508 
0509         # private/secret
0510         secret='0'
0511 
0512         try:
0513             if pb_entry['flags'][0]['secret']:
0514                 secret='1'
0515         except:
0516             pass
0517         e[self.__pb_secret]=secret
0518 
0519         # numbers & speed dial
0520         # preset to empty
0521         for k in range(len(self.__pb_numbers)):
0522             for kk in self.__pb_numbers[k]:
0523                 e[self.__pb_numbers[k][kk]]=''
0524                 e[self.__pb_numbers[k][kk]+1]=''
0525         speed_dial='0'
0526         n=pb_entry.get('numbers', [])
0527         for k in range(len(n)):
0528             try:
0529                 nk=n[k]
0530                 kkk, kk=self._get_number_type(nk['type'])
0531             except:
0532                 # invalid type, default to 'home'
0533                 nk['type']='home'
0534                 kkk, kk=0, self.__pb_home_num
0535             e[kk],e[kk+1]=self.phonize(nk['number']), secret
0536             try:
0537                 if nk['speeddial']:
0538                     speed_dial=`kkk`
0539             except:
0540                 pass
0541 
0542         e[self.__pb_speed_dial]=speed_dial
0543         # email
0544         email=''
0545         try:
0546             email=pb_entry['emails'][0]['email']
0547         except:
0548             pass
0549         e[self.__pb_email]='"'+email+'"'
0550         for k in self.__pb_blanks:
0551             e[k]=''
0552         e[self.__pb_date_time_stamp]=self.get_time_stamp()
0553 
0554         # final check to determine if this entry has changed.
0555         # if it has not then do nothing and just return
0556 
0557         ee=self.get_phone_entry(int(e[self.__pb_entry]))
0558         if len(ee):
0559             # valid phone entry, do comparison
0560             # DSV took " out, need put them back in
0561             ee[self.__pb_name]='"'+ee[self.__pb_name]+'"'
0562             ee[self.__pb_email]='"'+ee[self.__pb_email]+'"'
0563             k=self.__pb_max_entries-2
0564             if e[0:k]==ee[0:k]:
0565                 return True
0566 
0567         return self.save_phone_entry('0,'+','.join(e))
0568 
0569     getphoneinfo=com_samsung.Phone._getphoneinfo
0570 
0571     getringtones=None
0572 
0573     getwallpapers=None
0574 
0575     getmedia=None
0576 
0577 class Profile(com_samsung.Profile):
0578 
0579     serialsname='scha310'
0580     # use for auto-detection
0581     phone_manufacturer='SAMSUNG ELECTRONICS'
0582     phone_model='SCH-A310/148'
0583     
0584     def __init__(self):
0585         com_samsung.Profile.__init__(self)
0586 
0587     _supportedsyncs=(
0588         ('phonebook', 'read', None),  # all phonebook reading
0589         ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook
0590         ('calendar', 'read', None),   # all calendar reading
0591         ('calendar', 'write', 'OVERWRITE'),   # only overwriting calendar
0592         )
0593 
0594     def convertphonebooktophone(self, helper, data):
0595 
0596         return data;
0597 
0598 

Generated by PyXR 0.9.4