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

Source Code for Module phones.com_motov710

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org> 
  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_motov710.py 4600 2008-03-01 04:55:30Z djpham $ 
  9   
 10  """Communicate with Motorola phones using AT commands""" 
 11  # system modules 
 12  import datetime 
 13  import sha 
 14   
 15  # BitPim modules 
 16  import bpcalendar 
 17  import common 
 18  import commport 
 19  import com_brew 
 20  import com_moto_cdma 
 21  import fileinfo 
 22  import nameparser 
 23  import prototypes 
 24  import p_motov710 
 25  import helpids 
26 27 -class Phone(com_moto_cdma.Phone):
28 """ Talk to a Motorola V710 phone""" 29 desc='Moto-V710' 30 helpid=helpids.ID_PHONE_MOTOV710 31 protocolclass=p_motov710 32 serialsname='motov710' 33 34 builtinringtones=( 35 (0, ('Silent',)), 36 (5, ('Vibe Dot', 'Vibe Dash', 'Vibe Dot Dot', 'Vibe Dot Dash', 37 'Vibe Pulse')), 38 (11, ('Alert', 'Standard', 'Bells', 'Triads', 'Up and Down', 39 'Jitters', 'Upbeat')), 40 (22, ('Guitar Strings', 'High Impact')), 41 (30, ('Moonlit Haze', 'Nightlife', 'Wind Chime', 'Random', 42 'Bit & Bytes', 'Door Bell', 'Ding', 'One Moment', 'Provincial', 43 'Harmonics', 'Interlude', 'Snaggle', 'Cosmic', 'Gyroscope')), 44 (49, ('Chimes high', 'Chimes low', 'Ding', 'TaDa', 'Notify', 'Drum', 45 'Claps', 'Fanfare', 'Chord high', 'Chord low')) 46 ) 47
48 - def __init__(self, logtarget, commport):
49 com_moto_cdma.Phone.__init__(self, logtarget, commport) 50 self._group_count=None
51 52 # fundamentals stuff--------------------------------------------------------
53 - def _get_groups(self):
54 if self._group_count is None: 55 self._group_count=self.protocolclass.PB_TOTAL_GROUP 56 try: 57 _req=self.protocolclass.group_count_req() 58 _res=self.sendATcommand(_req, self.protocolclass.group_count_resp) 59 if len(_res)==1 and _res[0].countstring.startswith('(1-'): 60 self._group_count=int(_res[0].countstring[3:-1]) 61 self.log('Group Count: %d' % self._group_count) 62 except: 63 if __debug__: 64 raise 65 _req=self.protocolclass.read_group_req(end_index=self._group_count) 66 _res=self.sendATcommand(_req, self.protocolclass.read_group_resp) 67 res={} 68 for e in _res: 69 res[e.index]={ 'name': e.name, 'ringtone': e.ringtone } 70 return res
71
72 - def _save_groups(self, fundamentals):
73 """Save the Group(Category) data""" 74 # get the current group 75 _groups=fundamentals.get('groups', {}) 76 # remember the assigned ringtone name 77 _name2ringtone={} 78 for _key,_entry in _groups.items(): 79 _name2ringtone[_entry['name']]=_entry['ringtone'] 80 # setting up the list of existing group names 81 _keys=_groups.keys() 82 _keys.sort() 83 _group_list=[_groups[x]['name'] for x in _keys] 84 # new group names as reported by phonebook 85 _cats=fundamentals.get('categories', []) 86 if 'General' not in _cats: 87 # Make sure General is the 1st one in the list 88 _cats.append('General') 89 # build a new list of new group names 90 _new_list=[x for x in _group_list if x in _cats] 91 _new_list+=[x for x in _cats if x not in _group_list] 92 _new_group={} 93 for _idx,_entry in enumerate(_new_list): 94 _new_group[_idx+1]={ 'name': _entry, 95 'ringtone': _name2ringtone.get(_entry, None) } 96 _rt_name_index=fundamentals.get('ringtone-name-index', {}) 97 # deleting existing group entries 98 _req=self.protocolclass.read_group_req() 99 _res=self.sendATcommand(_req, self.protocolclass.read_group_resp) 100 _req=self.protocolclass.del_group_req() 101 for e in _res: 102 if e.index==1: # Group 'General': can't delete, add, or modify 103 continue 104 _req.index=e.index 105 self.sendATcommand(_req, None) 106 # and save the new ones 107 _req=self.protocolclass.write_group_req() 108 for _key,_entry in _new_group.items(): 109 if _key==1: 110 continue 111 _req.index=_key 112 _req.name=_entry['name'] 113 _req.ringtone=_rt_name_index.get(_entry.get('ringtone', None), 255) 114 self.sendATcommand(_req, None) 115 fundamentals['groups']=_new_group
116
117 - def _get_ringtone_index(self):
118 res={} 119 # first the builtin ones 120 for _l in self.builtinringtones: 121 _idx=_l[0] 122 for _e in _l[1]: 123 res[_idx]={ 'name': _e, 'origin': 'builtin' } 124 _idx+=1 125 # now the custome one 126 _buf=prototypes.buffer(self.getfilecontents( 127 self.protocolclass.RT_INDEX_FILE)) 128 _idx_file=self.protocolclass.ringtone_index_file() 129 _idx_file.readfrombuffer(_buf, logtitle='Read ringtone index file') 130 for _entry in _idx_file.items: 131 _filename=self.decode_utf16(_entry.name) 132 res[_entry.index]={ 'name': common.basename(_filename), 133 'filename': _filename, 134 'type': _entry.ringtone_type, 135 'origin': 'ringers' } 136 return res
137
138 - def _get_wallpaper_index(self):
139 res={} 140 _files=self.listfiles(self.protocolclass.WP_PATH).keys() 141 _files.sort() 142 _wp_path_len=len(self.protocolclass.WP_PATH)+1 143 for _index,_filename in enumerate(_files): 144 _name=common.basename(_filename) 145 if _name not in self.protocolclass.WP_EXCLUDED_FILES: 146 res[_index]={ 'name': _name, 147 'filename': _filename, 148 'origin': 'images' } 149 return res
150 151 # phonebook stuff-----------------------------------------------------------
152 - def _populate_pb_misc(self, pb_entry, pb_sub_entry, key_name, 153 entry, fundamentals):
154 """Populate ringtone, wallpaper to a number, email, or mail list 155 """ 156 # any ringtones? 157 _rt_index=fundamentals.get('ringtone-index', {}) 158 _rt_name=_rt_index.get(entry.ringtone, {}).get('name', None) 159 if _rt_name: 160 pb_sub_entry['ringtone']=_rt_name 161 # any wallpaper 162 if entry.picture_name: 163 pb_sub_entry['wallpaper']=common.basename(entry.picture_name) 164 if entry.is_primary: 165 # primary entry: insert it to the front 166 pb_entry[key_name]=[pb_sub_entry]+pb_entry.get(key_name, []) 167 else: 168 # append it to the end 169 pb_entry.setdefault(key_name, []).append(pb_sub_entry)
170
171 - def _populate_pb_number(self, pb_entry, entry, fundamentals):
172 """extract the number into BitPim phonebook entry""" 173 _number_type=self.protocolclass.NUMBER_TYPE_NAME.get(entry.number_type, None) 174 _number={ 'number': entry.number, 'type': _number_type, 175 'speeddial': entry.index } 176 self._populate_pb_misc(pb_entry, _number, 'numbers', entry, 177 fundamentals) 178 # and mark it 179 fundamentals['sd_dict'][entry.index]=entry.number
180
181 - def _populate_pb_email(self, pb_entry, entry, fundamentals):
182 """Extract the email component""" 183 _email={ 'email': entry.number, 184 'speeddial': entry.index } 185 self._populate_pb_misc(pb_entry, _email, 'emails', entry, 186 fundamentals) 187 # and mark it 188 fundamentals['sd_dict'][entry.index]=entry.number
189
190 - def _populate_pb_maillist(self, pb_entry, entry, fundamentals):
191 """Extract the mailing list component""" 192 _num_list=entry.number.split(' ') 193 for _idx,_entry in enumerate(_num_list): 194 _num_list[_idx]=int(_entry) 195 _maillist={ 'entry': _num_list, 196 'speeddial': entry.index } 197 self._populate_pb_misc(pb_entry, _maillist, 'maillist', entry, 198 fundamentals) 199 # and mark it 200 fundamentals['sd_dict'][entry.index]=entry.number
201
202 - def _populate_pb_entry(self, pb_entry, entry, fundamentals):
203 """Populate a BitPim phonebook entry with one from the phone 204 """ 205 # extract the number, email, or mailing list 206 _num_type=entry.number_type 207 if _num_type in self.protocolclass.NUMBER_TYPE: 208 self._populate_pb_number(pb_entry, entry, fundamentals) 209 elif _num_type in self.protocolclass.EMAIL_TYPE: 210 self._populate_pb_email(pb_entry, entry, fundamentals)
211 # this is a mail list, which is not currently supported 212 ## else: 213 ## self._populate_pb_maillist(pb_entry, entry, fundamentals) 214
215 - def _build_pb_entry(self, entry, pb_book, fundamentals):
216 """Build a BitPim phonebook entry based on phone data. 217 """ 218 # check of this entry belong to an existing name 219 try: 220 _idx=fundamentals['pb_list'].index(entry.name) 221 except ValueError: 222 _idx=None 223 if _idx is None: 224 # new name entry 225 _idx=len(fundamentals['pb_list']) 226 fundamentals['pb_list'].append(entry.name) 227 _group=fundamentals.get('groups', {}).get(entry.group, None) 228 pb_book[_idx]={ 'names': [{ 'full': entry.name }] } 229 if _group.get('name', None): 230 pb_book[_idx]['categories']=[{'category': _group['name'] }] 231 self._populate_pb_entry(pb_book[_idx], entry, fundamentals)
232
233 - def _update_a_mail_list(self, entry, sd_dict):
234 for _entry in entry['maillist']: 235 _name_list=[] 236 for m in _entry['entry']: 237 if sd_dict.has_key(m): 238 _name_list.append(sd_dict[m]) 239 ## _entry['entry']=_name_list 240 _entry['entry']='\x00\x00'.join(_name_list)
241
242 - def _update_mail_list(self, pb_book, fundamentals):
243 """Translate the contents of each mail list from speed-dial 244 into the corresponding names or numbers. 245 """ 246 _sd_dict=fundamentals.get('sd_dict', {}) 247 for _key,_entry in pb_book.items(): 248 if _entry.has_key('maillist'): 249 self._update_a_mail_list(_entry, _sd_dict)
250
251 - def _del_pb_entry(self, entry_index):
252 """Delete the phonebook entry index from the phone""" 253 _req=self.protocolclass.del_pb_req() 254 _req.index=entry_index 255 try: 256 self.sendATcommand(_req, None) 257 except commport.ATError: 258 self.log('Failed to delete contact index %d'%entry_index) 259 except: 260 self.log('Failed to delete contact index %d'%entry_index) 261 if __debug__: 262 raise
263
264 - def _get_group_code(self, entry, fundamentals):
265 """Return the group index of the group. Return 1(General) if none found 266 """ 267 _grp_name=entry.get('categories', [{}])[0].get('category', None) 268 if not _grp_name: 269 return 1 270 for _key,_entry in fundamentals.get('groups', {}).items(): 271 if _entry.get('name', None)==_grp_name: 272 return _key 273 return 1
274
275 - def _get_ringtone_code(self, entry, fundamentals):
276 """Return the ringtone code of this entry""" 277 # check the global ringtone setting first 278 _ringtone_name=fundamentals.get('phoneringtone', None) 279 if not _ringtone_name: 280 _ringtone_name=entry.get('ringtone', None) 281 # and then individual number ringtone 282 if not _ringtone_name: 283 return 255 284 for _key,_entry in fundamentals.get('ringtone-index', {}).items(): 285 if _entry['name']==_ringtone_name: 286 return _key 287 return 255
288
289 - def _get_wallpaper_name(self, entry, fundamentals):
290 """Return the full path name for the wallpaper""" 291 # Check for global wallpaper setting first 292 _wp_name=fundamentals.get('phonewallpaper', None) 293 # then check for individual number ringtone 294 if not _wp_name: 295 _wp_name=entry.get('wallpaper', None) 296 if not _wp_name: 297 return '' 298 return '/a/'+self.protocolclass.WP_PATH+'/'+_wp_name
299
300 - def _get_primary_code(self, fundamentals):
301 if fundamentals['primary']: 302 return 0 303 fundamentals['primary']=True 304 return 1
305
306 - def _build_pb_maillist(self, entry, fundamentals):
307 """Translate the mail list from text name to indices""" 308 _sd_list=fundamentals.get('sd-slots', []) 309 _names_list=entry.get('entry', '').split('\x00\x00') 310 _codes_list=[] 311 for _name in _names_list: 312 try: 313 _codes_list.append('%d'%_sd_list.index(_name)) 314 except ValueError: 315 pass 316 return ' '.join(_codes_list)
317
318 - def _set_pb_entry_misc(self, req, entry, fundamentals):
319 """Set the ringtone, wallpaper, and primary parameters""" 320 req.ringtone=self._get_ringtone_code(entry, fundamentals) 321 req.is_primary=self._get_primary_code(fundamentals) 322 req.picture_name=self._get_wallpaper_name(entry, fundamentals)
323
324 - def _write_pb_entry_numbers(self, entry, req, fundamentals):
325 """Write all the numbers to the phone""" 326 req.local_type=self.protocolclass.LOCAL_TYPE_LOCAL 327 _cell1=False 328 for _entry in entry.get('numbers', []): 329 req.index=_entry['speeddial'] 330 if req.index>self.protocolclass.PB_TOTAL_ENTRIES: 331 # out of range 332 continue 333 req.number=_entry['number'] 334 req.number_type=self.protocolclass.NUMBER_TYPE_CODE.get( 335 _entry['type'], self.protocolclass.NUMBER_TYPE_WORK) 336 if req.number_type==self.protocolclass.NUMBER_TYPE_MOBILE: 337 if _cell1: 338 # this is cell2 339 req.number_type=self.protocolclass.NUMBER_TYPE_MOBILE2 340 else: 341 _cell1=True 342 self._set_pb_entry_misc(req, _entry, fundamentals) 343 self._del_pb_entry(req.index) 344 self.sendATcommand(req, None)
345
346 - def _write_pb_entry_emails(self, entry, req, fundamentals):
347 """Write all emails to the phone""" 348 req.number_type=self.protocolclass.NUMBER_TYPE_EMAIL 349 req.local_type=self.protocolclass.LOCAL_TYPE_UNKNOWN 350 _email1=False 351 for _entry in entry.get('emails', []): 352 req.index=_entry['speeddial'] 353 if req.index>self.protocolclass.PB_TOTAL_ENTRIES: 354 continue 355 req.number=_entry['email'] 356 if _email1: 357 # this is email2 358 req.number_type=self.protocolclass.NUMBER_TYPE_EMAIL2 359 else: 360 _email1=True 361 self._set_pb_entry_misc(req, _entry, fundamentals) 362 self._del_pb_entry(req.index) 363 self.sendATcommand(req, None)
364
365 - def _write_pb_entry_maillist(self, entry, req, fundamentals):
366 """Write all the mail lists to the phone""" 367 req.number_type=self.protocolclass.NUMBER_TYPE_MAILING_LIST 368 req.local_type=self.protocolclass.LOCAL_TYPE_UNKNOWN 369 for _entry in entry.get('maillist', []): 370 req.index=_entry['speeddial'] 371 if req.index>self.protcolclass.PB_TOTAL_ENTRIES: 372 continue 373 req.number=self._build_pb_maillist(_entry, fundamentals) 374 self._set_pb_entry_misc(req, _entry, fundamentals) 375 self._del_pb_entry(req.index) 376 self.sendATcommand(req, None)
377
378 - def _write_pb_entry(self, entry, fundamentals):
379 """Write an phonebook entry to the phone""" 380 _req=self.protocolclass.write_pb_req() 381 _req.name=nameparser.getfullname(entry['names'][0]) 382 _req.group=self._get_group_code(entry, fundamentals) 383 fundamentals['primary']=False 384 fundamentals['phonewallpaper']=entry.get('wallpapers', [{}])[0].get('wallpaper', None) 385 fundamentals['phoneringtone']=entry.get('ringtones', [{}])[0].get('ringtone', None) 386 # first, write out the numbers 387 self._write_pb_entry_numbers(entry, _req, fundamentals) 388 # then email 389 self._write_pb_entry_emails(entry, _req, fundamentals) 390 # and mail list 391 # self._write_pb_entry_maillist(entry, _req, fundamentals) 392 del fundamentals['primary'], fundamentals['phonewallpaper'], 393 fundamentals['phoneringtone']
394
395 - def _write_pb_entries(self, fundamentals):
396 """Write out the phonebook to the phone""" 397 _pb_book=fundamentals.get('phonebook', {}) 398 _total_entries=len(_pb_book) 399 _cnt=0 400 for _key,_entry in _pb_book.items(): 401 try: 402 _name=nameparser.getfullname(_entry['names'][0]) 403 except: 404 _name='<Unknown>' 405 _cnt+=1 406 self.progress(_cnt, _total_entries, 407 'Writing contact %d: %s'%(_cnt, _name)) 408 self._write_pb_entry(_entry, fundamentals) 409 # delete all unused slots 410 for _index,_entry in enumerate(fundamentals.get('sd-slots', [])): 411 if not _entry: 412 self.progress(_index, self.protocolclass.PB_TOTAL_ENTRIES, 413 'Deleting contact slot %d'%_index) 414 self._del_pb_entry(_index)
415 416 # Calendar stuff------------------------------------------------------------
417 - def _dow(self, ymd):
418 """Return a bitmap dayofweek""" 419 return 1<<(datetime.date(*ymd).isoweekday()%7)
420
421 - def _build_repeat_part(self, entry, calendar, fundamentals):
422 """Build and return a repeat object of this entry""" 423 _rep=None 424 _repeat_type=entry.repeat_type 425 if _repeat_type==self.protocolclass.CAL_REP_DAILY: 426 _rep=bpcalendar.RepeatEntry() 427 _rep.interval=1 428 elif _repeat_type==self.protocolclass.CAL_REP_WEEKLY: 429 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.weekly) 430 _rep.interval=1 431 elif _repeat_type==self.protocolclass.CAL_REP_MONTHLY: 432 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.monthly) 433 _rep.interval2=1 434 _rep.dow=0 435 elif _repeat_type==self.protocolclass.CAL_REP_MONTHLY_NTH: 436 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.monthly) 437 _rep.interval=_rep.get_nthweekday(entry.start_date) 438 _rep.interval2=1 439 _rep.dow=self._dow(entry.start_date) 440 elif _repeat_type==self.protocolclass.CAL_REP_YEARLY: 441 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.yearly) 442 443 return _rep
444
445 - def _build_regular_cal_entry(self, entry, calendar, fundamentals):
446 """ Build a regular BitPim entry frm phone data""" 447 _bp_entry=bpcalendar.CalendarEntry() 448 _bp_entry.id=`entry.index` 449 _bp_entry.desc_loc=entry.title 450 _bp_entry.start=entry.start_date+entry.start_time 451 _t0=datetime.datetime(*_bp_entry.start) 452 _t1=_t0+datetime.timedelta(minutes=entry.duration) 453 _bp_entry.end=(_t1.year, _t1.month, _t1.day, _t1.hour, _t1.minute) 454 if entry.alarm_timed and entry.alarm_enabled: 455 _t3=datetime.datetime(*(entry.alarm_date+entry.alarm_time)) 456 if _t0>=_t3: 457 _bp_entry.alarm=(_t0-_t3).seconds/60 458 # repeat 459 _rep=self._build_repeat_part(entry, calendar, fundamentals) 460 if _rep: 461 # this is a recurrent event, adjust the end date 462 _bp_entry.repeat=_rep 463 _bp_entry.end=bpcalendar.CalendarEntry.no_end_date+_bp_entry.end[3:] 464 465 calendar[_bp_entry.id]=_bp_entry
466
467 - def _process_exceptions(self, calendar):
468 """Process all exceptions""" 469 for _idx,_exc in calendar.get('exceptions', []): 470 if not calendar.has_key(`_idx`): 471 continue 472 _rep=calendar[`_idx`].repeat 473 if _rep: 474 _date=calendar[`_idx`].start[:3] 475 for _i in range(_exc): 476 _date=_rep.next_date(_date) 477 calendar[`_idx`].suppress_repeat_entry(*_date)
478
479 - def _build_cal_entry(self, entry, calendar, fundamentals):
480 """Build a BitPim calendar object from phonebook data""" 481 if hasattr(entry, 'title'): 482 # this is a regular entry 483 self._build_regular_cal_entry(entry, calendar, fundamentals) 484 else: 485 # this is an exception to a regular entry 486 calendar['exceptions'].append((entry.index, entry.ex_event))
487
488 - def _build_phone_repeat_entry(self, entry, calendar):
489 """Build the repeat part of this phone entry""" 490 _rep=calendar.repeat 491 if _rep: 492 #this is a repeat event 493 if _rep.repeat_type==_rep.daily: 494 entry.repeat_type=self.protocolclass.CAL_REP_DAILY 495 elif _rep.repeat_type==_rep.weekly: 496 entry.repeat_type=self.protocolclass.CAL_REP_WEEKLY 497 elif _rep.repeat_type==_rep.monthly: 498 if _rep.dow: 499 entry.repeat_type=self.protocolclass.CAL_REP_MONTHLY_NTH 500 else: 501 entry.repeat_type=self.protocolclass.CAL_REP_MONTHLY 502 else: 503 entry.repeat_type=self.protocolclass.CAL_REP_YEARLY 504 else: 505 entry.repeat_type=self.protocolclass.CAL_REP_NONE
506
507 - def _build_phone_alarm_entry(self, entry, calendar):
508 _alarm=calendar.alarm 509 if _alarm is None or _alarm==-1: 510 entry.alarm_timed=1 511 entry.alarm_enabled=0 512 entry.alarm_time=(0,0) 513 entry.alarm_date=(2000,0,0) 514 else: 515 entry.alarm_timed=1 516 entry.alarm_enabled=1 517 _d1=datetime.datetime(*calendar.start)-datetime.timedelta(minutes=_alarm) 518 entry.alarm_date=(_d1.year, _d1.month, _d1.day) 519 entry.alarm_time=(_d1.hour, _d1.minute)
520
521 - def _build_phone_entry(self, entry, calendar):
522 """Build a phone entry based on a BitPim calendar entry""" 523 entry.title=calendar.desc_loc 524 entry.start_time=calendar.start[3:] 525 entry.start_date=calendar.start[:3] 526 entry.duration=(datetime.datetime(*calendar.start[:3]+calendar.end[3:])- 527 datetime.datetime(*calendar.start)).seconds/60 528 self._build_phone_repeat_entry(entry, calendar) 529 self._build_phone_alarm_entry(entry, calendar)
530
531 - def _build_phone_exception_entry(self, entry, calendar, exceptions):
532 """Build a phone exception entry based on a BitPim entry""" 533 _rep=calendar.repeat 534 _end_date=calendar.end[:3] 535 _date=calendar.start[:3] 536 _ex_date=exceptions.get()[:3] 537 _cnt=0 538 while _date<=_end_date: 539 if _date==_ex_date: 540 entry.nth_event=_cnt 541 return True 542 _date=_rep.next_date(_date) 543 _cnt+=1 544 return False
545
546 - def _write_calendar_entries(self, fundamentals):
547 """Write the calendar entries to the phone""" 548 _calendar=fundamentals.get('calendar', {}) 549 _req=self.protocolclass.calendar_write_req() 550 _req_ex=self.protocolclass.calendar_write_ex_req() 551 _max_entry=self.protocolclass.CAL_MAX_ENTRY 552 _total_entries=len(_calendar) 553 _cal_cnt=0 554 for _,_cal in _calendar.items(): 555 if _cal_cnt>_max_entry:\ 556 # enough entries written 557 break 558 self._build_phone_entry(_req, _cal) 559 _req.index=_cal_cnt 560 self.progress(_cal_cnt, _total_entries, 561 'Writing event: %s'%_cal.description) 562 self.sendATcommand(_req, None) 563 if _cal.repeat: 564 for _ex in _cal.repeat.suppressed[:self.protocolclass.CAL_TOTAL_ENTRY_EXCEPTIONS]: 565 if self._build_phone_exception_entry(_req_ex, _cal, _ex): 566 _req_ex.index=_cal_cnt 567 self.sendATcommand(_req_ex, None) 568 _cal_cnt+=1 569 # and delete the rest 570 for _index in range(_cal_cnt, self.protocolclass.CAL_TOTAL_ENTRIES): 571 self.progress(_index, _total_entries, 572 'Deleting event #%d'%_index) 573 self.del_calendar_entry(_index)
574 575 # Ringtones stuff----------------------------------------------------------
576 - def _get_new_list(self, index_key, media_key, fundamentals):
577 """Return a list of media being replaced""" 578 _index=fundamentals.get(index_key, {}) 579 _media=fundamentals.get(media_key, {}) 580 _index_file_list=[_entry['name'] for _,_entry in _index.items() \ 581 if _entry.has_key('filename')] 582 _bp_file_list=[_entry['name'] for _,_entry in _media.items()] 583 return [x for x in _bp_file_list if x in _index_file_list]
584
585 - def _item_from_index(self, name, item_key, index_dict):
586 for _key,_entry in index_dict.items(): 587 if _entry.get('name', None)==name: 588 if item_key: 589 # return a field 590 return _entry.get(item_key, None) 591 else: 592 # return the key 593 return _key
594
595 - def _replace_files(self, index_key, media_key, 596 new_list, fundamentals):
597 """Replace existing media files with new contents""" 598 _index=fundamentals.get(index_key, {}) 599 _media=fundamentals.get(media_key, {}) 600 for _file in new_list: 601 _data=self._item_from_index(_file, 'data', _media) 602 if not _data: 603 self.log('Failed to write file %s due to no data'%_file) 604 continue 605 _file_name=self._item_from_index(_file, 'filename', _index) 606 if _file_name: 607 # existing file, check if the same one 608 _stat=self.statfile(_file_name) 609 if _stat and _stat['size']!=len(_data): 610 # and write out the data 611 try: 612 self.writefile(_file_name, _data) 613 except: 614 self.log('Failed to write file '+_file_name) 615 if __debug__: 616 raise
617
618 - def saveringtones(self, fundamentals, merge):
619 """Save ringtones to the phone""" 620 self.log('Writing ringtones to the phone') 621 self.setmode(self.MODEPHONEBOOK) 622 self.setmode(self.MODEBREW) 623 try: 624 _new_list=self._get_new_list('ringtone-index', 'ringtone', 625 fundamentals) 626 # replace files 627 self._replace_files('ringtone-index', 'ringtone', 628 _new_list, fundamentals) 629 except: 630 if __debug__: 631 self.setmode(self.MODEMODEM) 632 raise 633 self.setmode(self.MODEMODEM) 634 return fundamentals
635
636 - def savewallpapers(self, fundamentals, merge):
637 """Save wallpapers to the phone""" 638 self.log('Writing wallpapers to the phone') 639 self.setmode(self.MODEPHONEBOOK) 640 self.setmode(self.MODEBREW) 641 try: 642 _new_list=self._get_new_list('wallpaper-index', 'wallpapers', 643 fundamentals) 644 # replace files 645 self._replace_files('wallpaper-index', 'wallpapers', 646 _new_list, fundamentals) 647 except: 648 if __debug__: 649 self.setmode(self.MODEMODEM) 650 raise 651 self.setmode(self.MODEMODEM) 652 return fundamentals
653 654 # Phone Detection routine 655 @classmethod
656 - def detectphone(_, coms, likely_ports, res, _module, _log):
657 for port in likely_ports: 658 _model=res.get(port, {}).get('model', None) 659 if _model==_module.Profile.phone_model: 660 return port 661 elif _model==_module.Profile.generic_phone_model: 662 # this is a V710, try to get the actual model 663 try: 664 _comm=commport.CommConnection(_log, port) 665 _comm.sendatcommand('+MODE=2') 666 _comm.sendatcommand('') 667 _s=_comm.sendatcommand('+GMM')[0] 668 _comm.sendatcommand('+MODE=0') 669 _comm.close() 670 _model=_s.split(': ')[1].split(',')[-1].replace('"', '').split('=')[1] 671 if _model==_module.Profile.common_model_name: 672 _model=_module.Profile.phone_model 673 res[port]['model']=_model 674 if _model==_module.Profile.phone_model: 675 return port 676 except: 677 _comm.close() 678 if __debug__: 679 raise
680 681 #------------------------------------------------------------------------------ 682 parentprofile=com_moto_cdma.Profile
683 -class Profile(parentprofile):
684 685 serialsname=Phone.serialsname 686 687 WALLPAPER_WIDTH=176 688 WALLPAPER_HEIGHT=220 689 MAX_WALLPAPER_BASENAME_LENGTH=37 690 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()_ .-" 691 WALLPAPER_CONVERT_FORMAT="jpg" 692 693 # Motorola OEM USB Cable 694 usbids=( ( 0x22B8, 0x2A22, 1), 695 ( 0x22B8, 0x2A62, 1)) 696 deviceclasses=("modem",) 697 # use for auto-detection 698 phone_manufacturer='Motorola' 699 phone_model='V710 ' 700 common_model_name='V710' 701 generic_phone_model='Motorola CDMA v710 Phone' 702 703 # all dumped in "images" 704 imageorigins={} 705 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
706 - def GetImageOrigins(self):
707 return self.imageorigins
708 709 # our targets are the same for all origins 710 imagetargets={} 711 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 712 {'width': 176, 'height': 200, 'format': "JPEG"})) 713 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 714 {'width': 176, 'height': 140, 'format': "JPEG"})) 715 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 716 {'width': 176, 'height': 220, 'format': "JPEG"}))
717 - def GetTargetsForImageOrigin(self, origin):
718 return self.imagetargets
719
720 - def __init__(self):
721 parentprofile.__init__(self)
722 723 _supportedsyncs=( 724 ('phonebook', 'read', None), # all phonebook reading 725 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 726 ('calendar', 'read', None), # all calendar reading 727 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 728 ('ringtone', 'read', None), # all ringtone reading 729 ('ringtone', 'write', 'OVERWRITE'), 730 ('wallpaper', 'read', None), # all wallpaper reading 731 ('wallpaper', 'write', 'OVERWRITE'), 732 ('sms', 'read', None), # all SMS list reading DJP 733 ) 734
735 - def convertphonebooktophone(self, helper, data):
736 return data
737
738 - def QueryAudio(self, origin, currentextension, afi):
739 # we don't modify any of these 740 if afi.format in ("MIDI", "QCP", "PMD"): 741 return currentextension, afi 742 # examine mp3 743 if afi.format=="MP3": 744 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 745 return currentextension, afi 746 # convert it 747 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 48, 'samplerate': 44100}))
748 749 field_color_data={ 750 'phonebook': { 751 'name': { 752 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 753 'nickname': 0, 'details': 1 }, 754 'number': { 755 # not sure what the limit on these numbers 756 'type': True, 'speeddial': True, 'number': True, 757 'details': True, 758 'ringtone': True, 'wallpaper': True }, 759 'email': True, 760 'email_details': { 761 'emailspeeddial': True, 'emailringtone': True, 762 'emailwallpaper': True }, 763 'address': { 764 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 765 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 766 'details': 0 }, 767 'url': 0, 768 'memo': 0, 769 'category': 1, 770 'wallpaper': 1, 771 'ringtone': 1, 772 'storage': 0, 773 }, 774 'calendar': { 775 'description': True, 'location': True, 'allday': False, 776 'start': True, 'end': True, 'priority': False, 777 'alarm': True, 'vibrate': False, 778 'repeat': True, 779 'memo': False, 780 'category': False, 781 'wallpaper': False, 782 'ringtone': False, 783 }, 784 'memo': { 785 'subject': False, 786 'date': False, 787 'secret': False, 788 'category': False, 789 'memo': False, 790 }, 791 'todo': { 792 'summary': False, 793 'status': False, 794 'due_date': False, 795 'percent_complete': False, 796 'completion_date': False, 797 'private': False, 798 'priority': False, 799 'category': False, 800 'memo': False, 801 }, 802 }
803