0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2006 Joe Pham <djpham@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: com_motov710.py 4600 2008-03-01 04:55:30Z djpham $ 0009 0010 """Communicate with Motorola phones using AT commands""" 0011 # system modules 0012 import datetime 0013 import sha 0014 0015 # BitPim modules 0016 import bpcalendar 0017 import common 0018 import commport 0019 import com_brew 0020 import com_moto_cdma 0021 import fileinfo 0022 import nameparser 0023 import prototypes 0024 import p_motov710 0025 import helpids 0026 0027 class Phone(com_moto_cdma.Phone): 0028 """ Talk to a Motorola V710 phone""" 0029 desc='Moto-V710' 0030 helpid=helpids.ID_PHONE_MOTOV710 0031 protocolclass=p_motov710 0032 serialsname='motov710' 0033 0034 builtinringtones=( 0035 (0, ('Silent',)), 0036 (5, ('Vibe Dot', 'Vibe Dash', 'Vibe Dot Dot', 'Vibe Dot Dash', 0037 'Vibe Pulse')), 0038 (11, ('Alert', 'Standard', 'Bells', 'Triads', 'Up and Down', 0039 'Jitters', 'Upbeat')), 0040 (22, ('Guitar Strings', 'High Impact')), 0041 (30, ('Moonlit Haze', 'Nightlife', 'Wind Chime', 'Random', 0042 'Bit & Bytes', 'Door Bell', 'Ding', 'One Moment', 'Provincial', 0043 'Harmonics', 'Interlude', 'Snaggle', 'Cosmic', 'Gyroscope')), 0044 (49, ('Chimes high', 'Chimes low', 'Ding', 'TaDa', 'Notify', 'Drum', 0045 'Claps', 'Fanfare', 'Chord high', 'Chord low')) 0046 ) 0047 0048 def __init__(self, logtarget, commport): 0049 com_moto_cdma.Phone.__init__(self, logtarget, commport) 0050 self._group_count=None 0051 0052 # fundamentals stuff-------------------------------------------------------- 0053 def _get_groups(self): 0054 if self._group_count is None: 0055 self._group_count=self.protocolclass.PB_TOTAL_GROUP 0056 try: 0057 _req=self.protocolclass.group_count_req() 0058 _res=self.sendATcommand(_req, self.protocolclass.group_count_resp) 0059 if len(_res)==1 and _res[0].countstring.startswith('(1-'): 0060 self._group_count=int(_res[0].countstring[3:-1]) 0061 self.log('Group Count: %d' % self._group_count) 0062 except: 0063 if __debug__: 0064 raise 0065 _req=self.protocolclass.read_group_req(end_index=self._group_count) 0066 _res=self.sendATcommand(_req, self.protocolclass.read_group_resp) 0067 res={} 0068 for e in _res: 0069 res[e.index]={ 'name': e.name, 'ringtone': e.ringtone } 0070 return res 0071 0072 def _save_groups(self, fundamentals): 0073 """Save the Group(Category) data""" 0074 # get the current group 0075 _groups=fundamentals.get('groups', {}) 0076 # remember the assigned ringtone name 0077 _name2ringtone={} 0078 for _key,_entry in _groups.items(): 0079 _name2ringtone[_entry['name']]=_entry['ringtone'] 0080 # setting up the list of existing group names 0081 _keys=_groups.keys() 0082 _keys.sort() 0083 _group_list=[_groups[x]['name'] for x in _keys] 0084 # new group names as reported by phonebook 0085 _cats=fundamentals.get('categories', []) 0086 if 'General' not in _cats: 0087 # Make sure General is the 1st one in the list 0088 _cats.append('General') 0089 # build a new list of new group names 0090 _new_list=[x for x in _group_list if x in _cats] 0091 _new_list+=[x for x in _cats if x not in _group_list] 0092 _new_group={} 0093 for _idx,_entry in enumerate(_new_list): 0094 _new_group[_idx+1]={ 'name': _entry, 0095 'ringtone': _name2ringtone.get(_entry, None) } 0096 _rt_name_index=fundamentals.get('ringtone-name-index', {}) 0097 # deleting existing group entries 0098 _req=self.protocolclass.read_group_req() 0099 _res=self.sendATcommand(_req, self.protocolclass.read_group_resp) 0100 _req=self.protocolclass.del_group_req() 0101 for e in _res: 0102 if e.index==1: # Group 'General': can't delete, add, or modify 0103 continue 0104 _req.index=e.index 0105 self.sendATcommand(_req, None) 0106 # and save the new ones 0107 _req=self.protocolclass.write_group_req() 0108 for _key,_entry in _new_group.items(): 0109 if _key==1: 0110 continue 0111 _req.index=_key 0112 _req.name=_entry['name'] 0113 _req.ringtone=_rt_name_index.get(_entry.get('ringtone', None), 255) 0114 self.sendATcommand(_req, None) 0115 fundamentals['groups']=_new_group 0116 0117 def _get_ringtone_index(self): 0118 res={} 0119 # first the builtin ones 0120 for _l in self.builtinringtones: 0121 _idx=_l[0] 0122 for _e in _l[1]: 0123 res[_idx]={ 'name': _e, 'origin': 'builtin' } 0124 _idx+=1 0125 # now the custome one 0126 _buf=prototypes.buffer(self.getfilecontents( 0127 self.protocolclass.RT_INDEX_FILE)) 0128 _idx_file=self.protocolclass.ringtone_index_file() 0129 _idx_file.readfrombuffer(_buf, logtitle='Read ringtone index file') 0130 for _entry in _idx_file.items: 0131 _filename=self.decode_utf16(_entry.name) 0132 res[_entry.index]={ 'name': common.basename(_filename), 0133 'filename': _filename, 0134 'type': _entry.ringtone_type, 0135 'origin': 'ringers' } 0136 return res 0137 0138 def _get_wallpaper_index(self): 0139 res={} 0140 _files=self.listfiles(self.protocolclass.WP_PATH).keys() 0141 _files.sort() 0142 _wp_path_len=len(self.protocolclass.WP_PATH)+1 0143 for _index,_filename in enumerate(_files): 0144 _name=common.basename(_filename) 0145 if _name not in self.protocolclass.WP_EXCLUDED_FILES: 0146 res[_index]={ 'name': _name, 0147 'filename': _filename, 0148 'origin': 'images' } 0149 return res 0150 0151 # phonebook stuff----------------------------------------------------------- 0152 def _populate_pb_misc(self, pb_entry, pb_sub_entry, key_name, 0153 entry, fundamentals): 0154 """Populate ringtone, wallpaper to a number, email, or mail list 0155 """ 0156 # any ringtones? 0157 _rt_index=fundamentals.get('ringtone-index', {}) 0158 _rt_name=_rt_index.get(entry.ringtone, {}).get('name', None) 0159 if _rt_name: 0160 pb_sub_entry['ringtone']=_rt_name 0161 # any wallpaper 0162 if entry.picture_name: 0163 pb_sub_entry['wallpaper']=common.basename(entry.picture_name) 0164 if entry.is_primary: 0165 # primary entry: insert it to the front 0166 pb_entry[key_name]=[pb_sub_entry]+pb_entry.get(key_name, []) 0167 else: 0168 # append it to the end 0169 pb_entry.setdefault(key_name, []).append(pb_sub_entry) 0170 0171 def _populate_pb_number(self, pb_entry, entry, fundamentals): 0172 """extract the number into BitPim phonebook entry""" 0173 _number_type=self.protocolclass.NUMBER_TYPE_NAME.get(entry.number_type, None) 0174 _number={ 'number': entry.number, 'type': _number_type, 0175 'speeddial': entry.index } 0176 self._populate_pb_misc(pb_entry, _number, 'numbers', entry, 0177 fundamentals) 0178 # and mark it 0179 fundamentals['sd_dict'][entry.index]=entry.number 0180 0181 def _populate_pb_email(self, pb_entry, entry, fundamentals): 0182 """Extract the email component""" 0183 _email={ 'email': entry.number, 0184 'speeddial': entry.index } 0185 self._populate_pb_misc(pb_entry, _email, 'emails', entry, 0186 fundamentals) 0187 # and mark it 0188 fundamentals['sd_dict'][entry.index]=entry.number 0189 0190 def _populate_pb_maillist(self, pb_entry, entry, fundamentals): 0191 """Extract the mailing list component""" 0192 _num_list=entry.number.split(' ') 0193 for _idx,_entry in enumerate(_num_list): 0194 _num_list[_idx]=int(_entry) 0195 _maillist={ 'entry': _num_list, 0196 'speeddial': entry.index } 0197 self._populate_pb_misc(pb_entry, _maillist, 'maillist', entry, 0198 fundamentals) 0199 # and mark it 0200 fundamentals['sd_dict'][entry.index]=entry.number 0201 0202 def _populate_pb_entry(self, pb_entry, entry, fundamentals): 0203 """Populate a BitPim phonebook entry with one from the phone 0204 """ 0205 # extract the number, email, or mailing list 0206 _num_type=entry.number_type 0207 if _num_type in self.protocolclass.NUMBER_TYPE: 0208 self._populate_pb_number(pb_entry, entry, fundamentals) 0209 elif _num_type in self.protocolclass.EMAIL_TYPE: 0210 self._populate_pb_email(pb_entry, entry, fundamentals) 0211 # this is a mail list, which is not currently supported 0212 ## else: 0213 ## self._populate_pb_maillist(pb_entry, entry, fundamentals) 0214 0215 def _build_pb_entry(self, entry, pb_book, fundamentals): 0216 """Build a BitPim phonebook entry based on phone data. 0217 """ 0218 # check of this entry belong to an existing name 0219 try: 0220 _idx=fundamentals['pb_list'].index(entry.name) 0221 except ValueError: 0222 _idx=None 0223 if _idx is None: 0224 # new name entry 0225 _idx=len(fundamentals['pb_list']) 0226 fundamentals['pb_list'].append(entry.name) 0227 _group=fundamentals.get('groups', {}).get(entry.group, None) 0228 pb_book[_idx]={ 'names': [{ 'full': entry.name }] } 0229 if _group.get('name', None): 0230 pb_book[_idx]['categories']=[{'category': _group['name'] }] 0231 self._populate_pb_entry(pb_book[_idx], entry, fundamentals) 0232 0233 def _update_a_mail_list(self, entry, sd_dict): 0234 for _entry in entry['maillist']: 0235 _name_list=[] 0236 for m in _entry['entry']: 0237 if sd_dict.has_key(m): 0238 _name_list.append(sd_dict[m]) 0239 ## _entry['entry']=_name_list 0240 _entry['entry']='\x00\x00'.join(_name_list) 0241 0242 def _update_mail_list(self, pb_book, fundamentals): 0243 """Translate the contents of each mail list from speed-dial 0244 into the corresponding names or numbers. 0245 """ 0246 _sd_dict=fundamentals.get('sd_dict', {}) 0247 for _key,_entry in pb_book.items(): 0248 if _entry.has_key('maillist'): 0249 self._update_a_mail_list(_entry, _sd_dict) 0250 0251 def _del_pb_entry(self, entry_index): 0252 """Delete the phonebook entry index from the phone""" 0253 _req=self.protocolclass.del_pb_req() 0254 _req.index=entry_index 0255 try: 0256 self.sendATcommand(_req, None) 0257 except commport.ATError: 0258 self.log('Failed to delete contact index %d'%entry_index) 0259 except: 0260 self.log('Failed to delete contact index %d'%entry_index) 0261 if __debug__: 0262 raise 0263 0264 def _get_group_code(self, entry, fundamentals): 0265 """Return the group index of the group. Return 1(General) if none found 0266 """ 0267 _grp_name=entry.get('categories', [{}])[0].get('category', None) 0268 if not _grp_name: 0269 return 1 0270 for _key,_entry in fundamentals.get('groups', {}).items(): 0271 if _entry.get('name', None)==_grp_name: 0272 return _key 0273 return 1 0274 0275 def _get_ringtone_code(self, entry, fundamentals): 0276 """Return the ringtone code of this entry""" 0277 # check the global ringtone setting first 0278 _ringtone_name=fundamentals.get('phoneringtone', None) 0279 if not _ringtone_name: 0280 _ringtone_name=entry.get('ringtone', None) 0281 # and then individual number ringtone 0282 if not _ringtone_name: 0283 return 255 0284 for _key,_entry in fundamentals.get('ringtone-index', {}).items(): 0285 if _entry['name']==_ringtone_name: 0286 return _key 0287 return 255 0288 0289 def _get_wallpaper_name(self, entry, fundamentals): 0290 """Return the full path name for the wallpaper""" 0291 # Check for global wallpaper setting first 0292 _wp_name=fundamentals.get('phonewallpaper', None) 0293 # then check for individual number ringtone 0294 if not _wp_name: 0295 _wp_name=entry.get('wallpaper', None) 0296 if not _wp_name: 0297 return '' 0298 return '/a/'+self.protocolclass.WP_PATH+'/'+_wp_name 0299 0300 def _get_primary_code(self, fundamentals): 0301 if fundamentals['primary']: 0302 return 0 0303 fundamentals['primary']=True 0304 return 1 0305 0306 def _build_pb_maillist(self, entry, fundamentals): 0307 """Translate the mail list from text name to indices""" 0308 _sd_list=fundamentals.get('sd-slots', []) 0309 _names_list=entry.get('entry', '').split('\x00\x00') 0310 _codes_list=[] 0311 for _name in _names_list: 0312 try: 0313 _codes_list.append('%d'%_sd_list.index(_name)) 0314 except ValueError: 0315 pass 0316 return ' '.join(_codes_list) 0317 0318 def _set_pb_entry_misc(self, req, entry, fundamentals): 0319 """Set the ringtone, wallpaper, and primary parameters""" 0320 req.ringtone=self._get_ringtone_code(entry, fundamentals) 0321 req.is_primary=self._get_primary_code(fundamentals) 0322 req.picture_name=self._get_wallpaper_name(entry, fundamentals) 0323 0324 def _write_pb_entry_numbers(self, entry, req, fundamentals): 0325 """Write all the numbers to the phone""" 0326 req.local_type=self.protocolclass.LOCAL_TYPE_LOCAL 0327 _cell1=False 0328 for _entry in entry.get('numbers', []): 0329 req.index=_entry['speeddial'] 0330 if req.index>self.protocolclass.PB_TOTAL_ENTRIES: 0331 # out of range 0332 continue 0333 req.number=_entry['number'] 0334 req.number_type=self.protocolclass.NUMBER_TYPE_CODE.get( 0335 _entry['type'], self.protocolclass.NUMBER_TYPE_WORK) 0336 if req.number_type==self.protocolclass.NUMBER_TYPE_MOBILE: 0337 if _cell1: 0338 # this is cell2 0339 req.number_type=self.protocolclass.NUMBER_TYPE_MOBILE2 0340 else: 0341 _cell1=True 0342 self._set_pb_entry_misc(req, _entry, fundamentals) 0343 self._del_pb_entry(req.index) 0344 self.sendATcommand(req, None) 0345 0346 def _write_pb_entry_emails(self, entry, req, fundamentals): 0347 """Write all emails to the phone""" 0348 req.number_type=self.protocolclass.NUMBER_TYPE_EMAIL 0349 req.local_type=self.protocolclass.LOCAL_TYPE_UNKNOWN 0350 _email1=False 0351 for _entry in entry.get('emails', []): 0352 req.index=_entry['speeddial'] 0353 if req.index>self.protocolclass.PB_TOTAL_ENTRIES: 0354 continue 0355 req.number=_entry['email'] 0356 if _email1: 0357 # this is email2 0358 req.number_type=self.protocolclass.NUMBER_TYPE_EMAIL2 0359 else: 0360 _email1=True 0361 self._set_pb_entry_misc(req, _entry, fundamentals) 0362 self._del_pb_entry(req.index) 0363 self.sendATcommand(req, None) 0364 0365 def _write_pb_entry_maillist(self, entry, req, fundamentals): 0366 """Write all the mail lists to the phone""" 0367 req.number_type=self.protocolclass.NUMBER_TYPE_MAILING_LIST 0368 req.local_type=self.protocolclass.LOCAL_TYPE_UNKNOWN 0369 for _entry in entry.get('maillist', []): 0370 req.index=_entry['speeddial'] 0371 if req.index>self.protcolclass.PB_TOTAL_ENTRIES: 0372 continue 0373 req.number=self._build_pb_maillist(_entry, fundamentals) 0374 self._set_pb_entry_misc(req, _entry, fundamentals) 0375 self._del_pb_entry(req.index) 0376 self.sendATcommand(req, None) 0377 0378 def _write_pb_entry(self, entry, fundamentals): 0379 """Write an phonebook entry to the phone""" 0380 _req=self.protocolclass.write_pb_req() 0381 _req.name=nameparser.getfullname(entry['names'][0]) 0382 _req.group=self._get_group_code(entry, fundamentals) 0383 fundamentals['primary']=False 0384 fundamentals['phonewallpaper']=entry.get('wallpapers', [{}])[0].get('wallpaper', None) 0385 fundamentals['phoneringtone']=entry.get('ringtones', [{}])[0].get('ringtone', None) 0386 # first, write out the numbers 0387 self._write_pb_entry_numbers(entry, _req, fundamentals) 0388 # then email 0389 self._write_pb_entry_emails(entry, _req, fundamentals) 0390 # and mail list 0391 # self._write_pb_entry_maillist(entry, _req, fundamentals) 0392 del fundamentals['primary'], fundamentals['phonewallpaper'], 0393 fundamentals['phoneringtone'] 0394 0395 def _write_pb_entries(self, fundamentals): 0396 """Write out the phonebook to the phone""" 0397 _pb_book=fundamentals.get('phonebook', {}) 0398 _total_entries=len(_pb_book) 0399 _cnt=0 0400 for _key,_entry in _pb_book.items(): 0401 try: 0402 _name=nameparser.getfullname(_entry['names'][0]) 0403 except: 0404 _name='<Unknown>' 0405 _cnt+=1 0406 self.progress(_cnt, _total_entries, 0407 'Writing contact %d: %s'%(_cnt, _name)) 0408 self._write_pb_entry(_entry, fundamentals) 0409 # delete all unused slots 0410 for _index,_entry in enumerate(fundamentals.get('sd-slots', [])): 0411 if not _entry: 0412 self.progress(_index, self.protocolclass.PB_TOTAL_ENTRIES, 0413 'Deleting contact slot %d'%_index) 0414 self._del_pb_entry(_index) 0415 0416 # Calendar stuff------------------------------------------------------------ 0417 def _dow(self, ymd): 0418 """Return a bitmap dayofweek""" 0419 return 1<<(datetime.date(*ymd).isoweekday()%7) 0420 0421 def _build_repeat_part(self, entry, calendar, fundamentals): 0422 """Build and return a repeat object of this entry""" 0423 _rep=None 0424 _repeat_type=entry.repeat_type 0425 if _repeat_type==self.protocolclass.CAL_REP_DAILY: 0426 _rep=bpcalendar.RepeatEntry() 0427 _rep.interval=1 0428 elif _repeat_type==self.protocolclass.CAL_REP_WEEKLY: 0429 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.weekly) 0430 _rep.interval=1 0431 elif _repeat_type==self.protocolclass.CAL_REP_MONTHLY: 0432 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.monthly) 0433 _rep.interval2=1 0434 _rep.dow=0 0435 elif _repeat_type==self.protocolclass.CAL_REP_MONTHLY_NTH: 0436 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.monthly) 0437 _rep.interval=_rep.get_nthweekday(entry.start_date) 0438 _rep.interval2=1 0439 _rep.dow=self._dow(entry.start_date) 0440 elif _repeat_type==self.protocolclass.CAL_REP_YEARLY: 0441 _rep=bpcalendar.RepeatEntry(bpcalendar.RepeatEntry.yearly) 0442 0443 return _rep 0444 0445 def _build_regular_cal_entry(self, entry, calendar, fundamentals): 0446 """ Build a regular BitPim entry frm phone data""" 0447 _bp_entry=bpcalendar.CalendarEntry() 0448 _bp_entry.id=`entry.index` 0449 _bp_entry.desc_loc=entry.title 0450 _bp_entry.start=entry.start_date+entry.start_time 0451 _t0=datetime.datetime(*_bp_entry.start) 0452 _t1=_t0+datetime.timedelta(minutes=entry.duration) 0453 _bp_entry.end=(_t1.year, _t1.month, _t1.day, _t1.hour, _t1.minute) 0454 if entry.alarm_timed and entry.alarm_enabled: 0455 _t3=datetime.datetime(*(entry.alarm_date+entry.alarm_time)) 0456 if _t0>=_t3: 0457 _bp_entry.alarm=(_t0-_t3).seconds/60 0458 # repeat 0459 _rep=self._build_repeat_part(entry, calendar, fundamentals) 0460 if _rep: 0461 # this is a recurrent event, adjust the end date 0462 _bp_entry.repeat=_rep 0463 _bp_entry.end=bpcalendar.CalendarEntry.no_end_date+_bp_entry.end[3:] 0464 0465 calendar[_bp_entry.id]=_bp_entry 0466 0467 def _process_exceptions(self, calendar): 0468 """Process all exceptions""" 0469 for _idx,_exc in calendar.get('exceptions', []): 0470 if not calendar.has_key(`_idx`): 0471 continue 0472 _rep=calendar[`_idx`].repeat 0473 if _rep: 0474 _date=calendar[`_idx`].start[:3] 0475 for _i in range(_exc): 0476 _date=_rep.next_date(_date) 0477 calendar[`_idx`].suppress_repeat_entry(*_date) 0478 0479 def _build_cal_entry(self, entry, calendar, fundamentals): 0480 """Build a BitPim calendar object from phonebook data""" 0481 if hasattr(entry, 'title'): 0482 # this is a regular entry 0483 self._build_regular_cal_entry(entry, calendar, fundamentals) 0484 else: 0485 # this is an exception to a regular entry 0486 calendar['exceptions'].append((entry.index, entry.ex_event)) 0487 0488 def _build_phone_repeat_entry(self, entry, calendar): 0489 """Build the repeat part of this phone entry""" 0490 _rep=calendar.repeat 0491 if _rep: 0492 #this is a repeat event 0493 if _rep.repeat_type==_rep.daily: 0494 entry.repeat_type=self.protocolclass.CAL_REP_DAILY 0495 elif _rep.repeat_type==_rep.weekly: 0496 entry.repeat_type=self.protocolclass.CAL_REP_WEEKLY 0497 elif _rep.repeat_type==_rep.monthly: 0498 if _rep.dow: 0499 entry.repeat_type=self.protocolclass.CAL_REP_MONTHLY_NTH 0500 else: 0501 entry.repeat_type=self.protocolclass.CAL_REP_MONTHLY 0502 else: 0503 entry.repeat_type=self.protocolclass.CAL_REP_YEARLY 0504 else: 0505 entry.repeat_type=self.protocolclass.CAL_REP_NONE 0506 0507 def _build_phone_alarm_entry(self, entry, calendar): 0508 _alarm=calendar.alarm 0509 if _alarm is None or _alarm==-1: 0510 entry.alarm_timed=1 0511 entry.alarm_enabled=0 0512 entry.alarm_time=(0,0) 0513 entry.alarm_date=(2000,0,0) 0514 else: 0515 entry.alarm_timed=1 0516 entry.alarm_enabled=1 0517 _d1=datetime.datetime(*calendar.start)-datetime.timedelta(minutes=_alarm) 0518 entry.alarm_date=(_d1.year, _d1.month, _d1.day) 0519 entry.alarm_time=(_d1.hour, _d1.minute) 0520 0521 def _build_phone_entry(self, entry, calendar): 0522 """Build a phone entry based on a BitPim calendar entry""" 0523 entry.title=calendar.desc_loc 0524 entry.start_time=calendar.start[3:] 0525 entry.start_date=calendar.start[:3] 0526 entry.duration=(datetime.datetime(*calendar.start[:3]+calendar.end[3:])- 0527 datetime.datetime(*calendar.start)).seconds/60 0528 self._build_phone_repeat_entry(entry, calendar) 0529 self._build_phone_alarm_entry(entry, calendar) 0530 0531 def _build_phone_exception_entry(self, entry, calendar, exceptions): 0532 """Build a phone exception entry based on a BitPim entry""" 0533 _rep=calendar.repeat 0534 _end_date=calendar.end[:3] 0535 _date=calendar.start[:3] 0536 _ex_date=exceptions.get()[:3] 0537 _cnt=0 0538 while _date<=_end_date: 0539 if _date==_ex_date: 0540 entry.nth_event=_cnt 0541 return True 0542 _date=_rep.next_date(_date) 0543 _cnt+=1 0544 return False 0545 0546 def _write_calendar_entries(self, fundamentals): 0547 """Write the calendar entries to the phone""" 0548 _calendar=fundamentals.get('calendar', {}) 0549 _req=self.protocolclass.calendar_write_req() 0550 _req_ex=self.protocolclass.calendar_write_ex_req() 0551 _max_entry=self.protocolclass.CAL_MAX_ENTRY 0552 _total_entries=len(_calendar) 0553 _cal_cnt=0 0554 for _,_cal in _calendar.items(): 0555 if _cal_cnt>_max_entry:\ 0556 # enough entries written 0557 break 0558 self._build_phone_entry(_req, _cal) 0559 _req.index=_cal_cnt 0560 self.progress(_cal_cnt, _total_entries, 0561 'Writing event: %s'%_cal.description) 0562 self.sendATcommand(_req, None) 0563 if _cal.repeat: 0564 for _ex in _cal.repeat.suppressed[:self.protocolclass.CAL_TOTAL_ENTRY_EXCEPTIONS]: 0565 if self._build_phone_exception_entry(_req_ex, _cal, _ex): 0566 _req_ex.index=_cal_cnt 0567 self.sendATcommand(_req_ex, None) 0568 _cal_cnt+=1 0569 # and delete the rest 0570 for _index in range(_cal_cnt, self.protocolclass.CAL_TOTAL_ENTRIES): 0571 self.progress(_index, _total_entries, 0572 'Deleting event #%d'%_index) 0573 self.del_calendar_entry(_index) 0574 0575 # Ringtones stuff---------------------------------------------------------- 0576 def _get_new_list(self, index_key, media_key, fundamentals): 0577 """Return a list of media being replaced""" 0578 _index=fundamentals.get(index_key, {}) 0579 _media=fundamentals.get(media_key, {}) 0580 _index_file_list=[_entry['name'] for _,_entry in _index.items() \ 0581 if _entry.has_key('filename')] 0582 _bp_file_list=[_entry['name'] for _,_entry in _media.items()] 0583 return [x for x in _bp_file_list if x in _index_file_list] 0584 0585 def _item_from_index(self, name, item_key, index_dict): 0586 for _key,_entry in index_dict.items(): 0587 if _entry.get('name', None)==name: 0588 if item_key: 0589 # return a field 0590 return _entry.get(item_key, None) 0591 else: 0592 # return the key 0593 return _key 0594 0595 def _replace_files(self, index_key, media_key, 0596 new_list, fundamentals): 0597 """Replace existing media files with new contents""" 0598 _index=fundamentals.get(index_key, {}) 0599 _media=fundamentals.get(media_key, {}) 0600 for _file in new_list: 0601 _data=self._item_from_index(_file, 'data', _media) 0602 if not _data: 0603 self.log('Failed to write file %s due to no data'%_file) 0604 continue 0605 _file_name=self._item_from_index(_file, 'filename', _index) 0606 if _file_name: 0607 # existing file, check if the same one 0608 _stat=self.statfile(_file_name) 0609 if _stat and _stat['size']!=len(_data): 0610 # and write out the data 0611 try: 0612 self.writefile(_file_name, _data) 0613 except: 0614 self.log('Failed to write file '+_file_name) 0615 if __debug__: 0616 raise 0617 0618 def saveringtones(self, fundamentals, merge): 0619 """Save ringtones to the phone""" 0620 self.log('Writing ringtones to the phone') 0621 self.setmode(self.MODEPHONEBOOK) 0622 self.setmode(self.MODEBREW) 0623 try: 0624 _new_list=self._get_new_list('ringtone-index', 'ringtone', 0625 fundamentals) 0626 # replace files 0627 self._replace_files('ringtone-index', 'ringtone', 0628 _new_list, fundamentals) 0629 except: 0630 if __debug__: 0631 self.setmode(self.MODEMODEM) 0632 raise 0633 self.setmode(self.MODEMODEM) 0634 return fundamentals 0635 0636 def savewallpapers(self, fundamentals, merge): 0637 """Save wallpapers to the phone""" 0638 self.log('Writing wallpapers to the phone') 0639 self.setmode(self.MODEPHONEBOOK) 0640 self.setmode(self.MODEBREW) 0641 try: 0642 _new_list=self._get_new_list('wallpaper-index', 'wallpapers', 0643 fundamentals) 0644 # replace files 0645 self._replace_files('wallpaper-index', 'wallpapers', 0646 _new_list, fundamentals) 0647 except: 0648 if __debug__: 0649 self.setmode(self.MODEMODEM) 0650 raise 0651 self.setmode(self.MODEMODEM) 0652 return fundamentals 0653 0654 # Phone Detection routine 0655 @classmethod 0656 def detectphone(_, coms, likely_ports, res, _module, _log): 0657 for port in likely_ports: 0658 _model=res.get(port, {}).get('model', None) 0659 if _model==_module.Profile.phone_model: 0660 return port 0661 elif _model==_module.Profile.generic_phone_model: 0662 # this is a V710, try to get the actual model 0663 try: 0664 _comm=commport.CommConnection(_log, port) 0665 _comm.sendatcommand('+MODE=2') 0666 _comm.sendatcommand('') 0667 _s=_comm.sendatcommand('+GMM')[0] 0668 _comm.sendatcommand('+MODE=0') 0669 _comm.close() 0670 _model=_s.split(': ')[1].split(',')[-1].replace('"', '').split('=')[1] 0671 if _model==_module.Profile.common_model_name: 0672 _model=_module.Profile.phone_model 0673 res[port]['model']=_model 0674 if _model==_module.Profile.phone_model: 0675 return port 0676 except: 0677 _comm.close() 0678 if __debug__: 0679 raise 0680 0681 #------------------------------------------------------------------------------ 0682 parentprofile=com_moto_cdma.Profile 0683 class Profile(parentprofile): 0684 0685 serialsname=Phone.serialsname 0686 0687 WALLPAPER_WIDTH=176 0688 WALLPAPER_HEIGHT=220 0689 MAX_WALLPAPER_BASENAME_LENGTH=37 0690 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()_ .-" 0691 WALLPAPER_CONVERT_FORMAT="jpg" 0692 0693 # Motorola OEM USB Cable 0694 usbids=( ( 0x22B8, 0x2A22, 1), 0695 ( 0x22B8, 0x2A62, 1)) 0696 deviceclasses=("modem",) 0697 # use for auto-detection 0698 phone_manufacturer='Motorola' 0699 phone_model='V710 ' 0700 common_model_name='V710' 0701 generic_phone_model='Motorola CDMA v710 Phone' 0702 0703 # all dumped in "images" 0704 imageorigins={} 0705 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 0706 def GetImageOrigins(self): 0707 return self.imageorigins 0708 0709 # our targets are the same for all origins 0710 imagetargets={} 0711 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 0712 {'width': 176, 'height': 200, 'format': "JPEG"})) 0713 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 0714 {'width': 176, 'height': 140, 'format': "JPEG"})) 0715 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 0716 {'width': 176, 'height': 220, 'format': "JPEG"})) 0717 def GetTargetsForImageOrigin(self, origin): 0718 return self.imagetargets 0719 0720 def __init__(self): 0721 parentprofile.__init__(self) 0722 0723 _supportedsyncs=( 0724 ('phonebook', 'read', None), # all phonebook reading 0725 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0726 ('calendar', 'read', None), # all calendar reading 0727 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0728 ('ringtone', 'read', None), # all ringtone reading 0729 ('ringtone', 'write', 'OVERWRITE'), 0730 ('wallpaper', 'read', None), # all wallpaper reading 0731 ('wallpaper', 'write', 'OVERWRITE'), 0732 ('sms', 'read', None), # all SMS list reading DJP 0733 ) 0734 0735 def convertphonebooktophone(self, helper, data): 0736 return data 0737 0738 def QueryAudio(self, origin, currentextension, afi): 0739 # we don't modify any of these 0740 if afi.format in ("MIDI", "QCP", "PMD"): 0741 return currentextension, afi 0742 # examine mp3 0743 if afi.format=="MP3": 0744 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 0745 return currentextension, afi 0746 # convert it 0747 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 48, 'samplerate': 44100})) 0748 0749 field_color_data={ 0750 'phonebook': { 0751 'name': { 0752 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 0753 'nickname': 0, 'details': 1 }, 0754 'number': { 0755 # not sure what the limit on these numbers 0756 'type': True, 'speeddial': True, 'number': True, 0757 'details': True, 0758 'ringtone': True, 'wallpaper': True }, 0759 'email': True, 0760 'email_details': { 0761 'emailspeeddial': True, 'emailringtone': True, 0762 'emailwallpaper': True }, 0763 'address': { 0764 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 0765 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 0766 'details': 0 }, 0767 'url': 0, 0768 'memo': 0, 0769 'category': 1, 0770 'wallpaper': 1, 0771 'ringtone': 1, 0772 'storage': 0, 0773 }, 0774 'calendar': { 0775 'description': True, 'location': True, 'allday': False, 0776 'start': True, 'end': True, 'priority': False, 0777 'alarm': True, 'vibrate': False, 0778 'repeat': True, 0779 'memo': False, 0780 'category': False, 0781 'wallpaper': False, 0782 'ringtone': False, 0783 }, 0784 'memo': { 0785 'subject': False, 0786 'date': False, 0787 'secret': False, 0788 'category': False, 0789 'memo': False, 0790 }, 0791 'todo': { 0792 'summary': False, 0793 'status': False, 0794 'due_date': False, 0795 'percent_complete': False, 0796 'completion_date': False, 0797 'private': False, 0798 'priority': False, 0799 'category': False, 0800 'memo': False, 0801 }, 0802 } 0803
Generated by PyXR 0.9.4