Package phones :: Module com_samsung_packet
[hide private]
[frames] | no frames]

Source Code for Module phones.com_samsung_packet

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2004 Joe Pham <djpham@netzero.com> 
   4  ### Copyright (C) 2004-2006 Stephen Wood <saw@bitpim.org> 
   5  ### 
   6  ### This program is free software; you can redistribute it and/or modify 
   7  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   8  ### 
   9  ### $Id: com_samsung_packet.py 4470 2007-11-28 04:27:52Z djpham $ 
  10   
  11  """Communicate with a Samsung SCH-Axx phone using AT commands""" 
  12   
  13  # standard modules 
  14  import time 
  15  import re 
  16  import datetime 
  17   
  18  # BitPim modules 
  19  import bpcalendar 
  20  import p_brew 
  21  import com_brew 
  22  import com_phone 
  23  import prototypes 
  24  import common 
  25  import commport 
  26  import todo 
  27  import memo 
  28   
29 -class Phone(com_phone.Phone, com_brew.BrewProtocol):
30 "Talk to a Samsung phone using AT commands" 31 32 desc="Samsung SPH-Axx phone" 33 34 MODEPHONEBOOK="modephonebook" 35 36 __read_timeout=0.1 37 # Calendar class vars 38 # if your phone does not support and end-datetime, set this to a default 39 # value such as 19800106T000000 40 # if it does support end-datetime, set this to None 41 __cal_end_datetime_value=None 42 __cal_alarm_values={0: 10, 1: 30, 2: 60, 3: -1, 4: 0 } 43 __cal_max_name_len=32 44 _cal_max_events_per_day=9 45 46 builtinringtones=() 47 48 builtinimages=() 49
50 - def __init__(self, logtarget, commport):
51 "Call all the contructors and sets initial modes" 52 com_phone.Phone.__init__(self, logtarget, commport) 53 com_brew.BrewProtocol.__init__(self) 54 self.mode=self.MODENONE
55
56 - def _setmodephonebooktobrew(self):
57 self.log("_setmodephonebooktobrew") 58 self.setmode(self.MODEMODEM) 59 self.setmode(self.MODEBREW) 60 return True
61
62 - def _setmodemodemtobrew(self):
63 self.log("_setmodemodemtobrew") 64 self.log('Switching from modem to BREW') 65 try: 66 self.comm.sendatcommand('$QCDMG') 67 return True 68 except commport.ATError: 69 return False
70
71 - def _setmodebrewtomodem(self):
72 self.log("_setmodebrewtomodem") 73 self.log('Switching from BREW to modem') 74 try: 75 self.modemmoderequest() 76 self.mode=self.MODEMODEM 77 return True 78 except: 79 pass 80 # give it a 2nd try 81 try: 82 self.modemmoderequest() 83 self.mode=self.MODEMODEM 84 return True 85 except: 86 return False
87
88 - def _setmodemodemtophonebook(self):
89 self.log("_setmodemodemtophonebook") 90 self.log('Switching from modem to phonebook') 91 response=self.comm.sendatcommand("#PMODE=1") 92 return True
93
94 - def _setmodemodem(self):
95 self.log("_setmodemodem") 96 req=p_brew.memoryconfigrequest() 97 respc=p_brew.memoryconfigresponse 98 99 # Just try waking phone up first 100 try: 101 self.comm.sendatcommand("Z") 102 self.comm.sendatcommand('E0V1') 103 return True 104 except: 105 pass 106 107 # Now check to see if in diagnostic mode 108 for baud in 0, 38400,115200: 109 if baud: 110 if not self.comm.setbaudrate(baud): 111 continue 112 try: 113 self.sendbrewcommand(req, respc, callsetmode=False) 114 self.log('In BREW mode, trying to switch to Modem mode') 115 # Infinite loop 116 if self._setmodebrewtomodem(): 117 break 118 return False 119 except com_brew.modeignoreerrortypes: 120 pass 121 122 # Should be in modem mode. Wake up the interface 123 for baud in (0, 115200, 19200, 230400): 124 self.log("Baud="+`baud`) 125 if baud: 126 if not self.comm.setbaudrate(baud): 127 continue 128 129 try: 130 self.comm.sendatcommand("Z") 131 self.comm.sendatcommand('E0V1') 132 return True 133 except: 134 pass 135 136 return False
137
138 - def _setmodephonebook(self):
139 self.log("_setmodephonebook") 140 self.setmode(self.MODEMODEM) 141 self.setmode(self.MODEPHONEBOOK) 142 return True
143
144 - def _setmodephonebooktomodem(self):
145 self.log("_setmodephonebooktomodem") 146 self.log('Switching from phonebook to modem') 147 response=self.comm.sendatcommand("#PMODE=0") 148 return True
149
150 - def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None):
151 """Similar to the sendpbcommand in com_sanyo and com_lg, except that 152 a list of responses is returned, one per line of information returned 153 from the phone""" 154 155 buffer=prototypes.buffer() 156 157 request.writetobuffer(buffer, logtitle="Samsung phonebook request") 158 data=buffer.getvalue() 159 160 try: 161 response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror) 162 except commport.ATError: 163 self.comm.success=False 164 self.mode=self.MODENONE 165 self.raisecommsdnaexception("manipulating the phonebook") 166 167 self.comm.success=True 168 169 reslist=[] 170 for line in response_lines: 171 if fixup: 172 line=fixup(line) 173 res=responseclass() 174 buffer=prototypes.buffer(line) 175 res.readfrombuffer(buffer, logtitle="Samsung phonebook response") 176 reslist.append(res) 177 178 return reslist
179
180 - def get_esn(self):
181 req=self.protocolclass.esnrequest() 182 res=self.sendpbcommand(req, self.protocolclass.esnresponse) 183 try: 184 return res[0].esn 185 except: 186 pass 187 return ''
188 - def get_model(self):
189 req=self.protocolclass.modelreq() 190 res=self.sendpbcommand(req, self.protocolclass.modelresp) 191 try: 192 return res[0].model 193 except: 194 return ''
195 - def get_manufacturer(self):
196 req=self.protocolclass.manufacturerreq() 197 res=self.sendpbcommand(req, self.protocolclass.manufacturerresp) 198 try: 199 return res[0].manufacturer 200 except: 201 return ''
202 - def get_battery_level(self):
203 req=self.protocolclass.batterylevelreq() 204 res=self.sendpbcommand(req, self.protocolclass.batterylevelresp) 205 try: 206 return res[0].levelstr 207 except: 208 return ''
209 - def read_groups(self):
210 211 g={} 212 # Don't crash if phone doesn't accept #PMODE=1 (Canadian phones) 213 try: 214 self.setmode(self.MODEPHONEBOOK) 215 except: 216 return g 217 req=self.protocolclass.groupnamerequest() 218 for i in range(self.protocolclass.NUMGROUPS+1): 219 req.gid=i 220 # Don't crash if phone doesn't support groups 221 try: 222 res=self.sendpbcommand(req, self.protocolclass.groupnameresponse) 223 except: 224 return g 225 g[i]={'name': res[0].entry.groupname} 226 return g
227
228 - def savegroups(self, data):
229 """Write the groups, sending only those groups that have had 230 a name change. (So that ringers don't get messed up)""" 231 groups=data['groups'] 232 233 groups_onphone=self.read_groups() # Get groups on phone 234 235 # If groups read doesn't work, don't try to write groups 236 if not groups_onphone: 237 return 238 239 keys=groups.keys() 240 keys.sort() 241 242 for k in keys: 243 if groups[k]['name']!=groups_onphone[k]['name']: 244 if groups[k]['name']!="Unassigned": 245 req=self.protocolclass.groupnamesetrequest() 246 req.gid=k 247 req.groupname=groups[k]['name'] 248 # Response will have ERROR, even though it works 249 self.sendpbcommand(req, self.protocolclass.unparsedresponse, ignoreerror=True)
250
251 - def pblinerepair(self, line):
252 "Repair a line from a phone with broken firmware" 253 return line
254
255 - def getphonebook(self, result):
256 """Read the phonebook data.""" 257 pbook={} 258 self.setmode(self.MODEPHONEBOOK) 259 260 count=0 261 req=self.protocolclass.phonebookslotrequest() 262 lastname="" 263 for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): 264 req.slot=slot 265 res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair) 266 if len(res) > 0: 267 lastname=res[0].entry.name 268 self.log(`slot`+": "+lastname) 269 entry=self.extractphonebookentry(res[0].entry, result) 270 pbook[count]=entry 271 count+=1 272 else: 273 lastname="" 274 self.progress(slot, self.protocolclass.NUMPHONEBOOKENTRIES, 275 'Reading entry %(slot)d: %(name)s'%{ 'slot': slot, 276 'name': lastname }) 277 278 result['phonebook']=pbook 279 cats=[] 280 for i in result['groups']: 281 if result['groups'][i]['name']!='Unassigned': 282 cats.append(result['groups'][i]['name']) 283 result['categories']=cats 284 print "returning keys",result.keys() 285 286 return pbook
287
288 - def _extractphonebook_numbers(self, entry, fundamentals, res):
289 """Extract and build phone numbers""" 290 res['numbers']=[] 291 secret=0 292 293 speeddialtype=entry.speeddial 294 numberindex=0 295 for type in self.numbertypetab: 296 if len(entry.numbers[numberindex].number): 297 numhash={'number': entry.numbers[numberindex].number, 'type': type } 298 if entry.numbers[numberindex].secret==1: 299 secret=1 300 if speeddialtype==numberindex: 301 numhash['speeddial']=entry.uslot 302 res['numbers'].append(numhash) 303 304 numberindex+=1 305 306 # Field after each number is secret flag. Setting secret on 307 # phone sets secret flag for every defined phone number 308 res['flags']=[ {'secret': secret} ]
309 - def _extractphonebook_ringtone(self, entry, fundamentals, res):
310 """Extract ringtone info""" 311 if entry.ringtone != self.protocolclass.DEFAULT_RINGTONE: 312 tone=self.serialsname+"Index_"+`entry.ringtone` 313 res['ringtones']=[{'ringtone': tone, 'use': 'call'}]
314 - def _extractphonebook_wallpaper(self, entry, fundamentals, res):
315 """Extract wallpaper info""" 316 try: 317 if entry.wallpaper != self.protocolclass.DEFAULT_WALLPAPER: 318 tone=self.serialsname+"Index_"+`entry.wallpaper` 319 res['wallpapers']=[{'wallpaper': tone, 'use': 'call'}] 320 except: 321 pass
322
323 - def extractphonebookentry(self, entry, fundamentals):
324 res={} 325 326 res['serials']=[ {'sourcetype': self.serialsname, 327 'slot': entry.slot, 328 'sourceuniqueid': fundamentals['uniqueserial']} ] 329 # only one name 330 res['names']=[ {'full': entry.name} ] 331 # only one category 332 cat=fundamentals['groups'].get(entry.group, {'name': "Unassigned"})['name'] 333 if cat!="Unassigned": 334 res['categories']=[ {'category': cat} ] 335 # only one email 336 if len(entry.email): 337 res['emails']=[ {'email': entry.email} ] 338 # only one url 339 if len(entry.url): 340 res['urls']=[ {'url': entry.url} ] 341 # separate the following processing into methods so subclass can 342 # customize them 343 self._extractphonebook_numbers(entry, fundamentals, res) 344 self._extractphonebook_ringtone(entry, fundamentals, res) 345 self._extractphonebook_wallpaper(entry, fundamentals, res) 346 347 # We don't have a place to put these 348 # print entry.name, entry.birthday 349 # print entry.name, entry.timestamp 350 351 return res
352
353 - def savephonebook(self, data):
354 "Saves out the phonebook" 355 356 pb=data['phonebook'] 357 keys=pb.keys() 358 keys.sort() 359 keys=keys[:self.protocolclass.NUMPHONEBOOKENTRIES] 360 361 # 362 # Read the existing phonebook so that we cache birthdays 363 # Erase all entries, being carefull to modify entries with 364 # with URL's first 365 # 366 uslots={} 367 names={} 368 birthdays={} 369 req=self.protocolclass.phonebookslotrequest() 370 371 self.log('Erasing '+self.desc+' phonebook') 372 progressmax=self.protocolclass.NUMPHONEBOOKENTRIES+len(keys) 373 for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): 374 req.slot=slot 375 self.progress(slot,progressmax,"Erasing "+`slot`) 376 try: 377 res=self.sendpbcommand(req,self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair) 378 if len(res) > 0: 379 names[slot]=res[0].entry.name 380 birthdays[slot]=res[0].entry.birthday 381 if len(res[0].entry.url)>0: 382 reqhack=self.protocolclass.phonebookslotupdaterequest() 383 reqhack.entry=res[0].entry 384 reqhack.entry.url="" 385 reqhack.entry.ringtone=self.protocolclass.DEFAULT_RINGTONE 386 reqhack.entry.wallpaper=self.protocolclass.DEFAULT_WALLPAPER 387 reqhack.entry.timestamp=[1900,1,1,0,0,0] 388 self.sendpbcommand(reqhack, self.protocolclass.phonebookslotupdateresponse) 389 else: 390 names[slot]="" 391 except: 392 names[slot]="" 393 self.log("Slot "+`slot`+" read failed") 394 reqerase=self.protocolclass.phonebooksloterase() 395 reqerase.slot=slot 396 self.sendpbcommand(reqerase, self.protocolclass.phonebookslotupdateresponse) 397 398 self.savegroups(data) 399 400 for i in range(len(keys)): 401 slot=keys[i] 402 req=self.protocolclass.phonebookslotupdaterequest() 403 req.entry=self.makeentry(pb[slot],data) 404 if names[slot]==req.entry.name: 405 req.entry.birthday=birthdays[slot] 406 self.log('Writing entry '+`slot`+" - "+req.entry.name) 407 self.progress(i+self.protocolclass.NUMPHONEBOOKENTRIES,progressmax,"Writing "+req.entry.name) 408 self.sendpbcommand(req, self.protocolclass.phonebookslotupdateresponse) 409 self.progress(progressmax+1,progressmax+1, "Phone book write completed") 410 return data
411
412 - def makeentry(self, entry, data):
413 e=self.protocolclass.pbentry() 414 415 for k in entry: 416 # special treatment for lists 417 if k=='numbertypes' or k=='secrets': 418 continue 419 if k=='ringtone': 420 # e.ringtone=self._findmediaindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') 421 continue 422 elif k=='wallpaper': 423 # e.wallpaper=self._findmediaindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper') 424 continue 425 elif k=='numbers': 426 #l=getattr(e,k) 427 for numberindex in range(self.protocolclass.NUMPHONENUMBERS): 428 enpn=self.protocolclass.phonenumber() 429 # l.append(enpn) 430 e.numbers.append(enpn) 431 for i in range(len(entry[k])): 432 numberindex=entry['numbertypes'][i] 433 e.numbers[numberindex].number=entry[k][i] 434 e.numbers[numberindex].secret=entry['secrets'][i] 435 continue 436 # everything else we just set 437 setattr(e, k, entry[k]) 438 e.ringtone=self.protocolclass.DEFAULT_RINGTONE 439 e.wallpaper=self.protocolclass.DEFAULT_WALLPAPER 440 return e
441
442 - def getcalendar(self, result):
443 entries = {} 444 self.log("Getting calendar entries") 445 self.setmode(self.MODEPHONEBOOK) 446 req=self.protocolclass.eventrequest() 447 cal_cnt=0 448 for slot in range(self.protocolclass.NUMCALENDAREVENTS): 449 req.slot=slot 450 res=self.sendpbcommand(req,self.protocolclass.eventresponse) 451 if len(res) > 0: 452 self.progress(slot+1, self.protocolclass.NUMCALENDAREVENTS, 453 res[0].eventname) 454 455 # build a calendar entry 456 entry=bpcalendar.CalendarEntry() 457 458 # start time date 459 entry.start=res[0].start[0:5] 460 461 if res[0].end: 462 # valid end time 463 entry.end=res[0].end[0:5] 464 else: 465 entry.end=entry.start 466 467 # description[location] 468 entry.desc_loc=res[0].eventname 469 470 try: 471 alarm=self.__cal_alarm_values[res[0].alarm] 472 except: 473 alarm=None 474 entry.alarm=alarm 475 476 # update calendar dict 477 entries[entry.id]=entry 478 cal_cnt += 1 479 480 result['calendar']=entries 481 self.setmode(self.MODEMODEM) 482 return result
483
484 - def _set_unused_calendar_fields(self, entry):
485 entry['repeat']=None 486 entry['changeserial']=1 487 entry['snoozedelay']=0 488 entry['daybitmap']=0 489 entry['ringtone']=0
490
491 - def process_calendar(self, dict):
492 """ Optimize and expand calendar data suitable for phone download 493 """ 494 # first go thru the dict to organize events by date 495 # and also determine the latest event date 496 r={} 497 rp=[] 498 today=datetime.date.today() 499 last_date=today 500 if __debug__: 501 print 'original calendar:' 502 for k,e in dict.items(): 503 if __debug__: 504 print e.description,':',e.start 505 sd=datetime.date(*e.start[:3]) 506 ed=datetime.date(*e.end[:3]) 507 if ed>last_date: 508 last_date=ed 509 if e.repeat is None: 510 if sd>=today: 511 r.setdefault(e.start[:3], []).append(Samsung_Calendar(e)) 512 else: 513 if ed>=today: 514 rp.append(e) 515 # go through and expand on the repeated events 516 delta_1=datetime.timedelta(1) 517 for n in rp: 518 current_date=today 519 end_date=datetime.date(*n.end[:3]) 520 cnt=0 521 while current_date<=end_date: 522 if n.is_active(current_date.year, current_date.month, 523 current_date.day): 524 cd_l=(current_date.year, current_date.month, 525 current_date.day) 526 r.setdefault(cd_l, []).append(\ 527 Samsung_Calendar(n, cd_l)) 528 cnt+=1 529 if cnt>self.protocolclass.NUMCALENDAREVENTS: 530 # enough for this one, breaking out 531 break 532 current_date+=delta_1 533 # and put them all into a list 534 res=[] 535 keys=r.keys() 536 # sort by date 537 keys.sort() 538 for k in keys: 539 # sort by time within this date 540 r[k].sort() 541 # clip by max events/day 542 if len(r[k])>self._cal_max_events_per_day: 543 res+=r[k][:self._cal_max_events_per_day] 544 else: 545 res+=r[k] 546 # clip by max events 547 if len(res)>self.protocolclass.NUMCALENDAREVENTS: 548 res=res[:self.protocolclass.NUMCALENDAREVENTS] 549 return res
550
551 - def savecalendar(self, dict, merge):
552 553 self.log("Sending calendar entries") 554 555 cal=self.process_calendar(dict['calendar']) 556 # testing 557 if __debug__: 558 print 'processed calendar: ', len(cal), ' items' 559 for c in cal: 560 print c.description,':', c.start 561 562 self.setmode(self.MODEPHONEBOOK) 563 self.log("Saving calendar entries") 564 cal_cnt=0 565 req=self.protocolclass.eventupdaterequest() 566 l = self.protocolclass.NUMCALENDAREVENTS 567 for c in cal: 568 # Save this entry to phone 569 # self.log('Item %d' %k) 570 571 # pos 572 req.slot=cal_cnt 573 574 # start date time 575 #print "Start ",c.start 576 req.start=list(c.start)+[0] 577 578 # end date time 579 if self.__cal_end_datetime_value is None: 580 # valid end-datetime 581 req.end=list(c.end)+[0] 582 #print "End ",c.end 583 else: 584 # no end-datetime, set to start-datetime 585 req.end=req.start 586 587 # time stamp 588 req.timestamp=list(time.localtime(time.time())[0:6]) 589 590 #print "Alarm ",c.alarm 591 req.alarm=c.alarm 592 593 # Name, check for bad char & proper length 594 #name=c.description.replace('"', '') 595 name=c.desc_loc 596 if len(name)>self.__cal_max_name_len: 597 name=name[:self.__cal_max_name_len] 598 req.eventname=name 599 600 # and save it 601 self.progress(cal_cnt+1, l, "Updating "+name) 602 self.sendpbcommand(req,self.protocolclass.eventupdateresponse) 603 cal_cnt += 1 604 605 # delete the rest of the calendar slots 606 self.log('Deleting unused entries') 607 for k in range(cal_cnt, l): 608 self.progress(k, l, "Deleting entry %d" % k) 609 reqerase=self.protocolclass.eventsloterase() 610 reqerase.slot=k 611 self.sendpbcommand(reqerase, self.protocolclass.eventupdateresponse) 612 self.setmode(self.MODEMODEM) 613 614 return dict
615
616 - def gettodo(self, result):
617 todos = {} 618 self.log("Getting todo entries") 619 self.setmode(self.MODEPHONEBOOK) 620 req=self.protocolclass.todorequest() 621 for slot in range(self.protocolclass.NUMTODOENTRIES): 622 req.slot=slot 623 res=self.sendpbcommand(req,self.protocolclass.todoresponse) 624 if len(res) > 0: 625 entry = todo.TodoEntry() 626 entry.summary=res[0].subject 627 # Convert back to formatted date string 628 # Shouldn't todo take dates as a list like 629 # other modules do? 630 entry.due_date='%4.4d%2.2d%2.2d'%(res[0].duedate[0],res[0].duedate[1],res[0].duedate[2]) 631 if res[0].priority: 632 entry.priority=1 633 else: 634 entry.priority=10 635 636 self.log("Todo "+`slot`+" "+entry.summary+" "+entry.due_date) 637 todos[entry.id]=entry 638 639 result['todo']=todos 640 return result
641
642 - def savetodo(self, dict, merge):
643 self.setmode(self.MODEPHONEBOOK) 644 todos=dict.get('todo', {}) 645 #todos=dict['todo'] 646 todos_len=len(todos) 647 l=self.protocolclass.NUMTODOENTRIES 648 if todos_len > l: 649 self.log("The number of Todo entries (%d) exceeded the mamximum (%d)" % (cal_len, l)) 650 self.setmode(self.MODEPHONEBOOK) 651 self.log("Saving todo entries") 652 todo_cnt=0 653 req=self.protocolclass.todoupdaterequest() 654 for k in todos: 655 todo=todos[k] 656 print todo.__doc__ 657 if todo_cnt >= l: 658 break 659 660 req.slot=todo_cnt 661 if todo.priority is not None and todo.priority<5: 662 req.priority=1 663 else: 664 req.priority=0 665 666 dd=todo.due_date 667 req.duedate=(int(dd[:4]),int(dd[4:6]),int(dd[6:10]),0,0,0) 668 req.timestamp=list(time.localtime(time.time())[0:6]) 669 req.subject=todo.summary 670 self.sendpbcommand(req,self.protocolclass.todoupdateresponse) 671 todo_cnt += 1 672 673 req=self.protocolclass.todoerase() 674 for slot in range(todo_cnt, self.protocolclass.NUMTODOENTRIES): 675 req.slot=slot 676 self.sendpbcommand(req,self.protocolclass.todoupdateresponse)
677
678 - def getmemo(self, result):
679 memos = {} 680 self.log("Getting memo entries") 681 self.setmode(self.MODEPHONEBOOK) 682 req=self.protocolclass.memorequest() 683 for slot in range(self.protocolclass.NUMMEMOENTRIES): 684 req.slot=slot 685 res=self.sendpbcommand(req,self.protocolclass.memoresponse) 686 if len(res) > 0: 687 entry=memo.MemoEntry() 688 entry.text=res[0].text 689 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]) 690 memos[entry.id]=entry 691 692 result['memo']=memos 693 return result
694
695 - def savememo(self, dict, merge):
696 self.setmode(self.MODEPHONEBOOK) 697 memos=dict.get('memo', {}) 698 memos_len=len(memos) 699 l=self.protocolclass.NUMMEMOENTRIES 700 if memos_len > l: 701 self.log("The number of Memo entries (%d) exceeded the mamximum (%d)" % (cal_len, l)) 702 self.setmode(self.MODEPHONEBOOK) 703 self.log("Saving memo entries") 704 memo_cnt=0 705 req=self.protocolclass.memoupdaterequest() 706 for k in memos: 707 memo=memos[k] 708 if memo_cnt >= l: 709 break 710 711 dd=memo.set_date_isostr 712 req.timestamp=list(time.localtime(time.time())[0:6]) 713 req.text=memo.text 714 req.slot=memo_cnt 715 self.sendpbcommand(req,self.protocolclass.memoupdateresponse) 716 memo_cnt += 1 717 718 req=self.protocolclass.memoerase() 719 for slot in range(memo_cnt, self.protocolclass.NUMMEMOENTRIES): 720 req.slot=slot 721 self.sendpbcommand(req,self.protocolclass.memoupdateresponse)
722 723 # return some basic info about this phone
724 - def getbasicinfo(self, phoneinfo):
725 self.log('Getting Basic Phone Info') 726 for _key,_ in phoneinfo.standard_keys: 727 _val=getattr(self, 'get_'+_key, 728 lambda *_: '')() 729 if _val: 730 setattr(phoneinfo, _key, _val)
731 getphoneinfo=getbasicinfo 732 getcallhistory=None
733
734 -class Profile(com_phone.Profile):
735 736 BP_Calendar_Version=3 737 738 usbids=( ( 0x04e8, 0x6601, 1), # Samsung internal USB interface 739 ) 740 741 # which device classes we are. 742 deviceclasses=("modem","serial") 743 WALLPAPER_WIDTH=128 744 WALLPAPER_HEIGHT=118 745 OVERSIZE_PERCENTAGE=100 746 MAX_WALLPAPER_BASENAME_LENGTH=19 747 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'" 748 WALLPAPER_CONVERT_FORMAT="png" 749 750 MAX_RINGTONE_BASENAME_LENGTH=19 751 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'" 752 753 _supportedsyncs=() 754
755 - def __init__(self):
757
758 - def _getgroup(self, name, groups):
759 for key in groups: 760 if groups[key]['name']==name: 761 return key,groups[key] 762 return None,None
763 764
765 - def normalisegroups(self, helper, data):
766 "Assigns groups based on category data" 767 768 pad=[] 769 keys=data['groups'].keys() 770 keys.sort() 771 for k in keys: 772 if k==self.protocolclass.NUMGROUPS: # ignore key 4 which is 'Unassigned' 773 name=data['groups'][k]['name'] 774 pad.append(name) 775 776 groups=helper.getmostpopularcategories(self.protocolclass.NUMGROUPS, data['phonebook'], ["Unassigned"], 12, pad) 777 778 # alpha sort 779 groups.sort() 780 781 # newgroups 782 newgroups={} 783 784 # Unassigned in 5th group 785 newgroups[self.protocolclass.NUMGROUPS]={'name': 'Unassigned'} 786 787 # populate 788 for name in groups: 789 # existing entries keep same key 790 if name=="Unassigned": continue 791 key,value=self._getgroup(name, data['groups']) 792 if key is not None: 793 newgroups[key]=value 794 # new entries get whatever numbers are free 795 for name in groups: 796 key,value=self._getgroup(name, newgroups) 797 if key is None: 798 for key in range(self.protocolclass.NUMGROUPS): 799 if key not in newgroups: 800 newgroups[key]={'name': name, 'icon': 1} 801 break 802 803 # yay, done 804 if data['groups']!=newgroups: 805 data['groups']=newgroups
806
807 - def convertphonebooktophone(self, helper, data):
808 """Converts the data to what will be used by the phone 809 810 @param data: contains the dict returned by getfundamentals 811 as well as where the results go""" 812 813 self.normalisegroups(helper, data) 814 results={} 815 816 # find which entries are already known to this phone 817 pb=data['phonebook'] 818 # decorate list with (slot, pbkey) tuples 819 slots=[ (helper.getserial(pb[pbentry].get("serials", []), self.serialsname, data['uniqueserial'], "slot", None), pbentry) 820 for pbentry in pb] 821 slots.sort() # numeric order 822 # make two lists - one contains known slots, one doesn't 823 newones=[(pbentry,slot) for slot,pbentry in slots if slot is None] 824 existing=[(pbentry,slot) for slot,pbentry in slots if slot is not None] 825 826 uslotsused={} 827 828 tempslot=0 # Temporarily just pick slots and speed dial in order 829 for pbentry,slot in existing+newones: 830 831 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: 832 break 833 834 try: 835 836 e={} # entry out 837 838 entry=data['phonebook'][pbentry] 839 840 secret=helper.getflag(entry.get('flags', []), 'secret', False) 841 if secret: 842 secret=1 843 else: 844 secret=0 845 846 # name 847 e['name']=helper.getfullname(entry.get('names', []),1,1,20)[0] 848 849 cat=helper.makeone(helper.getcategory(entry.get('categories',[]),0,1,12), None) 850 if cat is None: 851 e['group']=self.protocolclass.NUMGROUPS # Unassigned group 852 else: 853 key,value=self._getgroup(cat, data['groups']) 854 if key is not None: 855 e['group']=key 856 else: 857 # Sorry no space for this category 858 e['group']=self.protocolclass.NUMGROUPS # Unassigned 859 860 # email addresses 861 e['email']=helper.makeone(helper.getemails(entry.get('emails', []), 0,1,32), "") 862 # url 863 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,32), "") 864 865 # phone numbers 866 # there must be at least one phone number 867 minnumbers=1 868 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) 869 e['numbertypes']=[] 870 e['numbers']=[] 871 e['secrets']=[] 872 unusednumbers=[] # Hold duplicate types here 873 typesused={} 874 defaulttypenum=0 875 for num in numbers: 876 typename=num['type'] 877 if typesused.has_key(typename): 878 unusednumbers.append(num) 879 continue 880 typesused[typename]=1 881 for typenum,tnsearch in enumerate(self.numbertypetab): 882 if typename==tnsearch: 883 if defaulttypenum==0: 884 defaulttypenum=typenum 885 number=self.phonize(num['number']) 886 if len(number)>self.protocolclass.MAXNUMBERLEN: 887 # :: TODO:: number is too long and we have to either truncate it or ignore it? 888 number=number[:self.protocolclass.MAXNUMBERLEN] 889 e['numbers'].append(number) 890 if(num.has_key('speeddial')): 891 # Only one number per name can be a speed dial 892 # Should make speed dial be the first that 893 # we come accross 894 e['speeddial']=typenum 895 tryuslot = num['speeddial'] 896 e['numbertypes'].append(typenum) 897 e['secrets'].append(secret) 898 899 break 900 901 # Should print to log when a requested speed dial slot is 902 # not available 903 if e.has_key('speeddial'): 904 if tryuslot>=1 and tryuslot<=self.protocolclass.NUMPHONEBOOKENTRIES and not uslotsused.has_key(tryuslot): 905 uslotsused[tryuslot]=1 906 e['uslot']=tryuslot 907 else: 908 e['speeddial']=defaulttypenum 909 910 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 911 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) 912 913 # find the right slot 914 if slot is None or slot<1 or slot>self.protocolclass.NUMPHONEBOOKENTRIES or slot in results: 915 for i in range(1,100000): 916 if i not in results: 917 slot=i 918 break 919 920 e['slot']=slot 921 922 e['timestamp']=list(time.localtime(time.time())[0:6]) 923 924 results[slot]=e 925 except helper.ConversionFailed: 926 continue 927 928 # Fill in uslot for entries that don't have it. 929 930 tryuslot=1 931 for slot in results.keys(): 932 933 e=results[slot] 934 if not e.has_key('uslot'): 935 while tryuslot<self.protocolclass.NUMPHONEBOOKENTRIES and uslotsused.has_key(tryuslot): 936 tryuslot += 1 937 uslotsused[tryuslot]=1 938 e['uslot'] = tryuslot 939 results[slot] = e 940 941 data['phonebook']=results 942 return data
943
944 - def phonize(self,str):
945 """Convert the phone number into something the phone understands 946 All digits, P, T, * and # are kept, everything else is removed""" 947 948 return re.sub("[^0-9PT#*]", "", str)[:self.protocolclass.MAXNUMBERLEN]
949
950 -class Samsung_Calendar:
951 _cal_alarm_values={ 952 10: 0, 30: 1, 60: 2, -1: 3, 0: 4 } 953
954 - def __init__(self, calendar_entry, new_date=None):
955 self._start=self._end=self._alarm=self._desc=self._desc_loc=None 956 self._extract_cal_info(calendar_entry, new_date)
957
958 - def _extract_cal_info(self, cal_entry, new_date):
959 s=cal_entry.start 960 if new_date is not None: 961 s=new_date[:3]+s[3:] 962 self._start=s 963 self._end=cal_entry.end 964 self._desc=cal_entry.description 965 self._desc_loc=cal_entry.desc_loc 966 # approximate the alarm value 967 self._alarm=0 968 alarm=cal_entry.alarm 969 _keys=self._cal_alarm_values.keys() 970 _keys.sort() 971 _keys.reverse() 972 for k in _keys: 973 if alarm>=k: 974 self._alarm=self._cal_alarm_values[k] 975 break
976
977 - def __lt__(self, rhs):
978 return self.start<rhs.start
979 - def __le__(self, rhs):
980 return self.start<=rhs.start
981 - def __eq__(self, rhs):
982 return self.start==rhs.start
983 - def __ne__(self, rhs):
984 return self.start!=rhs.start
985 - def __gt__(self, rhs):
986 return self.start>rhs.start
987 - def __ge__(self, rhs):
988 return self.start>=rhs.start
989
990 - def _get_start(self):
991 return self._start
992 start=property(fget=_get_start) 993
994 - def _get_end(self):
995 return self._end
996 end=property(fget=_get_end) 997
998 - def _get_desc(self):
999 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