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