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

Source Code for Module phones.com_sanyo

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2003-2007 Stephen Wood <sawecw@users.sf.net> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: com_sanyo.py 4785 2010-01-15 07:03:24Z hjelmn $ 
   9   
  10  """Phonebook conversations with Sanyo phones""" 
  11   
  12  # standard modules 
  13  import re 
  14  import time 
  15  import cStringIO 
  16  import sha 
  17  import datetime 
  18   
  19  # BitPim modules 
  20  import com_brew 
  21  import com_phone 
  22  import p_sanyo 
  23  import prototypes 
  24  import common 
  25  import conversions 
  26  import bpcalendar 
  27  import call_history 
  28  import sms 
  29  import todo 
  30  import helpids 
  31   
  32  numbertypetab=( 'home', 'office', 'cell', 'pager', 
  33                      'data', 'fax', 'none' ) 
  34   
35 -class SanyoCommandException(Exception):
36 - def __init__(self, errnum, str=None):
37 if str is None: 38 str="Sanyo Packet Error 0x%02x" % (errnum,) 39 Exception.__init__(self, str) 40 self.errnum=errnum
41
42 -class SanyoPhonebook:
43 "Talk to a Sanyo Sprint Phone such as SCP-4900, SCP-5300, or SCP-8100" 44 45 # phone uses Jan 1, 1980 as epoch. Python uses Jan 1, 1970. This is difference 46 # plus a fudge factor of 5 days for no reason I can find 47 _sanyoepochtounix=315532800+432000; 48 49 pbterminator="\x7e" 50 MODEPHONEBOOK="modephonebook" # can speak the phonebook protocol 51 52 calendar_defaultringtone=0 53 calendar_defaultcaringtone=0 54 calendar_voicenumber=0 55 phonebook_voicenumber=0 56
57 - def __init__(self):
59
60 - def get_esn(self):
61 req=self.protocolclass.esnrequest() 62 res=self.sendpbcommand(req, self.protocolclass.esnresponse) 63 return '%08X'%res.esn
64
65 - def _setmodephonebook(self):
66 self.setmode(self.MODEBREW) 67 req=p_sanyo.firmwarerequest() 68 respc=p_sanyo.firmwareresponse 69 try: 70 self.sendpbcommand(req, respc, callsetmode=False) 71 return 1 72 except com_phone.modeignoreerrortypes: 73 pass 74 try: 75 self.comm.setbaudrate(38400) 76 self.sendpbcommand(req, respc, callsetmode=False) 77 return 1 78 except com_phone.modeignoreerrortypes: 79 pass 80 return 0
81
82 - def getmediaindices(self, results):
83 """Get all the media indices 84 85 @param results: places results in this dict 86 """ 87 # Do all media types in one function to avoid having to scan through 88 # directories more than once 89 ringermedia={} 90 imagemedia={} 91 92 c=1 93 for name in self.builtinimages: 94 if name: 95 imagemedia[c]={'name': name, 'origin': 'builtin' } 96 # print c,name 97 c+=1 98 results['wallpaper-index']=imagemedia 99 c=1 100 for name in self.builtinringtones: 101 if name: 102 ringermedia[c]={'name': name, 'origin': 'builtin' } 103 # print c,name 104 c+=1 105 results['ringtone-index']=ringermedia 106 107 return 108 109 wallpaper = self.getsanyobuffer(self.protocolclass.wallpaperbuffer) 110 for i in range(0, self.protocolclass._NUMPBSLOTS): 111 if wallpaper.wallpapers[i].flag: 112 print i, wallpaper.wallpapers[i].word1, wallpaper.wallpapers[i].word2 113 114 req=self.protocolclass.bufferpartrequest() 115 116 req.header.command=0xc8 117 req.header.packettype=0x0c 118 res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 119 #req.header.command=0x69 120 #req.header.packettype=0x0f 121 #res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 122 #req.header.command=0x6a 123 #res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 124 #req.header.command=0x6b 125 #res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 126 127 req.header.packettype=0x0c 128 req.header.command=0x49 129 res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 130 req.header.command=0xc8 131 res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 132 133 if not self.serialsname == 'scp4900': 134 # 4900 doesn't like this 135 req.header.command=0xc9 136 res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse) 137 138 return
139
140 - def getmediaindex(self, builtins, maps, results, key):
141 """Gets the media (wallpaper/ringtone) index 142 143 @param builtins: the builtin list on the phone 144 @param results: places results in this dict 145 @param maps: the list of index files and locations 146 @param key: key to place results in 147 """ 148 149 self.log("Reading "+key) 150 media={} 151 152 # builtins 153 c=1 154 for name in builtins: 155 if name: 156 media[c]={'name': name, 'origin': 'builtin' } 157 # print c,name 158 c+=1 159 160 results[key]=media 161 return media
162
163 - def getwallpaperindices(self, results):
164 return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index')
165
166 - def getringtoneindices(self, results):
167 return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index')
168
169 - def getsanyobuffer(self, responseclass):
170 # Read buffer parts and concatenate them together 171 bufres=responseclass() 172 desc="Reading "+bufres.comment 173 data=cStringIO.StringIO() 174 bufp=0 175 command=bufres.startcommand 176 buffersize=bufres.bufsize 177 req=self.protocolclass.bufferpartrequest() 178 if command==0xd7: 179 req.header.packettype=0x0c # Special Case for 5500 Class phones 180 for offset in range(0, buffersize, req.bufpartsize): 181 self.progress(data.tell(), buffersize, desc) 182 req.header.command=command 183 res=self.sendpbcommand(req, self.protocolclass.bufferpartresponse); 184 data.write(res.data) 185 command+=1 186 187 self.progress(1,1,desc) 188 189 data=data.getvalue() 190 self.log("expected size "+`buffersize`+" actual "+`len(data)`) 191 assert buffersize==len(data) 192 buffer=prototypes.buffer(data) 193 bufres.readfrombuffer(buffer, logtitle="Sanyo buffer response") 194 return bufres
195
196 - def sendsanyobuffer(self, bufreq):
197 comment=bufreq.comment 198 buffer=prototypes.buffer() 199 bufreq.writetobuffer(buffer, logtitle="Send sanyo buffer") 200 buffer=buffer.getvalue() 201 self.log("Writing "+comment+" "+` len(buffer) `+" bytes") 202 desc="Writing "+comment 203 req=self.protocolclass.bufferpartupdaterequest() 204 bufpartsize=req.bufpartsize 205 numblocks=len(buffer)/bufpartsize 206 offset=0 207 command=bufreq.startcommand 208 if command==0xd7: 209 req.header.packettype=0x0c 210 for offset in range(0, len(buffer), bufpartsize): 211 self.progress(offset/bufpartsize, numblocks, desc) 212 req.header.command=command 213 block=buffer[offset:] 214 l=min(len(block), bufpartsize) 215 block=block[:l] 216 req.data=block 217 command+=1 218 self.sendpbcommand(req, self.protocolclass.bufferpartresponse, writemode=True)
219
220 - def sendpbcommand(self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False):
221 if writemode: 222 numretry=3 223 else: 224 numretry=0 225 226 if callsetmode: 227 self.setmode(self.MODEPHONEBOOK) 228 buffer=prototypes.buffer() 229 230 request.writetobuffer(buffer, logtitle="Sanyo phonebook request") 231 data=buffer.getvalue() 232 firsttwo=data[:2] 233 data=common.pppescape(data+common.crcs(data))+common.pppterminator 234 isendretry=numsendretry 235 while isendretry>=0: 236 try: 237 rdata=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry) 238 break 239 except com_phone.modeignoreerrortypes: 240 if isendretry>0: 241 self.log("Resending request packet...") 242 time.sleep(0.3) 243 else: 244 self.comm.success=False 245 self.mode=self.MODENONE 246 self.raisecommsdnaexception("manipulating the phonebook") 247 isendretry-=1 248 249 self.comm.success=True 250 251 origdata=rdata 252 # sometimes there is junk at the beginning, eg if the user 253 # turned off the phone and back on again. So if there is more 254 # than one 7e in the escaped data we should start after the 255 # second to last one 256 d=rdata.rfind(common.pppterminator,0,-1) 257 if d>=0: 258 self.log("Multiple Sanyo packets in data - taking last one starting at "+`d+1`) 259 self.logdata("Original Sanyo data", origdata, None) 260 rdata=rdata[d+1:] 261 262 # turn it back to normal 263 data=common.pppunescape(rdata) 264 265 # Sometimes there is other crap at the beginning. But it might 266 # be a Sanyo error byte. So strip off bytes from the beginning 267 # until the crc agrees, or we get to the first two bytes of the 268 # request packet. 269 d=data.find(firsttwo) 270 crc=data[-3:-1] 271 crcok=False 272 for i in range(0,d+1): 273 trydata=data[i:-3] 274 if common.crcs(trydata)==crc: 275 crcok=True 276 break 277 278 if not crcok: 279 self.logdata("first two",firsttwo, None) 280 self.logdata("Original Sanyo data", origdata, None) 281 self.logdata("Working on Sanyo data", data, None) 282 raise common.CommsDataCorruption("Sanyo packet failed CRC check", self.desc) 283 284 res=responseclass() 285 if d>0: 286 if d==i: 287 self.log("Junk at beginning of Sanyo packet, data at "+`d`) 288 self.logdata("Original Sanyo data", origdata, None) 289 self.logdata("Working on Sanyo data", data, None) 290 else: 291 if returnerror: 292 res=self.protocolclass.sanyoerror() 293 else: 294 self.log("Sanyo Error code "+`ord(data[0])`) 295 self.logdata("sanyo phonebook response", data, None) 296 raise SanyoCommandException(ord(data[0])) 297 298 data=trydata 299 300 # parse data 301 buffer=prototypes.buffer(data) 302 res.readfrombuffer(buffer, logtitle="sanyo phonebook response") 303 return res
304
305 - def getfundamentals(self, results):
306 """Gets information fundamental to interopating with the phone and UI.""" 307 308 # use a hash of ESN and other stuff (being paranoid) 309 print "HASRINGPICBUF=",self.protocolclass.HASRINGPICBUF 310 self.log("Retrieving fundamental phone information") 311 self.log("Phone serial number") 312 results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() 313 314 #self.getwallpaperindices(results) 315 #self.getringtoneindices(results) 316 self.getmediaindices(results) 317 self.log("Fundamentals retrieved") 318 return results
319
320 - def sanyosort(self, a, b):
321 "Sanyo sort order. Case insensitive, letters first" 322 x=a[1] 323 y=b[1] 324 # This is right only for first column 325 if(x[0:1].isalpha() and not y[0:1].isalpha()): 326 return -1 327 if(y[0:1].isalpha() and not x[0:1].isalpha()): 328 return 1 329 return cmp(x.lower(), y.lower())
330
331 - def getsms(self, result):
332 gsms = {} 333 self.log("Getting sms entries") 334 req=self.protocolclass.messagerequest() 335 for box in range(2): 336 if box==0: 337 req=self.protocolclass.messagerequest() 338 respc=self.protocolclass.messageresponse 339 else: 340 req=self.protocolclass.messagesentrequest() 341 respc=self.protocolclass.messagesentresponse 342 for slot in range(self.protocolclass.NUMMESSAGESLOTS): 343 req.slot=slot 344 res=self.sendpbcommand(req, respc) 345 if res.entry.dunno4==2: 346 entry=sms.SMSEntry() 347 entry.datetime="%04d%02d%02dT%02d%02d%02d" % ((2000 + res.entry.year, res.entry.month, res.entry.day, res.entry.hour,res.entry.minute, res.entry.second)) 348 if box==0: 349 entry.folder=entry.Folder_Inbox 350 entry._from=res.entry.phonenum 351 else: 352 entry.folder=entry.Folder_Sent 353 entry._to=res.entry.phonenum 354 #entry.add_recipient(res.entry.phonenum,confirmed=True) 355 if res.entry.read==17: 356 entry.read=1 357 else: 358 entry.read=0 359 entry.callback=res.entry.callback 360 entry.subject=res.entry.message[:12] 361 self.log(res.entry.message[:8]) 362 if res.entry.priority==100: 363 entry.priority=sms.SMSEntry.Priority_Normal 364 if res.entry.priority==200: 365 entry.priority=sms.SMSEntry.Priority_High 366 entry.text=res.entry.message 367 gsms[entry.id]=entry 368 result['sms']=gsms 369 return result
370
371 - def gettodo(self, result):
372 gtodo = {} 373 self.log("Getting todo entries") 374 req=self.protocolclass.todorequest() 375 for slot in range(self.protocolclass.NUMTODOSLOTS): 376 req.slot=slot 377 res=self.sendpbcommand(req, self.protocolclass.todoresponse) 378 entry=todo.TodoEntry() 379 if res.entry.flag: 380 entry.summary=res.entry.todo 381 if res.entry.priority==2: 382 entry.status=4 383 if res.entry.priority==0: 384 entry.priority=5 385 if res.entry.priority==1: 386 entry.priority=1 387 gtodo[entry.id]=entry 388 result['todo']=gtodo 389 return result
390
391 - def getphonebook(self,result):
392 pbook={} 393 # Get Sort buffer so we know which of the 300 phone book slots 394 # are in use. 395 sortstuff = self.getsanyobuffer(self.protocolclass.pbsortbuffer) 396 397 # Get the ringer and wall paper assignments 398 if self.protocolclass.HASRINGPICBUF: 399 ringpic = self.getsanyobuffer(self.protocolclass.ringerpicbuffer) 400 401 speedslot=[] 402 speedtype=[] 403 for i in range(self.protocolclass._NUMSPEEDDIALS): 404 speedslot.append(sortstuff.speeddialindex[i].pbslotandtype & 0xfff) 405 numtype=(sortstuff.speeddialindex[i].pbslotandtype>>12)-1 406 if(numtype >= 0 and numtype <= len(self.numbertypetab)): 407 speedtype.append(self.numbertypetab[numtype]) 408 else: 409 speedtype.append("") 410 411 numentries=sortstuff.slotsused 412 self.log("There are %d entries" % (numentries,)) 413 414 count = 0 # Number of phonebook entries 415 numcount = 0 # Number of phone numbers 416 numemail = 0 # Number of emails 417 numurl = 0 # Number of urls 418 res=self.protocolclass.phonebookentry() 419 usedefaultnum = 'defaultnum' in res.getfields() 420 print "usedefaultnum =",usedefaultnum 421 422 req=self.protocolclass.phonebookslotrequest() 423 for i in range(0, self.protocolclass._NUMPBSLOTS): 424 if sortstuff.usedflags[i].used: 425 ### Read current entry 426 req.slot = i 427 res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse) 428 self.log("Read entry "+`i`+" - "+res.entry.name) 429 430 entry=self.extractphonebookentry(res.entry, result) 431 # Speed dials 432 for j in range(len(speedslot)): 433 if(speedslot[j]==req.slot): 434 for k in range(len(entry['numbers'])): 435 if(entry['numbers'][k]['type']==speedtype[j]): 436 entry['numbers'][k]['speeddial']=j+2 437 break 438 439 if self.protocolclass.HASRINGPICBUF: 440 # ringtones 441 if ringpic.ringtones[i].ringtone>0: 442 print res.entry.name,ringpic.ringtones[i].ringtone 443 try: 444 tone=result['ringtone-index'][ringpic.ringtones[i].ringtone]['name'] 445 except: 446 tone=self.serialsname+"Index_"+`ringpic.ringtones[i].ringtone` 447 entry['ringtones']=[{'ringtone': tone, 'use': 'call'}] 448 449 # wallpapers 450 if ringpic.wallpapers[i].wallpaper>0: 451 try: 452 paper=result['wallpaper-index'][ringpic.wallpapers[i].wallpaper]['name'] 453 except: 454 paper=self.serialsname+"Index_"+`ringpic.wallpapers[i].wallpaper` 455 entry['wallpapers']=[{'wallpaper': paper, 'use': 'call'}] 456 457 # Set default number, swap with first number 458 if usedefaultnum: 459 firsttype=res.entry.defaultnum-1 460 if firsttype < len(self.numbertypetab): 461 defaulttype=self.numbertypetab[firsttype] 462 k=0 463 for j in range(len(entry['numbers'])): 464 if entry['numbers'][j]['type'] == defaulttype: 465 k=j 466 break 467 if k>0: 468 exchange=entry['numbers'][k] 469 for kk in range(k,0,-1): 470 entry['numbers'][kk]=entry['numbers'][kk-1] 471 entry['numbers'][0]=exchange 472 473 pbook[count]=entry 474 self.progress(count, numentries, res.entry.name) 475 count+=1 476 numcount+=len(entry['numbers']) 477 if entry.has_key('emails'): 478 numemail+=len(entry['emails']) 479 if entry.has_key('urls'): 480 numurl+=len(entry['urls']) 481 482 self.progress(numentries, numentries, "Phone book read completed") 483 self.log("Phone contains "+`count`+" contacts, "+`numcount`+" phone numbers, "+`numemail`+" Emails, "+`numurl`+" URLs") 484 result['phonebook']=pbook 485 return pbook
486
487 - def extractphonebookentry(self, entry, fundamentals):
488 """Return a phonebook entry in BitPim format""" 489 res={} 490 # serials 491 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.slot, 'serial2': entry.slotdup, 492 'sourceuniqueid': fundamentals['uniqueserial']} ] 493 # only one name 494 res['names']=[ {'full': entry.name} ] 495 # only one email 496 if len(entry.email): 497 res['emails']=[ {'email': entry.email} ] 498 # only one url 499 if len(entry.url): 500 res['urls']=[ {'url': entry.url} ] 501 # private 502 res['flags']=[ {'secret': entry.secret } ] 503 # 7 phone numbers 504 res['numbers']=[] 505 numberindex = 0 506 for type in self.numbertypetab: 507 if len(entry.numbers[numberindex].number): 508 res['numbers'].append({'number': entry.numbers[numberindex].number, 'type': type }) 509 510 numberindex+=1 511 return res
512
513 - def _findmediaindex(self, index, name, pbentryname, type):
514 if name is None: 515 return 0 516 for i in index: 517 if index[i]['name']==name: 518 return i 519 # Not found in index, assume Vision download 520 pos=name.find('_') 521 if(pos>=0): 522 i=int(name[pos+1:]) 523 return i 524 525 return 0
526
527 - def makeentry(self, entry, dict):
528 # dict is unused at moment, will be used later to convert string ringtone/wallpaper to numbers?? 529 # This is stolen from com_lgvx4400 and modified for the Sanyo as 530 # we start to develop a vague understanding of what it is for. 531 e=self.protocolclass.phonebookentry() 532 533 for k in entry: 534 # special treatment for lists 535 if k=='ringtones' or k=='wallpapers' or k=='numbertypes': 536 continue 537 if k=='numbers': 538 for numberindex in range(self.protocolclass.NUMPHONENUMBERS): 539 enpn=self.protocolclass.phonenumber() 540 e.numbers.append(enpn) 541 542 for i in range(len(entry[k])): 543 numberindex=entry['numbertypes'][i] 544 e.numbers[numberindex].number=entry[k][i] 545 e.numbers[numberindex].number_len=len(e.numbers[numberindex].number) 546 continue 547 # everything else we just set 548 setattr(e,k,entry[k]) 549 return e
550
551 - def writewait(self):
552 """Loop until phone status indicates ready to write""" 553 for i in range(100): 554 req=self.protocolclass.statusrequest() 555 res=self.sendpbcommand(req, self.protocolclass.statusresponse) 556 # print res.flag0, res.ready, res.flag2, res.flag3 557 if res.ready==res.readyvalue: 558 return 559 time.sleep(0.1) 560 561 self.log("Phone did not transfer to ready to write state") 562 self.log("Waiting a bit longer and trying anyway") 563 return
564
565 - def savephonebook(self, data):
566 # Overwrite the phonebook in the phone with the data. 567 # As we write the phone book slots out, we need to build up 568 # the indices in the callerid, ringpic and pbsort buffers. 569 # We could save some writing by reading the phonebook slots first 570 # and then only writing those that are different, but all the buffers 571 # would still need to be written. 572 # 573 574 newphonebook={} 575 self.mode=self.MODENONE 576 self.setmode(self.MODEBREW) # see note in getphonebook in com_lgvx4400 for why this is necessary 577 self.setmode(self.MODEPHONEBOOK) 578 579 ### 580 ### Create Sanyo buffers and Initialize lists 581 ### 582 sortstuff=self.protocolclass.pbsortbuffer() 583 ringpic=self.protocolclass.ringerpicbuffer() 584 callerid=self.protocolclass.calleridbuffer() 585 586 res=self.protocolclass.phonebookentry() 587 usedefaultnum = 'defaultnum' in res.getfields() 588 589 for i in range(self.protocolclass._NUMPBSLOTS): 590 sortstuff.usedflags.append(0) 591 sortstuff.firsttypes.append(0) 592 sortstuff.sortorder.append(0xffff) 593 sortstuff.sortorder2.append(0xffff) 594 sortstuff.emails.append(0xffff) 595 sortstuff.urls.append(0xffff) 596 ringpic.ringtones.append(0) 597 ringpic.wallpapers.append(0) 598 599 for i in range(self.protocolclass._NUMSPEEDDIALS): 600 sortstuff.speeddialindex.append(0xffff) 601 602 for i in range(self.protocolclass._NUMLONGNUMBERS): 603 sortstuff.longnumbersindex.append(0xffff) 604 605 606 ### 607 ### Initialize mappings 608 ### 609 namemap=[] 610 emailmap=[] 611 urlmap=[] 612 613 callerid.numentries=0 614 615 pbook=data['phonephonebook'] # Get converted phonebook 616 self.log("Putting phone into write mode") 617 req=self.protocolclass.beginendupdaterequest() 618 req.beginend=1 # Start update 619 res=self.sendpbcommand(req, self.protocolclass.beginendupdateresponse, writemode=True) 620 621 self.writewait() 622 623 keys=pbook.keys() 624 keys.sort() 625 sortstuff.slotsused=len(keys) 626 sortstuff.numemail=0 627 sortstuff.numurl=0 628 629 progresscur=0 630 progressmax=len(keys) 631 self.log("Writing %d entries to phone" % (len(keys),)) 632 nlongphonenumbers=0 633 nonumbercount=0 634 for ikey in keys: 635 ii=pbook[ikey] 636 slot=ii['slot'] # Or should we just use i for the slot 637 # Will depend on Profile to keep the serial numbers in range 638 progresscur+=1 639 self.progress(progresscur, progressmax, "Writing "+ii['name']) 640 self.log("Writing entry "+`slot`+" - "+ii['name']) 641 entry=self.makeentry(ii, data) 642 if not usedefaultnum: 643 delattr(entry,'defaultnum') 644 req=self.protocolclass.phonebookslotupdaterequest() 645 req.entry=entry 646 res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse, writemode=True) 647 # Put entry into newphonebooks 648 entry=self.extractphonebookentry(entry, data) 649 entry['ringtones']=[{'ringtone': ii['ringtone'], 'use': 'call'}] 650 entry['wallpapers']=[{'wallpaper': ii['wallpaper'], 'use': 'call'}] 651 652 653 # Accumulate information in and needed for buffers 654 sortstuff.usedflags[slot].used=1 655 if(len(ii['numbers'])): 656 sortstuff.firsttypes[slot].firsttype=min(ii['numbertypes'])+1 657 else: 658 if(len(ii['email'])): 659 sortstuff.firsttypes[slot].firsttype=8 660 nonumbercount+=1 661 elif(len(ii['url'])): 662 sortstuff.firsttypes[slot].firsttype=9 663 nonumbercount+=1 664 else: 665 sortstuff.firsttypes[slot].firsttype=0 666 667 # Fill in Caller ID buffer 668 # Want to move this out of this loop. Callerid buffer is 500 numbers, but 669 # can potentially hold 2100 numbers. Would like to preferentially put the 670 # first number for each name in this buffer. 671 # If more than 500 numbers are in phone, the phone won't let you add 672 # any more. So we should probably respect this limit. 673 for i in range(len(ii['numbers'])): 674 nindex=ii['numbertypes'][i] 675 speeddial=ii['speeddials'][i] 676 code=slot+((nindex+1)<<12) 677 if(speeddial>=2 and speeddial<=self.protocolclass._NUMSPEEDDIALS+1): 678 sortstuff.speeddialindex[speeddial-2]=code 679 for k in range(len(entry['numbers'])): 680 if(entry['numbers'][k]['type']==nindex): 681 entry['numbers'][k]['speeddial']=speeddial 682 break 683 if(callerid.numentries<callerid.maxentries): 684 phonenumber=ii['numbers'][i] 685 cidentry=self.makecidentry(phonenumber,slot,nindex) 686 callerid.items.append(cidentry) 687 callerid.numentries+=1 688 if(len(phonenumber)>self.protocolclass._LONGPHONENUMBERLEN): 689 if(nlongphonenumbers<self.protocolclass._NUMLONGNUMBERS): 690 sortstuff.longnumbersindex[nlongphonenumbers].pbslotandtype=code 691 692 namemap.append((slot,ii['name'])) 693 if(len(ii['email'])): 694 emailmap.append((slot,ii['email'])) 695 sortstuff.numemail+=1 696 if(len(ii['url'])): 697 urlmap.append((slot,ii['url'])) 698 sortstuff.numurl+=1 699 # Add ringtone and wallpaper 700 ringpic.ringtones[slot].ringtone=self._findmediaindex(data['ringtone-index'], ii['ringtone'],ii['name'],'ringtone') 701 ringpic.wallpapers[slot].wallpaper=self._findmediaindex(data['wallpaper-index'], ii['wallpaper'],ii['name'],'wallpaper') 702 703 newphonebook[slot]=entry 704 705 sortstuff.slotsused2=len(keys)-nonumbercount 706 # Sort Names, Emails and Urls for the sort buffer 707 # The phone sorts case insensitive and puts numbers after the 708 # letters. 709 i=0 710 j=0 711 sortstuff.pbfirstletters="" 712 namemap.sort(self.sanyosort) 713 for (slot, name) in namemap: 714 sortstuff.sortorder[i].pbslot=slot 715 if sortstuff.firsttypes[slot].firsttype<=7: 716 sortstuff.sortorder2[j].pbslot=slot 717 j+=1 718 if name: 719 sortstuff.pbfirstletters+=name[0] 720 else: 721 sortstuff.pbfirstletters+=chr(0) 722 i+=1 723 724 i=0 725 sortstuff.emailfirstletters="" 726 emailmap.sort(self.sanyosort) 727 for (slot, email) in emailmap: 728 sortstuff.emails[i].pbslot=slot 729 sortstuff.emailfirstletters+=email[0] 730 i+=1 731 732 i=0 733 sortstuff.urlfirstletters="" 734 urlmap.sort(self.sanyosort) 735 for (slot, url) in urlmap: 736 sortstuff.urls[i].pbslot=slot 737 sortstuff.urlfirstletters+=url[0] 738 i+=1 739 740 # Now write out the 3 buffers 741 self.sendsanyobuffer(sortstuff) 742 743 if self.protocolclass.HASRINGPICBUF: 744 self.sendsanyobuffer(ringpic) 745 746 self.sendsanyobuffer(callerid) 747 748 time.sleep(1.0) 749 750 data['phonebook']=newphonebook 751 del data['phonephonebook'] 752 data['rebootphone'] = 1
753
754 - def makecidentry(self, number, slot, nindex):
755 "Prepare entry for caller ID lookup buffer" 756 757 numstripped=re.sub("[^0-9PT#*]", "", number) 758 numonly=re.sub("^(\\d*).*$", "\\1", numstripped) 759 760 cidentry=self.protocolclass.calleridentry() 761 cidentry.pbslotandtype=slot+((nindex+1)<<12) 762 cidentry.actualnumberlen=len(numonly) 763 cidentry.numberfragment=numonly[-10:] 764 765 return cidentry
766 767
768 - def savewallpapers(self, results, merge):
769 # print "savewallpapers ",results['wallpaper-index'] 770 return self.savemedia('wallpapers', 'wallpaper-index', 'images', results, merge)
771 772 # Note: Was able to write 6 ringers to phone. If there are ringers 773 # already on phone, this counts against the 6. Don't know yet if the limit 774 # is a count limit or a byte limit.
775 - def saveringtones(self, results, merge):
776 return self.savemedia('ringtone', 'ringtone-index', 'ringers', results, merge)
777
778 - def savemedia(self, mediakey, mediaindexkey, mediatype, results, merge):
779 """Actually saves out the media 780 781 @param mediakey: key of the media (eg 'wallpapers' or 'ringtones') 782 Index of media in wallpaper or ringer tab 783 @param mediaindexkey: index key (eg 'wallpaper-index') 784 Index of media on the phone 785 @param results: results dict 786 """ 787 788 wp=results[mediakey].copy() 789 wpi=results[mediaindexkey].copy() 790 791 # remove builtins 792 for k in wpi.keys(): 793 if wpi[k]['origin']=='builtin': 794 del wpi[k] 795 796 # Remove camera pictures 797 for w in wp.keys(): 798 name=wp[w]['name'] 799 if wp[w].get("origin", "")=='camera': 800 self.log("Not transferring camera picture "+name+" to phone") 801 del wp[w] 802 else: 803 for k in wpi.keys(): 804 if name==wpi[k]['name']: 805 self.log("Not transferring "+name+" as it is already on the phone") 806 del wp[w] 807 break 808 809 init={} 810 init[mediatype]={} 811 idx=0 812 for w in wp.keys(): 813 idx-=1 814 data=wp[w]['data'] 815 name=wp[w]['name'] 816 init[mediatype][idx]={'name': name, 'data': data} 817 818 index=init[mediatype] 819 820 # write out the content 821 822 #### index is dict, key is index number, value is dict 823 #### value['name'] = filename 824 #### value['data'] is contents 825 826 errors=False 827 for key in index: 828 efile=index[key]['name'] 829 content=index[key]['data'] 830 if content is None: 831 continue # in theory we could rewrite .desc file in case index number has changed 832 # dirname=stripext(efile) 833 834 if mediatype=='images': 835 content = conversions.convertto8bitpng(content,16383) 836 if not self.writesanyofile(efile, content): 837 errors=True 838 839 if errors: 840 self.log("One or more files were rejected by the phone, due to") 841 self.log("invalid file type (only PNG or MIDI are allowed), file") 842 self.log("size too big, or too many ringers or wallpaper on the phone") 843 self.log("See Sanyo Error Codes section of the Sanyh Phone Notes") 844 self.log("in the help for more information") 845 846 # Note that we don't write to the camera area 847 848 # tidy up - reread indices 849 # del results[mediakey] # done with it 850 # reindexfunction(results) 851 return results
852 853 854 # Error codes 855 # 0x13 Unknown command 856 # 0x14 Bad media command 857 # 0x6d on sendfileterminator 858 # 0x65 not in sync mode on sendfilename 859 # 0x6a on sendfilefragment when buffer full, for ringers 6 max? 860 # 0x6b ?? 861 # 0x6c on sendfilefragment when full of pictures 11 max? 862 # 0x6d 863 # 0x69 on sendfilefragment. Invalid file type. PNG works, jpg, bmp don't 864 # 0x68 on sendfilefragment. Bad picture size
865 - def writesanyofile(self, name, contents):
866 start=time.time() 867 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 868 desc="Writing "+name 869 870 # strip .png and .mid from name because phone will add them 871 # back on 872 try: 873 name=name[:name.index(".mid")] 874 except: 875 try: 876 name=name[:name.index(".png")] 877 except: 878 pass 879 880 # The newer phones can be put in PC Sync mode with these commands 881 if hasattr(self.protocolclass,"sanyomediathingyrequest"): 882 req=self.protocolclass.sanyomediathingyrequest() 883 req.faset = 0x10 884 res=self.sendpbcommand(req, self.protocolclass.sanyomediathingyresponse) 885 time.sleep(10.0) 886 req.faset = 0x13 887 res=self.sendpbcommand(req, self.protocolclass.sanyomediathingyresponse) 888 889 req=self.protocolclass.sanyosendfilename() 890 req.filename=name 891 res=self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, returnerror=True) 892 if 'errorcode' in res.getfields(): 893 if res.errorcode==0x65: 894 self.alert("Please put your phone into PC Sync Mode", False) 895 else: 896 raise SanyoCommandException(res.errorcode) 897 # Wait about 5 minutes before giving up 898 waitcount=120 899 while waitcount>0: 900 time.sleep(1.0) 901 res=self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, returnerror=True) 902 if not 'errorcode' in res.getfields(): 903 break 904 waitcount-=1 905 if waitcount==0: 906 raise SanyoCommandException(res.errorcode) 907 908 req=self.protocolclass.sanyosendfilesize() 909 req.filesize=len(contents) 910 self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse) 911 912 req=self.protocolclass.sanyosendfilefragment() 913 packetsize=req.payloadsize 914 915 # time.sleep(1.0) 916 offset=0 917 count=0 918 numblocks=len(contents)/packetsize+1 919 for offset in range(0, len(contents), packetsize): 920 count+=1 921 if count % 5==0: 922 self.progress(count,numblocks,desc) 923 req.header.command=offset 924 req.data=contents[offset:min(offset+packetsize,len(contents))] 925 # self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, numsendretry=2) 926 self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, writemode=True, returnerror=True) 927 if 'errorcode' in res.getfields(): 928 self.log(name+" not written due to error code "+`res.errorcode`) 929 return False 930 931 req=self.protocolclass.sanyosendfileterminator() 932 try: 933 res=self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, writemode=True, returnerror=True) 934 # The returned value in res.header.faset may mean something 935 if 'errorcode' in res.getfields(): 936 self.log(name+" not written due to error code "+`res.errorcode`) 937 return False 938 except: 939 self.log("Exception on writing terminator for file "+name) 940 self.log("Continuing...") 941 942 end=time.time() 943 if end-start>3: 944 self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second") 945 return True
946 947
948 - def getcalendar(self,result):
949 # Read the event list from the phone. Proof of principle code. 950 # For now, join the event name and location into a single event. 951 # description. 952 # Todo: 953 # Read Call Alarms (reminder to call someone) 954 # Read call history into calendar. 955 # 956 calres={} 957 958 progressmax=self.protocolclass._NUMEVENTSLOTS+self.protocolclass._NUMCALLALARMSLOTS 959 req=self.protocolclass.eventrequest() 960 count=0 961 962 try: 963 reqflag=self.protocolclass.eventslotinuserequest() 964 except: 965 reqflag=0 966 967 for i in range(0, self.protocolclass._NUMEVENTSLOTS): 968 self.progress(i,progressmax,"Events") 969 if reqflag: 970 reqflag.slot=i 971 resflag=self.sendpbcommand(reqflag, self.protocolclass.eventslotinuseresponse) 972 if not resflag.flag: 973 continue 974 req.slot = i 975 res=self.sendpbcommand(req, self.protocolclass.eventresponse) 976 if not reqflag: 977 if not res.entry.flag: 978 continue 979 self.log("Read calendar event "+`i`+" - "+res.entry.eventname+", alarm ID "+`res.entry.ringtone`) 980 entry=bpcalendar.CalendarEntry() 981 #entry.pos=i 982 entry.changeserial=res.entry.serial 983 entry.description=res.entry.eventname 984 entry.location=res.entry.location 985 starttime=res.entry.start 986 entry.start=self.decodedate(starttime) 987 entry.end=self.decodedate(res.entry.end) 988 repeat=self._calrepeatvalues[res.entry.period] 989 entry.repeat = self.makerepeat(repeat,entry.start) 990 991 if res.entry.alarm==0xffffffff: 992 entry.alarm=res.entry.alarmdiff/60 993 else: 994 alarmtime=res.entry.alarm 995 entry.alarm=(starttime-alarmtime)/60 996 ringtone=res.entry.ringtone 997 print "ringtone=",ringtone 998 if self.calendar_voicenumber and ringtone == self.calendar_voicenumber: 999 ringtone=self.phonebook_voicenumber 1000 if ringtone in self.calendar_tonerange: 1001 print "Adjusting ringtone by",-self.calendar_toneoffset 1002 ringtone-=self.calendar_toneoffset 1003 print "ringtone=",ringtone 1004 if ringtone!=self.calendar_defaultringtone: 1005 if result['ringtone-index'].has_key(ringtone): 1006 entry.ringtone=result['ringtone-index'][ringtone]['name'] 1007 1008 entry.snoozedelay=0 1009 calres[entry.id]=entry 1010 count+=1 1011 1012 req=self.protocolclass.callalarmrequest() 1013 for i in range(0, self.protocolclass._NUMCALLALARMSLOTS): 1014 self.progress(self.protocolclass._NUMEVENTSLOTS,progressmax,"Call Alarms") 1015 req.slot=i 1016 res=self.sendpbcommand(req, self.protocolclass.callalarmresponse) 1017 if res.entry.flag and res.entry.date: 1018 self.log("Read call alarm entry "+`i`+" - "+res.entry.phonenum+", alarm ID "+`res.entry.ringtone`) 1019 entry=bpcalendar.CalendarEntry() 1020 #entry.pos=i+self.protocolclass._NUMEVENTSLOTS # Make unique 1021 entry.changeserial=res.entry.serial 1022 entry.description=res.entry.phonenum 1023 starttime=res.entry.date 1024 entry.start=self.decodedate(starttime) 1025 entry.end=entry.start 1026 repeat=self._calrepeatvalues[res.entry.period] 1027 entry.repeat = self.makerepeat(repeat,entry.start) 1028 entry.alarm=0 1029 if res.entry.ringtone!=self.calendar_defaultcaringtone: 1030 entry.ringtone=result['ringtone-index'][res.entry.ringtone]['name'] 1031 entry.snoozedelay=0 1032 calres[entry.id]=entry 1033 count+=1 1034 1035 result['calendar']=calres 1036 return result
1037
1038 - def makerepeat(self, repeatword, startdate):
1039 if repeatword is None: 1040 repeat_entry=None 1041 else: 1042 repeat_entry=bpcalendar.RepeatEntry() 1043 if repeatword=='daily': 1044 repeat_entry.repeat_type=repeat_entry.daily 1045 repeat_entry.interval=1 1046 elif repeatword=='monfri': 1047 repeat_entry.repeat_type=repeat_entry.daily 1048 repeat_entry.interval=0 1049 elif repeatword=='weekly': 1050 repeat_entry.repeat_type=repeat_entry.weekly 1051 repeat_entry.interval=1 1052 dow=datetime.date(*startdate[:3]).isoweekday()%7 1053 repeat_entry.dow=1<<dow 1054 elif repeatword=='monthly': 1055 repeat_entry.repeat_type=repeat_entry.monthly 1056 else: 1057 repeat_entry.repeat_type=repeat_entry.yearly 1058 1059 return repeat_entry
1060
1061 - def savecalendar(self, dict, merge):
1062 # ::TODO:: obey merge param 1063 # what will be written to the files 1064 # Handle Change Serial better. 1065 # Advance date on repeating entries to after now so that they 1066 # won't all go off when the phone gets turned on. 1067 # Sort by date so that that already happened entries don't get 1068 # loaded if we don't have room 1069 # 1070 cal=dict['calendar'] 1071 newcal={} 1072 keys=cal.keys() 1073 1074 # Value to subtract from mktime results since there is no inverse 1075 # of gmtime 1076 zonedif=time.mktime(time.gmtime(0))-time.mktime(time.localtime(0)) 1077 save_tonerange = xrange(self.calendar_tonerange[0]-self.calendar_toneoffset,self.calendar_tonerange[-1]-self.calendar_toneoffset+1) 1078 1079 try: 1080 reqflag=self.protocolclass.eventslotinuseupdaterequest() 1081 except: 1082 reqflag=0 1083 1084 eventslot=0 1085 callslot=0 1086 progressmax=self.protocolclass._NUMEVENTSLOTS+self.protocolclass._NUMCALLALARMSLOTS 1087 for k in keys: 1088 entry=cal[k] 1089 1090 descloc=entry.description 1091 self.progress(eventslot+callslot, progressmax, "Writing "+descloc) 1092 1093 rp=entry.repeat 1094 if rp is None: 1095 repeat=0 1096 else: 1097 repeatname=None 1098 if rp.repeat_type==rp.daily: 1099 repeatname='daily' 1100 elif rp.repeat_type==rp.weekly: 1101 repeatname='weekly' 1102 elif rp.repeat_type==rp.monthly: 1103 repeatname='monthly' 1104 elif rp.repeat_type==rp.yearly: 1105 repeatname='yearly' 1106 for k,v in self._calrepeatvalues.items(): 1107 if repeatname==v: 1108 repeat=k 1109 break 1110 if repeatname is None: 1111 self.log(descloc+": Repeat type "+`entry.repeat`+" not valid for this phone") 1112 repeat=0 1113 1114 phonenum=re.sub("\-","",descloc) 1115 now=time.mktime(time.localtime(time.time()))-zonedif 1116 if(phonenum.isdigit()): # This is a phone number, use call alarm 1117 self.log("Write calendar call alarm slot "+`callslot`+ " - "+descloc) 1118 e=self.protocolclass.callalarmentry() 1119 e.slot=callslot 1120 e.phonenum=phonenum 1121 e.phonenum_len=len(e.phonenum) 1122 1123 1124 timearray=list(entry.start)+[0,0,0,0] 1125 starttimelocal=time.mktime(timearray)-zonedif 1126 if(starttimelocal<now and repeat==0): 1127 e.flag=2 # In the past 1128 else: 1129 e.flag=1 # In the future 1130 e.date=starttimelocal-self._sanyoepochtounix 1131 e.datedup=e.date 1132 e.phonenumbertype=0 1133 e.phonenumberslot=0 1134 e.name="" # Could get this by reading phone book 1135 # But it would take a lot more time 1136 e.name_len=len(e.name) 1137 #e.ringtone=self.calendar_defaultcaringtone 1138 print "Setting ringtone "+`e.ringtone` 1139 1140 req=self.protocolclass.callalarmupdaterequest() 1141 callslot+=1 1142 respc=self.protocolclass.callalarmresponse 1143 else: # Normal calender event 1144 self.log("Write calendar event slot "+`eventslot`+ " - "+descloc) 1145 e=self.protocolclass.evententry() 1146 e.slot=eventslot 1147 1148 e.eventname=descloc 1149 e.eventname_len=len(e.eventname) 1150 e.location=entry.location 1151 e.location_len=len(e.location) 1152 1153 timearray=list(entry.start)+[0,0,0,0] 1154 starttimelocal=time.mktime(timearray)-zonedif 1155 e.start=starttimelocal-self._sanyoepochtounix 1156 1157 try: 1158 timearray=list(entry.end)+[0,0,0,0] 1159 endtimelocal=time.mktime(timearray)-zonedif 1160 e.end=endtimelocal-self._sanyoepochtounix 1161 except: # If no valid end date, make end 1162 e.end=e.start+60 # one minute later 1163 1164 alarmdiff=entry.alarm 1165 if alarmdiff<0: 1166 alarmdiff=0 1167 alarmdiff=max(alarmdiff,0)*60 1168 e.alarmdiff=alarmdiff 1169 e.alarm=starttimelocal-self._sanyoepochtounix-alarmdiff 1170 1171 if reqflag: 1172 reqflag.slot=eventslot 1173 if(e.alarm+self._sanyoepochtounix<now and repeat==0): 1174 reqflag.flag=4 # In the past 1175 else: 1176 reqflag.flag=1 # In the future 1177 res=self.sendpbcommand(reqflag, self.protocolclass.eventslotinuseresponse) 1178 else: 1179 if(starttimelocal<now and repeat==0): 1180 e.flag=2 # In the past 1181 else: 1182 e.flag=1 # In the future 1183 1184 #timearray=list(entry.get('end', entry['start']))+[0,0,0,0] 1185 #e.end=time.mktime(timearray)-self._sanyoepochtounix-zonedif 1186 try: 1187 timearray=list(entry.end)+[0,0,0,0] 1188 endtimelocal=time.mktime(timearray)-zonedif 1189 e.end=endtimelocal-self._sanyoepochtounix 1190 except: # If no valid end date, make end 1191 e.end=e.start+60 # one minute later 1192 1193 ringtoneindex=self._findmediaindex(dict['ringtone-index'],entry.ringtone,'Calendar','ringtone') 1194 if ringtoneindex==0: 1195 e.ringtone=self.calendar_defaultringtone 1196 else: 1197 e.ringtone=ringtoneindex 1198 if e.ringtone in save_tonerange: 1199 e.ringtone+=self.calendar_toneoffset 1200 if self.calendar_voicenumber and e.ringtone==self.phonebook_voicenumber: 1201 e.ringtone=self.calendar_voicenumber 1202 # Use default ringtone for now 1203 #e.ringtone=self.calendar_defaultringtone 1204 print "Setting ringtone "+`e.ringtone` 1205 1206 # What we should do is first find largest changeserial, and then increment 1207 # whenever we have one that is undefined or zero. 1208 1209 req=self.protocolclass.eventupdaterequest() 1210 eventslot+=1 1211 respc=self.protocolclass.eventresponse 1212 1213 e.period=repeat 1214 e.dom=entry.start[2] 1215 if entry.id>=0 and entry.id<256: 1216 e.serial=entry.id 1217 else: 1218 e.serial=0 1219 req.entry=e 1220 res=self.sendpbcommand(req, respc, writemode=True) 1221 1222 1223 # Blank out unused slots 1224 1225 if reqflag: 1226 req=reqflag 1227 req.flag=0 1228 else: 1229 e=self.protocolclass.evententry() 1230 e.flag=0 # Unused slot 1231 e.eventname="" 1232 e.eventname_len=0 1233 e.location="" 1234 e.location_len=0 1235 e.start=0 1236 e.end=0 1237 e.period=0 1238 e.dom=0 1239 e.ringtone=0 1240 e.alarm=0 1241 e.alarmdiff=0 1242 req=self.protocolclass.eventupdaterequest() 1243 req.entry=e 1244 1245 for eventslot in range(eventslot,self.protocolclass._NUMEVENTSLOTS): 1246 self.progress(eventslot+callslot, progressmax, "Writing unused") 1247 self.log("Write calendar event slot "+`eventslot`+ " - Unused") 1248 if reqflag: 1249 req.slot=eventslot 1250 else: 1251 req.entry.slot=eventslot 1252 res=self.sendpbcommand(req, self.protocolclass.eventresponse, writemode=True) 1253 1254 e=self.protocolclass.callalarmentry() 1255 e.flag=0 # Unused slot 1256 e.name="" 1257 e.name_len=0 1258 e.phonenum="" 1259 e.phonenum_len=0 1260 e.date=0 1261 e.datedup=0 1262 e.period=0 1263 e.dom=0 1264 e.ringtone=0 1265 e.phonenumbertype=0 1266 e.phonenumberslot=0 1267 req=self.protocolclass.callalarmupdaterequest() 1268 req.entry=e 1269 for callslot in range(callslot,self.protocolclass._NUMCALLALARMSLOTS): 1270 self.progress(eventslot+callslot, progressmax, "Writing unused") 1271 self.log("Write calendar call alarm slot "+`callslot`+ " - Unused") 1272 req.entry.slot=callslot 1273 res=self.sendpbcommand(req, self.protocolclass.callalarmresponse, writemode=True) 1274 1275 self.progress(progressmax, progressmax, "Calendar write done") 1276 1277 dict['rebootphone'] = True 1278 return dict
1279
1280 - def getcallhistory(self, result):
1281 res={} 1282 self._readhistory(self.protocolclass.OUTGOING,'Outgoing',res) 1283 self._readhistory(self.protocolclass.MISSED,'Missed',res) 1284 self._readhistory(self.protocolclass.INCOMING,'Incoming',res) 1285 result['call_history']=res 1286 return result
1287
1288 - def _readhistory(self, type, folder, res):
1289 req=self.protocolclass.historyrequest() 1290 req.type=type 1291 for slot in range(self.protocolclass.NUMCALLHISTORY): 1292 req.slot=slot 1293 call=self.sendpbcommand(req, self.protocolclass.historyresponse) 1294 if call.entry.phonenum=='' and call.entry.name=='': 1295 continue 1296 entry=call_history.CallHistoryEntry() 1297 entry.folder=folder 1298 entry.name=call.entry.name 1299 entry.number=call.entry.phonenum 1300 entry.datetime=((call.entry.date)) 1301 res[entry.id]=entry
1302
1303 - def getphoneinfo(self, phone_info):
1304 self.log('Getting Phone Info') 1305 try: 1306 phone_info.append('ESN:', self.get_esn()) 1307 phone_info.append('Manufacturer:', 'Sanyo') 1308 req=self.protocolclass.lockcoderequest() 1309 try: 1310 res=self.sendpbcommand(req, self.protocolclass.lockcoderesponse) 1311 phone_info.append('Lock Code:', res.lockcode) 1312 except: 1313 pass 1314 1315 req=self.protocolclass.sanyofirmwarerequest() 1316 res=self.sendpbcommand(req, self.protocolclass.sanyofirmwareresponse) 1317 phone_info.append('Firmware Version:',res.firmware) 1318 if 'prl' in res.getfields(): 1319 phone_info.append('PRL:', res.prl) 1320 if 'phonemodel' in res.getfields(): 1321 phone_info.append('Model:', res.phonemodel) 1322 else: 1323 phone_info.append('Model:', self.desc) 1324 req=self.protocolclass.phonenumberrequest() 1325 res=self.sendpbcommand(req, self.protocolclass.phonenumberresponse) 1326 phone_info.append('Phone Number:', res.myphonenumber) 1327 1328 req=self.protocolclass.reconditionedrequest() 1329 res=self.sendpbcommand(req, self.protocolclass.reconditionedresponse) 1330 if res.reconditioned: 1331 recon='Yes' 1332 else: 1333 recon='No' 1334 phone_info.append('Reconditioned:',recon) 1335 except: 1336 pass 1337 1338 return
1339
1340 - def decodedate(self,val):
1341 """Unpack 32 bit value into date/time 1342 1343 @rtype: tuple 1344 @return: (year, month, day, hour, minute) 1345 """ 1346 return list(time.gmtime(val+self._sanyoepochtounix)[:5])
1347 1348 _calrepeatvalues={ 1349 0: None, 1350 1: 'daily', 1351 2: 'weekly', 1352 3: 'monthly', 1353 4: 'yearly' 1354 }
1355 1356
1357 -def phonize(str):
1358 """Convert the phone number into something the phone understands 1359 1360 All digits, P, T, * and # are kept, everything else is removed""" 1361 # Note: when looking at phone numbers on the phone, you will see 1362 # "H" instead of "P". However, phone saves this internally as "P". 1363 return re.sub("[^0-9PT#*]", "", str)
1364
1365 -class Profile(com_phone.Profile):
1366 serialsname='sanyo' 1367 1368 WALLPAPER_WIDTH=120 1369 WALLPAPER_HEIGHT=128 1370 OVERSIZE_PERCENTAGE=100 1371 1372 MAX_WALLPAPER_BASENAME_LENGTH=19 1373 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'" 1374 WALLPAPER_CONVERT_FORMAT="png" 1375 1376 MAX_RINGTONE_BASENAME_LENGTH=19 1377 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'" 1378 1379 # which usb ids correspond to us 1380 usbids=( ( 0x0474, 0x0701, 1), # VID=Sanyo, PID=4900 internal USB interface 1381 ) 1382 # which device classes we are. 1383 deviceclasses=("modem",) 1384 1385 BP_Calendar_Version=3 1386
1387 - def __init__(self):
1390 1391 # which sync types we deal with 1392 _supportedsyncs=( 1393 ('phonebook', 'read', None), # all phonebook reading 1394 ('calendar', 'read', None), # all calendar reading 1395 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1396 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1397 ('wallpaper', 'write', 'MERGE'), 1398 ('ringtone', 'write', 'MERGE'), 1399 ('call_history', 'read', None),# all call history list reading 1400 ('sms', 'read', None), 1401 ) 1402 ### 1403 1404 # Processes phone book for writing to Sanyo. But does not leave phone book 1405 # in a bitpim compatible format. Need to understand exactly what bitpim 1406 # is expecting the method to do. 1407
1408 - def convertphonebooktophone(self, helper, data):
1409 "Converts the data to what will be used by the phone" 1410 results={} 1411 1412 slotsused={} 1413 for pbentry in data['phonebook']: 1414 entry=data['phonebook'][pbentry] 1415 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', -1) 1416 if(serial1 >= 0 and serial1 < self.protocolclass._NUMPBSLOTS): 1417 slotsused[serial1]=1 1418 1419 lastunused=0 # One more than last unused slot 1420 1421 for pbentry in data['phonebook']: 1422 e={} # entry out 1423 entry=data['phonebook'][pbentry] # entry in 1424 try: 1425 try: 1426 e['name']=helper.getfullname(entry.get('names', []),1,1,16)[0] 1427 except: 1428 e['name']='' 1429 e['name_len']=len(e['name']) 1430 1431 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', -1) 1432 1433 if(serial1 >= 0 and serial1 < self.protocolclass._NUMPBSLOTS): 1434 e['slot']=serial1 1435 else: # A new entry. Must find unused slot 1436 while(slotsused.has_key(lastunused)): 1437 lastunused+=1 1438 if(lastunused >= self.protocolclass._NUMPBSLOTS): 1439 raise helper.ConversionFailed() 1440 e['slot']=lastunused 1441 slotsused[lastunused]=1 1442 1443 e['slotdup']=e['slot'] 1444 1445 e['email']=helper.makeone(helper.getemails(entry.get('emails', []),0,1,self.protocolclass._MAXEMAILLEN), "") 1446 e['email_len']=len(e['email']) 1447 1448 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,self.protocolclass._MAXEMAILLEN), "") 1449 e['url_len']=len(e['url']) 1450 # Could put memo in email or url 1451 1452 numbers=helper.getnumbers(entry.get('numbers', []),0,7) 1453 e['numbertypes']=[] 1454 e['numbers']=[] 1455 e['speeddials']=[] 1456 unusednumbers=[] # Hold duplicate types here 1457 typesused={} 1458 defaultnum=0 1459 for num in numbers: 1460 typename=num['type'] 1461 if(typesused.has_key(typename)): 1462 unusednumbers.append(num) 1463 continue 1464 typesused[typename]=1 1465 for typenum,tnsearch in enumerate(self.numbertypetab): 1466 if typename==tnsearch: 1467 if defaultnum==0: 1468 defaultnum=typenum+1 1469 number=phonize(num['number']) 1470 if len(number)>self.protocolclass._MAXNUMBERLEN: # get this number from somewhere sensible 1471 # :: TODO:: number is too long and we have to either truncate it or ignore it? 1472 number=number[:self.protocolclass._MAXNUMBERLEN] 1473 e['numbers'].append(number) 1474 if(num.has_key('speeddial')): 1475 e['speeddials'].append(num['speeddial']) 1476 else: 1477 e['speeddials'].append(-1) 1478 1479 e['numbertypes'].append(typenum) 1480 1481 break 1482 1483 # Now stick the unused numbers in unused types 1484 trytype=len(self.numbertypetab) 1485 for num in unusednumbers: 1486 while trytype>0: 1487 trytype-=1 1488 if not typesused.has_key(self.numbertypetab[trytype]): 1489 break 1490 else: 1491 break 1492 if defaultnum==0: 1493 defaultnum=trytype+1 1494 number=phonize(num['number']) 1495 if len(number)>self.protocolclass._MAXNUMBERLEN: # get this number from somewhere sensible 1496 # :: TODO:: number is too long and we have to either truncate it or ignore it? 1497 number=number[:self.protocolclass._MAXNUMBERLEN] 1498 e['numbers'].append(number) 1499 e['numbertypes'].append(trytype) 1500 if(num.has_key('speeddial')): 1501 e['speeddials'].append(num['speeddial']) 1502 else: 1503 e['speeddials'].append(-1) 1504 1505 if defaultnum==0: 1506 if e['url_len'] > 0: 1507 defaultnum=8 1508 elif e['email_len'] > 0: 1509 defaultnum=9 1510 1511 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 1512 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) 1513 1514 e['secret']=helper.getflag(entry.get('flags', []), 'secret', False) 1515 e['defaultnum']=defaultnum 1516 1517 results[pbentry]=e 1518 1519 except helper.ConversionFailed: 1520 #self.log("No Free Slot for "+e['name']) 1521 print "No Free Slot for "+e['name'] 1522 continue 1523 1524 data['phonephonebook']=results 1525 return data
1526 1527
1528 -class Phone(SanyoPhonebook,com_phone.Phone,com_brew.BrewProtocol):
1529 "Talk to a Sanyo Sprint Phone such as SCP-4900, SCP-5300, or SCP-8100" 1530 desc="Sanyo" 1531 helpid=helpids.ID_PHONE_SANYOOTHERS 1532 imagelocations=() 1533 1534 ringtonelocations=() 1535 1536 builtinimages=() 1537 1538 builtinringtones=() 1539
1540 - def __init__(self, logtarget, commport):
1541 "Call all the contructors and sets initial modes" 1542 com_phone.Phone.__init__(self, logtarget, commport) 1543 com_brew.BrewProtocol.__init__(self) 1544 SanyoPhonebook.__init__(self) 1545 self.log("Attempting to contact phone") 1546 self.mode=self.MODENONE
1547 1548 getwallpapers=None 1549 getringtones=None
1550