LG VX7000 Update
Jungho Park <[email protected]>
| Newsgroups | gmane.comp.mobile.bitpim.devel |
|---|---|
| Message-ID | <[email protected]> |
Hello I modified p_lgvx7000.p and com_lgvx7000.py to enable calendar, memo, sms, and call history. I modified code copied from lgvx4650. There are some issues but I am not familiar with Python. Calendar issue - Repeat Yearly is not supported. - Deleted or some events with Repeat have a strange 4 byte date, which causes exception error. Memo (I just used LG VX4650 code) - Appears to be OK SMS - Somehow, "Locked" does not work for Inbox. Call History - Appears to be OK I have LG VX7000. If you want to test this phone, send me the code. Thanks. Jungho Park ====================================== Jungho Park ([email protected]) 245 Natural History Bldg 1301 W.Green St. Urbana, IL 61801-3011 ======================================
com_lgvx7000.py
(text/plain, 24.2 KB)
### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: com_lgvx7000.py,v 1.10 2005/08/24 04:32:52 djpham Exp $ """Communicate with the LG VX7000 cell phone The VX7000 is substantially similar to the VX6000 but also supports video. The code in this file mainly inherits from VX4400 code and then extends where the 6000 has extra functionality """ # standard modules import time import cStringIO import sha # my modules import bpcalendar import call_history import common import copy import p_lgvx7000 import com_lgvx4400 import com_brew import com_phone import com_lg import memo import prototypes import sms class Phone(com_lg.LGNewIndexedMedia,com_lgvx4400.Phone): "Talk to the LG VX7000 cell phone" desc="LG-VX7000" protocolclass=p_lgvx7000 serialsname='lgvx7000' builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps') + \ tuple(['Ringtone '+`n` for n in range(1,11)]) + \ ('No Ring',) ringtonelocations= ( # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major ( 'ringers', 'dload/sound.dat', 'dload/soundsize.dat', 'dload/snd', 100, 50, 1), ) builtinwallpapers = () # none wallpaperlocations= ( ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'dload/img', 100, 50, 0), ) def __init__(self, logtarget, commport): com_lgvx4400.Phone.__init__(self,logtarget,commport) com_lg.LGNewIndexedMedia.__init__(self) self.mode=self.MODENONE my_model='VX7000' # SMS stuff def savesms(self, result, merge): self._setquicktext(result) result['rebootphone']=True return result def _setquicktext(self, result): sf=self.protocolclass.sms_quick_text() quicktext=result.get('canned_msg', []) count=0 for entry in quicktext: if count < self.protocolclass.SMS_CANNED_MAX_ITEMS: sf.msgs.append(entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1]) count+=1 else: break if count!=0: # don't create the file if there are no entries buf=prototypes.buffer() sf.writetobuffer(buf) self.logdata("Writing calendar", buf.getvalue(), sf) self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue()) return def getsms(self, result): # get the quicktext (LG name for canned messages) result['canned_msg']=self._getquicktext() result['sms']=self._readsms() return result def _readsms(self): res={} # go through the sms directory looking for messages for item in self.getfilesystem("sms").values(): if item['type']=='file': folder=None for f,pat in self.protocolclass.SMS_PATTERNS.items(): if pat.match(item['name']): folder=f break if folder: buf=prototypes.buffer(self.getfilecontents(item['name'], True)) self.logdata("SMS message file " +item['name'], buf.getdata()) if folder=='Inbox': sf=self.protocolclass.sms_in() sf.readfrombuffer(buf) entry=self._getinboxmessage(sf) res[entry.id]=entry elif folder=='Sent': sf=self.protocolclass.sms_out() sf.readfrombuffer(buf) entry=self._getoutboxmessage(sf) res[entry.id]=entry elif folder=='Saved': sf=self.protocolclass.sms_saved() sf.readfrombuffer(buf) if sf.outboxmsg: entry=self._getoutboxmessage(sf.outbox) else: entry=self._getinboxmessage(sf.inbox) entry.folder=entry.Folder_Saved res[entry.id]=entry return res def _getquicktext(self): quicks=[] try: buf=prototypes.buffer(self.getfilecontents("sms/mediacan000.dat")) sf=self.protocolclass.sms_quick_text() sf.readfrombuffer(buf) self.logdata("SMS quicktext file sms/mediacan000.dat", buf.getdata(), sf) for rec in sf.msgs: if rec.msg!="": quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type }) except com_brew.BrewNoSuchFileException: pass # do nothing if file doesn't exist return quicks def _getinboxmessage(self, sf): entry=sms.SMSEntry() entry.folder=entry.Folder_Inbox entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) entry._from=self._getsender(sf.sender, sf.sender_length) entry.subject=sf.subject entry.locked=sf.locked if sf.priority==0: entry.priority=sms.SMSEntry.Priority_Normal else: entry.priority=sms.SMSEntry.Priority_High entry.read=sf.read txt="" if sf.num_msg_elements==1 and sf.bin_header1==0: txt=self._get_text_from_sms_msg_without_header(sf.msgs[0].msg, sf.msglengths[0].msglength) else: for i in range(sf.num_msg_elements): txt+=self._get_text_from_sms_msg_with_header(sf.msgs[i].msg, sf.msglengths[i].msglength) entry.text=unicode(txt, errors='ignore') entry.callback=sf.callback return entry def _getoutboxmessage(self, sf): entry=sms.SMSEntry() entry.folder=entry.Folder_Sent entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) # add all the recipients for r in sf.recipients: if r.number: confirmed=(r.status==5) confirmed_date=None if confirmed: confirmed_date="%d%02d%02dT%02d%02d00" % r.timereceived entry.add_recipient(r.number, confirmed, confirmed_date) entry.subject=sf.subject txt="" if sf.num_msg_elements==1 and not sf.messages[0].binary: txt=self._get_text_from_sms_msg_without_header(sf.messages[0].msg, sf.messages[0].length) else: for i in range(sf.num_msg_elements): txt+=self._get_text_from_sms_msg_with_header(sf.messages[i].msg, sf.messages[i].length) entry.text=unicode(txt, errors='ignore') if sf.priority==0: entry.priority=sms.SMSEntry.Priority_Normal else: entry.priority=sms.SMSEntry.Priority_High entry.locked=sf.locked entry.callback=sf.callback return entry def _get_text_from_sms_msg_without_header(self, msg, num_septets): out="" for i in range(num_septets): tmp = (msg[(i*7)/8].byte<<8) | msg[((i*7)/8) + 1].byte bit_index = 9 - ((i*7) % 8) out += chr((tmp >> bit_index) & 0x7f) return out def _get_text_from_sms_msg_with_header(self, msg, num_septets): data_len = ((msg[0].byte+1)*8+6)/7 seven_bits={} raw={} out={} # re-order the text into the correct order for separating into # 7-bit characters for i in range(0, (num_septets*7)/8+7, 7): for k in range(7): raw[i+6-k]=msg[i+k].byte # extract the 7-bit chars for i in range(num_septets+7): tmp = (raw[(i*7)/8]<<8) | raw[((i*7)/8) + 1] bit_index = 9 - ((i*7) % 8) seven_bits[i] = (tmp >> bit_index) & 0x7f # correct the byte order and remove the data portion of the message i=0 for i in range(0, num_septets+7, 8): for k in range(8): if(i+7-k-data_len>=0): if i+k<num_septets+7: out[i+7-k-data_len]=seven_bits[i+k] res="" for i in range(num_septets-data_len): res+=chr(out[i]) return res def _getsender(self, raw, len): result="" for i in range(len): if(raw[i].byte==10): result+="0" else: result+="%d" % raw[i].byte return result # Calendar stuff------------------------------------------------------------ # all taken care by the VX4400 # Calendar stuff------------------------------------------------------------ def getcalendar(self,result): # Read exceptions file first try: buf=prototypes.buffer(self.getfilecontents( self.protocolclass.cal_exception_file_name)) ex=self.protocolclass.scheduleexceptionfile() ex.readfrombuffer(buf) self.logdata("Calendar exceptions", buf.getdata(), ex) exceptions={} for i in ex.items: exceptions.setdefault(i.pos, []).append( (i.year,i.month,i.day) ) except com_brew.BrewNoSuchFileException: exceptions={} # Now read schedule try: buf=prototypes.buffer(self.getfilecontents( self.protocolclass.cal_data_file_name)) if len(buf.getdata())<2: # file is empty, and hence same as non-existent raise com_brew.BrewNoSuchFileException() sc=self.protocolclass.schedulefile() self.logdata("Calendar", buf.getdata(), sc) sc.readfrombuffer(buf) res=self.get_cal(sc, exceptions, result.get('ringtone-index', {})) except com_brew.BrewNoSuchFileException: res={} result['calendar']=res return result def savecalendar(self, dict, merge): # ::TODO:: obey merge param # get the list of available voice alarm files voice_files={} if self._cal_has_voice_id: try: file_list=self.getfilesystem(self.protocolclass.cal_dir) for k in file_list.keys(): if k.endswith(self.protocolclass.cal_voice_ext): voice_files[int(k[8:11])]=k except: self.log('Failed to list Calendar Voice Files') # build the schedule file sc=self.protocolclass.schedulefile() sc_ex=self.set_cal(sc, dict.get('calendar', {}), dict.get('ringtone-index', {}), voice_files) buf=prototypes.buffer() sc.writetobuffer(buf) self.writefile(self.protocolclass.cal_data_file_name, buf.getvalue()) # build the exceptions exceptions_file=self.protocolclass.scheduleexceptionfile() for k,l in sc_ex.items(): for x in l: _ex=self.protocolclass.scheduleexception() _ex.pos=k _ex.year, _ex.month, _ex.day=x exceptions_file.items.append(_ex) buf=prototypes.buffer() exceptions_file.writetobuffer(buf) self.writefile(self.protocolclass.cal_exception_file_name, buf.getvalue()) # clear out any alarm voice files that may have been deleted if self._cal_has_voice_id: for k,e in voice_files.items(): try: self.rmfile(e) except: self.log('Failed to delete file '+e) return dict _repeat_values={ protocolclass.CAL_REP_DAILY: bpcalendar.RepeatEntry.daily, protocolclass.CAL_REP_MONFRI: bpcalendar.RepeatEntry.daily, protocolclass.CAL_REP_WEEKLY: bpcalendar.RepeatEntry.weekly, protocolclass.CAL_REP_MONTHLY: bpcalendar.RepeatEntry.monthly, protocolclass.CAL_REP_YEARLY: bpcalendar.RepeatEntry.yearly } def _build_cal_repeat(self, event, exceptions): rep_val=Phone._repeat_values.get(event.repeat, None) if not rep_val: return None rep=bpcalendar.RepeatEntry(rep_val) if event.repeat==self.protocolclass.CAL_REP_MONFRI: rep.interval=rep.dow=0 elif event.repeat!=self.protocolclass.CAL_REP_YEARLY: rep.interval=1 rep.dow=0 # do exceptions cal_ex=exceptions.get(event.pos, []) for e in cal_ex: rep.add_suppressed(*e) return rep def _get_voice_id(self, event, entry): if event.hasvoice: entry.voice=event.voiceid def _build_cal_entry(self, event, exceptions, ringtone_index): # return a copy of bpcalendar object based on my values # general fields entry=bpcalendar.CalendarEntry() entry.start=event.start entry.end=event.end entry.description=event.description # check for allday event if entry.start[3:]==(0, 0) and entry.end[3:]==(23, 59): entry.allday=True # alarm if event.alarmtype: entry.alarm=event.alarmhours*60+event.alarmminutes # ringtone rt_idx=event.ringtone # hack to account for the VX4650 weird ringtone setup # if rt_idx<50: # 1st part of builtin ringtones, need offset by 1 # rt_idx+=1 entry.ringtone=ringtone_index.get(rt_idx, {'name': None} )['name'] # voice ID if applicable if self._cal_has_voice_id: self._get_voice_id(event, entry) # repeat info entry.repeat=self._build_cal_repeat(event, exceptions) return entry def get_cal(self, sch_file, exceptions, ringtone_index): res={} for event in sch_file.events: if event.pos==-1: # blank entry continue cal_event=self._build_cal_entry(event, exceptions, ringtone_index) res[cal_event.id]=cal_event return res _alarm_info={ -1: (protocolclass.CAL_REMINDER_NONE, 100, 100), 0: (protocolclass.CAL_REMINDER_ONTIME, 0, 0), 5: (protocolclass.CAL_REMINDER_5MIN, 5, 0), 10: (protocolclass.CAL_REMINDER_10MIN, 10, 0), 60: (protocolclass.CAL_REMINDER_1HOUR, 0, 1), 1440: (protocolclass.CAL_REMINDER_1DAY, 0, 24), 2880: (protocolclass.CAL_REMINDER_2DAYS, 0, 48) } _default_alarm=(protocolclass.CAL_REMINDER_NONE, 100, 100) # default alarm is off _phone_dow={ 1: protocolclass.CAL_DOW_SUN, 2: protocolclass.CAL_DOW_MON, 4: protocolclass.CAL_DOW_TUE, 8: protocolclass.CAL_DOW_WED, 16: protocolclass.CAL_DOW_THU, 32: protocolclass.CAL_DOW_FRI, 64: protocolclass.CAL_DOW_SAT } def _set_repeat_event(self, event, entry, exceptions): rep_val=self.protocolclass.CAL_REP_NONE day_bitmap=0 rep=entry.repeat if rep: rep_type=rep.repeat_type rep_interval=rep.interval rep_dow=rep.dow if rep_type==bpcalendar.RepeatEntry.daily: if rep_interval==0: rep_val=self.protocolclass.CAL_REP_MONFRI elif rep_interval==1: rep_val=self.protocolclass.CAL_REP_DAILY elif rep_type==bpcalendar.RepeatEntry.weekly: start_dow=1<<datetime.date(*event.start[:3]).isoweekday()%7 if (rep_dow==0 or rep_dow==start_dow) and rep_interval==1: rep_val=self.protocolclass.CAL_REP_WEEKLY day_bitmap=self._phone_dow.get(start_dow, 0) elif rep_type==bpcalendar.RepeatEntry.monthly: if rep_dow==0: rep_val=self.protocolclass.CAL_REP_MONTHLY else: rep_val=self.protocolclass.CAL_REP_YEARLY if rep_val!=self.protocolclass.CAL_REP_NONE: # build exception list if rep.suppressed: day_bitmap|=self.protocolclass.CAL_DOW_EXCEPTIONS for x in rep.suppressed: exceptions.setdefault(event.pos, []).append(x.get()[:3]) # this is a repeat event, set the end date appropriately if event.end[:3]==entry.no_end_date: event.end=self.protocolclass.CAL_REPEAT_DATE+event.end[3:] else: event.end=entry.end event.repeat=rep_val event.daybitmap=day_bitmap def _set_alarm(self, event, entry): # set alarm value based on entry's value, or its approximation keys=Phone._alarm_info.keys() keys.sort() keys.reverse() _alarm_val=entry.alarm _alarm_key=None for k in keys: if _alarm_val>=k: _alarm_key=k break event.alarmtype, event.alarmminutes, event.alarmhours=Phone._alarm_info.get( _alarm_key, self._default_alarm) def _set_voice_id(self, event, entry, voice_files): if entry.voice and \ voice_files.has_key(entry.voice-self.protocolclass.cal_voice_id_ofs): event.hasvoice=1 event.voiceid=entry.voice del voice_files[entry.voice-self.protocolclass.cal_voice_id_ofs] else: event.hasvoice=0 event.voiceid=self.protocolclass.CAL_NO_VOICE def _set_cal_event(self, event, entry, exceptions, ringtone_index, voice_files): # desc event.description=entry.description # start & end times if entry.allday: event.start=entry.start[:3]+(0,0) event.end=entry.start[:3]+(23,59) else: event.start=entry.start event.end=entry.start[:3]+entry.end[3:] # make sure the event lasts in 1 calendar day if event.end<event.start: event.end=event.start[:3]+(23,59) # alarm self._set_alarm(event, entry) # ringtone rt=0 # always default to the first bultin ringtone if entry.ringtone: for k,e in ringtone_index.items(): if e['name']==entry.ringtone: rt=k break # if rt and rt<50: # rt-=1 event.ringtone=rt # voice ID if self._cal_has_voice_id: self._set_voice_id(event, entry, voice_files) # repeat self._set_repeat_event(event, entry, exceptions) def set_cal(self, sch_file, cal_dict, ringtone_index, voice_files): sch_file.numactiveitems=len(cal_dict) exceptions={} _pos=2 _packet_size=None ## _today=datetime.date.today().timetuple()[:5] for k, e in cal_dict.items(): ## # only send either repeat events or present&future single events ## if e.repeat or (e.start>=_today): event=self.protocolclass.scheduleevent() event.pos=_pos self._set_cal_event(event, e, exceptions, ringtone_index, voice_files) sch_file.events.append(event) if not _packet_size: _packet_size=event.packetsize() _pos+=_packet_size return exceptions # Text Memo stuff----------------------------------------------------------- def getmemo(self, result): # read the memo file try: buf=prototypes.buffer(self.getfilecontents( self.protocolclass.text_memo_file)) text_memo=self.protocolclass.textmemofile() text_memo.readfrombuffer(buf) res={} for m in text_memo.items: entry=memo.MemoEntry() entry.text=m.text res[entry.id]=entry except com_brew.BrewNoSuchFileException: res={} result['memo']=res return result def savememo(self, result, merge): text_memo=self.protocolclass.textmemofile() memo_dict=result.get('memo', {}) keys=memo_dict.keys() keys.sort() text_memo.itemcount=len(keys) for k in keys: entry=self.protocolclass.textmemo() entry.text=memo_dict[k].text text_memo.items.append(entry) buf=prototypes.buffer() text_memo.writetobuffer(buf) self.writefile(self.protocolclass.text_memo_file, buf.getvalue()) return result # Call History stuff-------------------------------------------------------- _call_history_info={ call_history.CallHistoryEntry.Folder_Incoming: protocolclass.incoming_call_file, call_history.CallHistoryEntry.Folder_Outgoing: protocolclass.outgoing_call_file, call_history.CallHistoryEntry.Folder_Missed: protocolclass.missed_call_file } def getcallhistory(self, result): # read the call history files res={} for _folder, _file_name in Phone._call_history_info.items(): try: buf=prototypes.buffer(self.getfilecontents(_file_name)) hist_file=self.protocolclass.callhistoryfile() hist_file.readfrombuffer(buf) for i in range(hist_file.itemcount): hist_call=hist_file.items[i] entry=call_history.CallHistoryEntry() entry.folder=_folder entry.datetime=hist_call.datetime entry.number=hist_call.number entry.name=hist_call.name if _folder!=call_history.CallHistoryEntry.Folder_Missed: entry.duration=hist_call.duration res[entry.id]=entry except com_brew.BrewNoSuchFileException: pass result['call_history']=res return result parentprofile=com_lgvx4400.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname phone_manufacturer='LG Electronics Inc' phone_model='VX7000' WALLPAPER_WIDTH=176 WALLPAPER_HEIGHT=184 MAX_WALLPAPER_BASENAME_LENGTH=32 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ." WALLPAPER_CONVERT_FORMAT="jpg" MAX_RINGTONE_BASENAME_LENGTH=32 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ." # the 7000 doesn't have seperate origins - they are all dumped in "images" imageorigins={} imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) def GetImageOrigins(self): return self.imageorigins # our targets are the same for all origins imagetargets={} imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", {'width': 176, 'height': 184, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", {'width': 176, 'height': 184, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", {'width': 96, 'height': 80, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", {'width': 176, 'height': 220, 'format': "JPEG"})) def GetTargetsForImageOrigin(self, origin): return self.imagetargets def __init__(self): parentprofile.__init__(self) _supportedsyncs=( ('phonebook', 'read', None), # all phonebook reading ('calendar', 'read', None), # all calendar reading ('wallpaper', 'read', None), # all wallpaper reading ('ringtone', 'read', None), # all ringtone reading ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper ('wallpaper', 'write', 'OVERWRITE'), ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone ('ringtone', 'write', 'OVERWRITE'), ('memo', 'read', None), # all memo list reading DJP ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP ('call_history', 'read', None), ('sms', 'read', None), ('sms', 'write', 'OVERWRITE'), )
p_lgvx7000.p
(text/plain, 11.3 KB)
### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of the BitPim license as detailed in the LICENSE file. ### ### $Id: p_lgvx7000.p,v 1.8 2005/03/14 08:26:48 rogerb Exp $ %{ """Various descriptions of data specific to LG VX7000""" from common import PhoneBookBusyException from prototypes import * # Make all lg stuff available in this module as well from p_lg import * # we are the same as lgvx4400 except as noted # below from p_lgvx4400 import * # We use LSB for all integer like fields UINT=UINTlsb BOOL=BOOLlsb NORINGTONE=65535 # -1 in two bytes NOMSGRINGTONE=65535 # -1 in two bytes NOWALLPAPER=0 # of course it wouldn't be 65535 ... NUMSPEEDDIALS=100 FIRSTSPEEDDIAL=2 LASTSPEEDDIAL=99 NUMPHONEBOOKENTRIES=500 MAXCALENDARDESCRIPTION=38 NUMEMAILS=2 NUMPHONENUMBERS=5 # Text Memo const text_memo_file='sch/memo.dat' # SMS const sms_dir='sms' sms_ext='.dat' sms_inbox_prefix='sms/inbox' sms_inbox_name_len=len(sms_inbox_prefix)+3+len(sms_ext) sms_saved_prefix='sms/sf' sms_saved_name_len=len(sms_saved_prefix)+2+len(sms_ext) sms_outbox_prefix='sms/outbox' sms_outbox_name_len=len(sms_outbox_prefix)+3+len(sms_ext) sms_canned_file='sms/mediacan000.dat' SMS_CANNED_MAX_ITEMS=18 # Call History const incoming_call_file='pim/incoming_log.dat' outgoing_call_file='pim/outgoing_log.dat' missed_call_file='pim/missed_log.dat' # The numbertype tab is different than all other LG phones numbertypetab= ( None, 'cell', 'home', 'office', 'cell2', 'fax' ) %} PACKET speeddial: 2 UINT {'default': 0xffff} +entry 1 UINT {'default': 0xff} +number PACKET speeddials: * LIST {'length': NUMSPEEDDIALS, 'elementclass': speeddial} +speeddials PACKET indexentry: 2 UINT index 2 UINT type 84 STRING filename "includes full pathname" 4 UINT {'default': 0} +date "i think this is bitfield of the date" 4 UINT dunno PACKET indexfile: "Used for tracking wallpaper and ringtones" * LIST {'elementclass': indexentry, 'createdefault': True} +items PACKET sizefile: "Used for tracking the total size used by a particular type of media" 4 UINT size # All STRINGS have raiseonterminatedread as False since the phone does # occassionally leave out the terminator byte # Note if you change the length of any of these fields, you also # need to modify com_lgvx7000 to give a different truncateat parameter # in the convertphonebooktophone method PACKET pbentry: 4 UINT serial1 2 UINT {'constant': 0x181, 'constantexception': PhoneBookBusyException} +entrysize 4 UINT serial2 2 UINT entrynumber 23 STRING {'raiseonunterminatedread': False} name 2 UINT group * LIST {'length': NUMEMAILS} +emails: 49 STRING {'raiseonunterminatedread': False} email 2 UINT ringtone "ringtone index for a call" 2 UINT msgringtone "ringtone index for a text message" 2 UINT wallpaper * LIST {'length': NUMPHONENUMBERS} +numbertypes: 1 UINT numbertype * LIST {'length': NUMPHONENUMBERS} +numbers: 49 STRING {'raiseonunterminatedread': False} number * UNKNOWN +unknown PACKET pbreadentryresponse: "Results of reading one entry" * pbheader header * pbentry entry PACKET pbupdateentryrequest: * pbheader {'command': 0x04, 'flag': 0x01} +header * pbentry entry PACKET pbappendentryrequest: * pbheader {'command': 0x03, 'flag': 0x01} +header * pbentry entry # Schedule (Calendar) PACKET scheduleexception: 4 UINT pos "Refers to event id (position in schedule file) that this suppresses" 1 UINT day 1 UINT month 2 UINT year PACKET scheduleexceptionfile: * LIST {'elementclass': scheduleexception} +items #63 bytes # P UINT { 'constant': 60 } packet_size "Faster than packetsize()" # 1 UINT { 'default': 0 } +pad2 PACKET scheduleevent: 4 UINT pos "position within file, used as an event id" 4 LGCALDATE start 4 LGCALDATE end 1 UINT repeat 2 UINT daybitmap "which days a weekly repeat event happens on" 1 UINT alarmminutes "a value of 100 indicates not set" 1 UINT alarmhours "a value of 100 indicates not set" 1 UINT alarmtype "preset alarm reminder type" 1 UINT { 'default': 0 } +snoozedelay "in minutes, not for this phone" 1 UINT ringtone 4 UINT { 'default': 0 } +pad2 39 STRING {'raiseontruncate': False } description PACKET schedulefile: 2 UINT numactiveitems * LIST {'elementclass': scheduleevent} +events # Text Memos PACKET textmemo: 151 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } text PACKET textmemofile: 4 UINT itemcount * LIST { 'elementclass': textmemo } +items # calling history file PACKET callentry: 4 GPSDATE datetime 4 UNKNOWN pad1 4 UINT duration 49 STRING { 'raiseonunterminatedread': False } number 36 STRING { 'raiseonunterminatedread': False } name 8 UNKNOWN pad2 PACKET callhistoryfile: 4 UINT itemcount 1 UNKNOWN pad1 * LIST { 'elementclass': callentry } +items # SMS stuff PACKET SMSInboxFile: 113 UNKNOWN pad1 4 LGCALDATE datetime 10 UNKNOWN pad2 1 UINT locked 9 UNKNOWN pad3 3770 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } text 57 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } _from 47 UNKNOWN pad4 57 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } callback * UNKNOWN pad5 PACKET SMSSavedFile: 4 UINT outboxmsg 4 UNKNOWN pad if self.outboxmsg: * SMSOutboxFile outbox if not self.outboxmsg: * SMSInboxFile inbox PACKET SMSOutboxFile: 4 UNKNOWN pad1 1 UINT locked 4 LGCALDATE datetime 1610 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } text 4 UNKNOWN pad2 35 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } callback 35 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } _to * UNKNOWN pad3 PACKET SMSCannedMsg: 101 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False, 'default': '' } +text PACKET SMSCannedFile: * LIST { 'length': SMS_CANNED_MAX_ITEMS, 'elementclass': SMSCannedMsg } +items ### ### SMS ### # # There are 3 types of SMS records, The inbox, outbox and unsent (pending) # Unlike other records in the phone each message is stored in a separate file # All messages are in the 'sms' directory in the root of the phone # Inbox messages are in files called 'inbox000.dat', the number 000 varies for # each message, typically there are no gaps in the numbering, but gaps can appear # if a message is deleted. # Outbox message are named 'outbox000.dat', unsent messages are named 'sf00.dat', # only two digit file name that suggests a max of 100 message for this type. # Messages in the outbox get updated when the message is received by the recipient, # they contain a delivery flag and a delivery time for all the possible 10 recipients. # The vx8100 supports SMS contatination, this allows you to send text messages that are # longer than 160 characters. The format is different for these type of messages, but # it is supported by this implementation. # The vx8100 also allows you to put small graphics, sounds and animations in a message. # This implementation does not support these, if they are contained in a message they # will be ignored and just the text will be shown when you view the message in bitpim. # The text in the the messages is stored in 7-bit characters, so they have # to be unpacked, in concatinated messages and messages with embeded graphics etc. the # format uses the GSM 03.38 specified format, a good example of this can be found at # "http://www.dreamfabric.com/sms/hello.html". # For simple messages less than 161 characters with no graphics the format is simpler, # the 7-bit characters are just packed into memory in the order they appear in the # message. PACKET msg_record: # the first few fields in this packet have something to do with the type of SMS # message contained. EMS and concatinated text are coded differently than a # simple text message 1 UINT binary # 0=simple text, 1=binary/concatinated 1 UINT unknown3 # 0=simple text, 1=binary/concatinated 1 UINT unknown4 # 0 1 UINT unknown6 # 2=simple text, 9=binary/concatinated 1 UINT length * LIST {'length': 220} +msg: 1 UINT byte "individual byte of message" PACKET recipient_record: 49 STRING number 2 UINT status # 1 when sent, 5 when received, 2 failed to send 4 LGCALDATE timesent 4 LGCALDATE timereceived 49 UNKNOWN unknown2 PACKET sms_saved: 4 UINT outboxmsg 4 UNKNOWN pad if self.outboxmsg: * sms_out outbox if not self.outboxmsg: * sms_in inbox PACKET sms_out: 4 UINT index # starting from 1, unique 1 UINT locked # 1=locked 1 UNKNOWN unknown2 4 LGCALDATE timesent # time the message was sent 6 UNKNOWN unknown2 21 STRING subject 1 UNKNOWN unknown4 2 UINT num_msg_elements # up to 10 * LIST {'elementclass': msg_record, 'length': 7} +messages 14 UNKNOWN unknown1 1 UINT priority # 0=normal, 1=high 1 UNKNOWN unknown5 35 STRING callback * LIST {'elementclass': recipient_record,'length': 9} +recipients * UNKNOWN pad PACKET SMSINBOXMSGFRAGMENT: * LIST {'length': 181} +msg: # this size could be wrong 1 UINT byte "individual byte of message" PACKET sms_in: 10 UNKNOWN unknown1 6 SMSDATE timesent 3 UINT unknown2 1 UINT callback_length # 0 for no callback number 38 STRING callback 1 UINT sender_length * LIST {'length': 38} +sender: 1 UINT byte "individual byte of senders phone number" 12 DATA unknown3 # set to zeros 4 LGCALDATE lg_time # time the message was sent 3 UNKNOWN unknown4 4 GPSDATE GPStime # num seconds since 0h 1-6-80, time message received by phone 4 UINT unknown5 # zero 1 UINT read # 1 if message has been read, 0 otherwise 1 UINT locked # 1 if the message is locked, 0 otherwise 8 UINT unknown6 # zero 1 UINT priority # 1 if the message is high priority, 0 otherwise 21 STRING subject 1 UINT bin_header1 # 0 in simple message 1 if the message contains a binary header 1 UINT bin_header2 # 0 in simple message 9 if the message contains a binary header 4 UINT unknown7 # zeros 2 UINT multipartID # multi-part message ID, used for concatinated messages only 1 UINT bin_header3 # 0 in simple message 2 if the message contains a binary header 1 UINT num_msg_elements # max 10 elements (guessing on max here) * LIST {'length': 10} +msglengths: 1 UINT msglength "lengths of individual messages in septets" 10 UNKNOWN unknown8 * LIST {'length': 10, 'elementclass': SMSINBOXMSGFRAGMENT} +msgs # 181 bytes per message, uncertain on this, no multipart message available # 20 messages, 7-bit ascii for simple text. for binary header # first byte is header length not including the length byte # rest depends on content of header, not known at this time. # text alway follows the header although the format it different # than a simple SMS * UNKNOWN unknown9