PyXR

c:\projects\bitpim\src \ phones \ com_motov710.py



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