0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 0004 ### Copyright (C) 2006 Simon Capper <skyjunky@sbcglobal.net> 0005 ### 0006 ### This program is free software; you can redistribute it and/or modify 0007 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0008 ### 0009 ### $Id: com_lglg8100.py 4303 2007-07-13 20:46:53Z djpham $ 0010 0011 """Communicate with the LG VX5200 cell phone 0012 0013 0014 The code in this file mainly inherits from VX8100 code and then extends where 0015 the 5200 has different functionality 0016 0017 """ 0018 0019 # standard modules 0020 import time 0021 import calendar 0022 import cStringIO 0023 import sha 0024 0025 # my modules 0026 import common 0027 import commport 0028 import copy 0029 import com_lgvx4400 0030 import p_brew 0031 import p_lglg8100 0032 import com_lgvx8100 0033 import com_brew 0034 import com_phone 0035 import com_lg 0036 import prototypes 0037 import bpcalendar 0038 import call_history 0039 import sms 0040 import memo 0041 0042 class Phone(com_lgvx8100.Phone): 0043 "Talk to the LG LG8100 cell phone (Telus)" 0044 0045 desc="LG-LG8100" 0046 helpid=None 0047 protocolclass=p_lglg8100 0048 serialsname='lglg8100' 0049 0050 builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps') + \ 0051 tuple(['Ringtone '+`n` for n in range(1,11)]) + \ 0052 ('No Ring',) 0053 0054 ringtonelocations= ( 0055 # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major icon 0056 ( 'ringers', 'dload/aod.dat', 'dload/aodsize.dat', 'brew/16452/mds', 1000, 150, 6, 1, 0), 0057 # the sound index file uses the same index as the ringers, bitpim does not support this (yet) 0058 # ( 'sounds', 'dload/mysound.dat', 'dload/mysoundsize.dat', 'brew/16452/ms', 100, 150, 2, 0, 151), 0059 ) 0060 0061 calendarlocation="sch/newschedule.dat" 0062 calendarexceptionlocation="sch/newschexception.dat" 0063 calenderrequiresreboot=1 0064 memolocation="sch/neomemo.dat" 0065 0066 builtinwallpapers = () # none 0067 0068 wallpaperlocations= ( 0069 ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0x300, 0, 0), 0070 #( 'video', 'dload/video.dat', None, 'brew/16452/mf', 1000, 50, 0x0304, 0, 0), 0071 ) 0072 0073 # for removable media (miniSD cards) 0074 _rs_path='mmc1/' 0075 _rs_ringers_path=_rs_path+'ringers' 0076 _rs_images_path=_rs_path+'images' 0077 media_info={ 'ringers': { 0078 'localpath': 'brew/16452/mds', 0079 'rspath': _rs_ringers_path, 0080 'vtype': protocolclass.MEDIA_TYPE_RINGTONE, 0081 'icon': protocolclass.MEDIA_RINGTONE_DEFAULT_ICON, 0082 'index': 1000, # starting index 0083 'maxsize': 155, 0084 'indexfile': 'dload/aod.dat', 0085 'sizefile': 'dload/aodsize.dat', 0086 'dunno': 0, 'date': True, 0087 }, 0088 # 'sounds': { 0089 # 'localpath': 'brew/16452/ms', 0090 # 'rspath': None, 0091 # 'vtype': protocolclass.MEDIA_TYPE_SOUND, 0092 # 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 0093 # 'index': 100, 0094 # 'maxsize': 155, 0095 # 'indexfile': 'dload/mysound.dat', 0096 # 'sizefile': 'dload/mysoundsize.dat', 0097 # 'dunno': 0, 'date': False }, 0098 'images': { 0099 'localpath': 'brew/16452/mp', 0100 'rspath': _rs_images_path, 0101 'vtype': protocolclass.MEDIA_TYPE_IMAGE, 0102 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 0103 'index': 100, 0104 'maxsize': 155, 0105 'indexfile': 'dload/image.dat', 0106 'sizefile': 'dload/imagesize.dat', 0107 'dunno': 0, 'date': True }, 0108 # 'video': { 0109 # 'localpath': 'brew/16452/mf', 0110 # 'rspath': None, 0111 # 'vtype': protocolclass.MEDIA_TYPE_VIDEO, 0112 # 'icon': protocolclass.MEDIA_VIDEO_DEFAULT_ICON, 0113 # 'index': 1000, 0114 # 'maxsize': 155, 0115 # 'indexfile': 'dload/video.dat', 0116 # 'sizefile': 'dload/videosize.dat', 0117 # 'dunno': 0, 'date': True }, 0118 } 0119 0120 def __init__(self, logtarget, commport): 0121 com_lgvx4400.Phone.__init__(self,logtarget,commport) 0122 self.mode=self.MODENONE 0123 0124 def __del__(self): 0125 pass 0126 0127 def getcalendar(self,result): 0128 res={} 0129 # Read exceptions file first 0130 try: 0131 buf=prototypes.buffer(self.getfilecontents(self.calendarexceptionlocation)) 0132 ex=self.protocolclass.scheduleexceptionfile() 0133 ex.readfrombuffer(buf, logtitle="Calendar exceptions") 0134 exceptions={} 0135 for i in ex.items: 0136 try: 0137 exceptions[i.pos].append( (i.year,i.month,i.day) ) 0138 except KeyError: 0139 exceptions[i.pos]=[ (i.year,i.month,i.day) ] 0140 except com_brew.BrewNoSuchFileException: 0141 exceptions={} 0142 0143 # Now read schedule 0144 try: 0145 buf=prototypes.buffer(self.getfilecontents(self.calendarlocation)) 0146 if len(buf.getdata())<3: 0147 # file is empty, and hence same as non-existent 0148 raise com_brew.BrewNoSuchFileException() 0149 sc=self.protocolclass.schedulefile() 0150 sc.readfrombuffer(buf, logtitle="Calendar") 0151 for event in sc.events: 0152 # the vx8100 has a bad entry when the calender is empty 0153 # stop processing the calender when we hit this record 0154 if event.pos==0: #invalid entry 0155 continue 0156 entry=bpcalendar.CalendarEntry() 0157 entry.desc_loc=event.description 0158 try: # delete events are still in the calender file but have garbage dates 0159 entry.start=event.start 0160 entry.end=event.end 0161 except ValueError: 0162 continue 0163 if event.alarmindex_vibrate&0x1: 0164 entry.vibrate=0 # vibarate bit is inverted in phone 0=on, 1=off 0165 else: 0166 entry.vibrate=1 0167 entry.repeat = self.makerepeat(event.repeat, event.start) 0168 min=event.alarmminutes 0169 hour=event.alarmhours 0170 if min==0x64 or hour==0x64: 0171 entry.alarm=None # no alarm set 0172 else: 0173 entry.alarm=hour*60+min 0174 entry.ringtone=result['ringtone-index'][event.ringtone]['name'] 0175 entry.snoozedelay=0 0176 # check for exceptions and remove them 0177 if event.repeat[3] and exceptions.has_key(event.pos): 0178 for year, month, day in exceptions[event.pos]: 0179 entry.suppress_repeat_entry(year, month, day) 0180 res[entry.id]=entry 0181 0182 assert sc.numactiveitems==len(res) 0183 except com_brew.BrewNoSuchFileException: 0184 pass # do nothing if file doesn't exist 0185 result['calendar']=res 0186 return result 0187 0188 def makerepeat(self, repeat, start): 0189 # get all the variables out of the repeat tuple 0190 # and convert into a bpcalender RepeatEntry 0191 type,dow,interval,interval2,exceptions=repeat 0192 #print "repeat "+`repeat`+" start "+`start` 0193 if type==0: 0194 repeat_entry=None 0195 else: 0196 repeat_entry=bpcalendar.RepeatEntry() 0197 if type==1: #daily 0198 repeat_entry.repeat_type=repeat_entry.daily 0199 repeat_entry.interval=interval 0200 elif type==2: #'monfri' 0201 repeat_entry.repeat_type=repeat_entry.daily 0202 repeat_entry.interval=0 0203 elif type==3: #'weekly' 0204 repeat_entry.repeat_type=repeat_entry.weekly 0205 repeat_entry.interval=interval 0206 elif type==5: #'monthly' 0207 repeat_entry.repeat_type=repeat_entry.monthly 0208 repeat_entry.interval2=interval2 0209 repeat_entry.dow=0 0210 elif type==4: #'monthly' #Xth Y day (e.g. 2nd friday each month) 0211 repeat_entry.repeat_type=repeat_entry.monthly 0212 repeat_entry.interval=interval #X 0213 repeat_entry.dow=dow #Y 0214 repeat_entry.interval2=interval2 0215 elif type==6: #'yearly #Xth Y day of Month (2nd sunday of april)' Not supported by bitpim 0216 #repeat_entry.repeat_type=repeat_entry.yearly 0217 repeat_entry=None 0218 else: # =7 'yearly' 0219 repeat_entry.repeat_type=repeat_entry.yearly 0220 return repeat_entry 0221 0222 def setalarm(self, entry, data): 0223 # vx8100 only allows certain repeat intervals, adjust to fit, it also stores an index to the interval 0224 rc=True 0225 if entry.alarm>=10080: 0226 entry.alarm=10080 0227 data.alarmminutes=0 0228 data.alarmhours=168 # 1 week 0229 data.alarmindex_vibrate=0xe 0230 elif entry.alarm>=4320: 0231 entry.alarm=4320 0232 data.alarmminutes=0 0233 data.alarmhours=72 # 3 days 0234 data.alarmindex_vibrate=0xc 0235 elif entry.alarm>=1440: 0236 entry.alarm=1440 0237 data.alarmminutes=0 0238 data.alarmhours=24 # 1 day 0239 data.alarmindex_vibrate=0xa 0240 elif entry.alarm>=60: 0241 entry.alarm=60 0242 data.alarmminutes=0 0243 data.alarmhours=1 # 1 hour 0244 data.alarmindex_vibrate=0x8 0245 elif entry.alarm>=30: 0246 entry.alarm=30 0247 data.alarmminutes=30 0248 data.alarmhours=0 # 30 mins 0249 data.alarmindex_vibrate=0x6 0250 elif entry.alarm>=15: 0251 entry.alarm=15 0252 data.alarmminutes=15 0253 data.alarmhours=0 0254 data.alarmindex_vibrate=0x4 0255 elif entry.alarm>=0: 0256 entry.alarm=0 0257 data.alarmminutes=0 0258 data.alarmhours=0 0259 data.alarmindex_vibrate=0x2 0260 else: # no alarm 0261 data.alarmminutes=0x64 0262 data.alarmhours=0x64 0263 data.alarmindex_vibrate=1 0264 rc=False 0265 # set the vibrate bit 0266 if data.alarmindex_vibrate > 1 and entry.vibrate==0: 0267 data.alarmindex_vibrate+=1 0268 return rc 0269 0270 0271 def savecalendar(self, dict, merge): 0272 # ::TODO:: 0273 # what will be written to the files 0274 eventsf=self.protocolclass.schedulefile() 0275 exceptionsf=self.protocolclass.scheduleexceptionfile() 0276 # what are we working with 0277 cal=dict['calendar'] 0278 newcal={} 0279 #sort into start order, makes it possible to see if the calendar has changed 0280 keys=[(x.start, k) for k,x in cal.items()] 0281 keys.sort() 0282 # number of entries 0283 eventsf.numactiveitems=len(keys) 0284 pos=0 0285 contains_alarms=False 0286 # play with each entry 0287 for (_,k) in keys: 0288 # entry is what we will return to user 0289 entry=cal[k] 0290 data=self.protocolclass.scheduleevent() 0291 data.index=pos 0292 pos+=1 0293 data.pos=eventsf.packetsize() 0294 data.description=entry.desc_loc 0295 data.start=entry.start 0296 data.end=entry.end 0297 alarm_set=self.setalarm(entry, data) 0298 data.ringtone=0 0299 if alarm_set: 0300 if entry.ringtone=="No Ring" and not entry.vibrate: 0301 alarm_name="Low Beep Once" 0302 else: 0303 alarm_name=entry.ringtone 0304 else:# set alarm to "No Ring" gets rid of alarm icon on phone 0305 alarm_name="No Ring" 0306 for i in dict['ringtone-index']: 0307 if dict['ringtone-index'][i]['name']==alarm_name: 0308 data.ringtone=i 0309 # check for exceptions and add them to the exceptions list 0310 exceptions=0 0311 if entry.repeat!=None: 0312 if data.end[:3]==entry.no_end_date: 0313 data.end=(2100, 12, 31)+data.end[3:] 0314 for i in entry.repeat.suppressed: 0315 de=self.protocolclass.scheduleexception() 0316 de.pos=data.pos 0317 de.day=i.date.day 0318 de.month=i.date.month 0319 de.year=i.date.year 0320 exceptions=1 0321 exceptionsf.items.append(de) 0322 if entry.repeat != None: 0323 data.repeat=(self.getrepeattype(entry, exceptions)) 0324 else: 0325 data.repeat=((0,0,0,0,0)) 0326 if data.alarmindex_vibrate!=1: # if alarm set 0327 contains_alarms=True 0328 # put entry in nice shiny new dict we are building 0329 entry=copy.copy(entry) 0330 newcal[data.pos]=entry 0331 eventsf.events.append(data) 0332 0333 buf=prototypes.buffer() 0334 eventsf.writetobuffer(buf, logtitle="New Calendar") 0335 # We check the existing calender as changes require a reboot for the alarms 0336 # to work properly, also no point writing the file if it is not changing 0337 if buf.getvalue()!=self.getfilecontents(self.calendarlocation): 0338 self.writefile(self.calendarlocation, buf.getvalue()) 0339 if contains_alarms or self.calenderrequiresreboot: 0340 self.log("Your phone has to be rebooted due to the calendar changing") 0341 dict["rebootphone"]=True 0342 else: 0343 self.log("Phone calendar unchanged, no update required") 0344 0345 buf=prototypes.buffer() 0346 exceptionsf.writetobuffer(buf, logtitle="Writing calendar exceptions") 0347 self.writefile(self.calendarexceptionlocation, buf.getvalue()) 0348 0349 # fix passed in dict 0350 dict['calendar']=newcal 0351 0352 return dict 0353 0354 def getrepeattype(self, entry, exceptions): 0355 #convert the bpcalender type into vx8100 type 0356 repeat_entry=bpcalendar.RepeatEntry() 0357 interval2=0 0358 if entry.repeat.repeat_type==repeat_entry.monthly: 0359 dow=entry.repeat.dow 0360 interval2=entry.repeat.interval2 0361 if entry.repeat.dow==0: 0362 # set interval for month type 4 to start day of month, (required by vx8100) 0363 interval=entry.start[2] 0364 type=5 0365 else: 0366 interval=entry.repeat.interval 0367 type=4 0368 elif entry.repeat.repeat_type==repeat_entry.daily: 0369 dow=entry.repeat.dow 0370 interval=entry.repeat.interval 0371 if entry.repeat.interval==0: 0372 type=2 0373 else: 0374 type=1 0375 elif entry.repeat.repeat_type==repeat_entry.weekly: 0376 dow=entry.repeat.dow 0377 interval=entry.repeat.interval 0378 type=3 0379 elif entry.repeat.repeat_type==repeat_entry.yearly: 0380 # set interval to start day of month, (required by vx8100) 0381 interval=entry.start[2] 0382 # set dow to start month, (required by vx8100) 0383 dow=entry.start[1] 0384 type=7 0385 return (type, dow, interval, interval2, exceptions) 0386 0387 def eval_detect_data(self, res): 0388 found=False 0389 if res.get(self.brew_version_txt_key, None) is not None: 0390 found=res[self.brew_version_txt_key][:len(self.my_version_txt)]==self.my_version_txt 0391 if found: 0392 res['model']=self.my_model 0393 res['manufacturer']='LG Electronics Inc' 0394 s=res.get(self.esn_file_key, None) 0395 if s: 0396 res['esn']=self.get_esn(s) 0397 0398 my_version_txt='CX8100' 0399 my_model='LG8100' 0400 0401 0402 # Media stuff--------------------------------------------------------------- 0403 # Bypassing the 8100/9800 specific stuff 0404 # def getmedia(self, maps, results, key): 0405 # return com_lg.LGNewIndexedMedia2.getmedia(self, maps, results, key) 0406 # def savemedia(self, mediakey, mediaindexkey, maps, results, merge, reindexfunction): 0407 # return com_lg.LGNewIndexedMedia2.savemedia(self, mediakey, mediaindexkey, maps, results, merge, reindexfunction) 0408 0409 parentprofile=com_lgvx8100.Profile 0410 class Profile(parentprofile): 0411 protocolclass=Phone.protocolclass 0412 serialsname=Phone.serialsname 0413 0414 BP_Calendar_Version=3 0415 phone_manufacturer='LG Electronics Inc' 0416 phone_model='LG8100' 0417 0418 WALLPAPER_WIDTH=176 0419 WALLPAPER_HEIGHT=220 0420 MAX_WALLPAPER_BASENAME_LENGTH=32 0421 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()-_ ." 0422 WALLPAPER_CONVERT_FORMAT="jpg" 0423 0424 # the 8100 uses "W" for wait in the dialstring, it does not support "T" 0425 DIALSTRING_CHARS="[^0-9PW#*]" 0426 0427 MAX_RINGTONE_BASENAME_LENGTH=32 0428 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()-_ ." 0429 0430 # the 8100 doesn't have seperate origins - they are all dumped in "images" 0431 imageorigins={} 0432 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 0433 def GetImageOrigins(self): 0434 return self.imageorigins 0435 0436 # our targets are the same for all origins 0437 imagetargets={} 0438 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 0439 {'width': 128, 'height': 128, 'format': "JPEG"})) 0440 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 0441 {'width': 176, 'height': 220, 'format': "JPEG"})) 0442 0443 def GetTargetsForImageOrigin(self, origin): 0444 return self.imagetargets 0445 0446 0447 def __init__(self): 0448 parentprofile.__init__(self) 0449 0450 _supportedsyncs=( 0451 ('phonebook', 'read', None), # all phonebook reading 0452 ('calendar', 'read', None), # all calendar reading 0453 ('wallpaper', 'read', None), # all wallpaper reading 0454 ('ringtone', 'read', None), # all ringtone reading 0455 ('call_history', 'read', None),# all call history list reading 0456 ('sms', 'read', None), # all SMS list reading 0457 ('memo', 'read', None), # all memo list reading 0458 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0459 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0460 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 0461 ('wallpaper', 'write', 'OVERWRITE'), 0462 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 0463 ('ringtone', 'write', 'OVERWRITE'), 0464 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 0465 ('memo', 'write', 'OVERWRITE'), # all memo list writing 0466 ) 0467 0468 field_color_data={ 0469 'phonebook': { 0470 'name': { 0471 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 0472 'nickname': 0, 'details': 1 }, 0473 'number': { 0474 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 0475 'email': 2, 0476 'address': { 0477 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 0478 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 0479 'details': 0 }, 0480 'url': 0, 0481 'memo': 1, 0482 'category': 1, 0483 'wallpaper': 1, 0484 'ringtone': 2, 0485 'storage': 0, 0486 }, 0487 'calendar': { 0488 'description': True, 'location': True, 'allday': True, 0489 'start': True, 'end': True, 'priority': False, 0490 'alarm': True, 'vibrate': True, 0491 'repeat': True, 0492 'memo': False, 0493 'category': False, 0494 'wallpaper': False, 0495 'ringtone': True, 0496 }, 0497 'memo': { 0498 'subject': True, 0499 'date': True, 0500 'secret': False, 0501 'category': False, 0502 'memo': True, 0503 }, 0504 'todo': { 0505 'summary': False, 0506 'status': False, 0507 'due_date': False, 0508 'percent_complete': False, 0509 'completion_date': False, 0510 'private': False, 0511 'priority': False, 0512 'category': False, 0513 'memo': False, 0514 }, 0515 } 0516
Generated by PyXR 0.9.4