RE: SMS on VX4400
"Simon C" <[email protected]>
| Newsgroups | gmane.comp.mobile.bitpim.devel |
|---|---|
| Message-ID | <000801c5a7a7$58d07280$6400a8c0@HOME> |
I've moved the 8100 SMS and call history code into the 4400 so the 8100 and 4400 now share the same code. The only difference is the sms PACKET definitions. I also added call history to the 6100 using the common code in the 4400, with different call history PACKETs Change summary: 8100: added memo, autodetect, phone info, changed ringtone directory, minor bug fixes. 6100: added autodetect, phone info, call history 4400: added SMS, call history I'd appreciate if someone could try the 4400 SMS changes out. The phone I have does not have service, so I have not tested with any inbox message that contain non-text information like ringtones. Simon Code attached with diffs.
com_lgvx4400.py
(application/octet-stream, 49.4 KB)
### BITPIM ### ### Copyright (C) 2003-2004 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_lgvx4400.py,v 1.130 2005/08/22 12:13:36 djpham Exp $ """Communicate with the LG VX4400 cell phone""" # standard modules import re import time import cStringIO import sha # my modules import common import copy import p_lgvx4400 import com_brew import com_phone import com_lg import prototypes import fileinfo import call_history import sms class Phone(com_phone.Phone,com_brew.BrewProtocol,com_lg.LGPhonebook,com_lg.LGIndexedMedia): "Talk to the LG VX4400 cell phone" desc="LG-VX4400" wallpaperindexfilename="dloadindex/brewImageIndex.map" ringerindexfilename="dloadindex/brewRingerIndex.map" protocolclass=p_lgvx4400 serialsname='lgvx4400' imagelocations=( # offset, index file, files location, type, maximumentries ( 10, "dloadindex/brewImageIndex.map", "brew/shared", "images", 30), ) ringtonelocations=( # offset, index file, files location, type, maximumentries ( 50, "dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 30), ) builtinimages=('Balloons', 'Soccer', 'Basketball', 'Bird', 'Sunflower', 'Puppy', 'Mountain House', 'Beach') builtinringtones=( 'Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5', 'Ring 6', 'Voices of Spring', 'Twinkle Twinkle', 'The Toreadors', 'Badinerie', 'The Spring', 'Liberty Bell', 'Trumpet Concerto', 'Eine Kleine', 'Silken Ladder', 'Nocturne', 'Csikos Post', 'Turkish March', 'Mozart Aria', 'La Traviata', 'Rag Time', 'Radetzky March', 'Can-Can', 'Sabre Dance', 'Magic Flute', 'Carmen' ) def __init__(self, logtarget, commport): "Calls all the constructors and sets initial modes" com_phone.Phone.__init__(self, logtarget, commport) com_brew.BrewProtocol.__init__(self) com_lg.LGPhonebook.__init__(self) com_lg.LGIndexedMedia.__init__(self) self.log("Attempting to contact phone") self.mode=self.MODENONE def getfundamentals(self, results): """Gets information fundamental to interopating with the phone and UI. Currently this is: - 'uniqueserial' a unique serial number representing the phone - 'groups' the phonebook groups - 'wallpaper-index' map index numbers to names - 'ringtone-index' map index numbers to ringtone names This method is called before we read the phonebook data or before we write phonebook data. """ # use a hash of ESN and other stuff (being paranoid) self.log("Retrieving fundamental phone information") self.log("Phone serial number") results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() # now read groups self.log("Reading group information") buf=prototypes.buffer(self.getfilecontents("pim/pbgroup.dat")) g=self.protocolclass.pbgroups() g.readfrombuffer(buf) self.logdata("Groups read", buf.getdata(), g) groups={} for i in range(len(g.groups)): if len(g.groups[i].name): # sometimes have zero length names groups[i]={ 'icon': g.groups[i].icon, 'name': g.groups[i].name } results['groups']=groups self.getwallpaperindices(results) self.getringtoneindices(results) self.log("Fundamentals retrieved") return results 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("sms/mediacan000.dat", buf.getvalue()) return _smspatterns={'Inbox': re.compile(r"^.*/inbox[0-9][0-9][0-9]\.dat$"), 'Sent': re.compile(r"^.*/outbox[0-9][0-9][0-9]\.dat$"), 'Saved': re.compile(r"^.*/sf[0-9][0-9]\.dat$"), } def getsms(self, result): res={} # get the quicktext (LG name for canned messages) result['canned_msg']=self._getquicktext() # 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._smspatterns.items(): if pat.match(item['name']): folder=f break if folder=='Inbox': self._getinboxmessage(item['name'], res) elif folder=='Sent': self._getoutboxmessage(item['name'], res) elif folder=='Saved': self._getsavedmessage(item['name'], res) result['sms']=res return result 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, file, res): buf=prototypes.buffer(self.getfilecontents(file)) sf=self.protocolclass.sms_in() sf.readfrombuffer(buf) self.logdata("SMS message in file "+file, buf.getdata(), 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 res[entry.id]=entry return def _getoutboxmessage(self, file, res): buf=prototypes.buffer(self.getfilecontents(file)) sf=self.protocolclass.sms_out() sf.readfrombuffer(buf) self.logdata("SMS message in file "+file, buf.getdata(), sf) entry=sms.SMSEntry() entry.folder=entry.Folder_Sent entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) # add all the recipients for i in range(10): if sf.recipients[i].number!="": confirmed=(sf.recipients[i].status==5) confirmed_date="" if confirmed: confirmed_date="%d%02d%02dT%02d%02d00" % ((sf.recipients[i].timereceived)) entry.add_recipient(sf.recipients[i].number, confirmed, confirmed_date) entry.subject=sf.subject txt="" if sf.num_msg_elements==1 and sf.messages[0].unknown2==0: 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 res[entry.id]=entry return def _getsavedmessage(self, file, res): buf=prototypes.buffer(self.getfilecontents(file)) sf=self.protocolclass.sms_saved() sf.readfrombuffer(buf) self.logdata("SMS message in file "+file, buf.getdata(), sf) entry=sms.SMSEntry() entry.folder=entry.Folder_Saved entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) # add all the recipients, unlike the outbox all 10 are the same format for i in range(10): if sf.recipients[i].number!="": confirmed=(sf.recipients[i].status==5) confirmed_date="" if confirmed: confirmed_date="%d%02d%02dT%02d%02d00" % ((sf.recipients[i].timereceived)) entry.add_recipient(sf.recipients[i].number, confirmed, confirmed_date) entry.subject=sf.subject txt="" if sf.num_msg_elements==1 and sf.messages[0].unknown2==0: 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 res[entry.id]=entry return 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 def getcallhistory(self, result): res={} # read the incoming call history file self._readhistoryfile("pim/missed_log.dat", 'Missed', res) self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res) self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res) result['call_history']=res return result def _readhistoryfile(self, fname, folder, res): try: buf=prototypes.buffer(self.getfilecontents(fname)) ch=self.protocolclass.callhistory() ch.readfrombuffer(buf) self.logdata("Call History", buf.getdata(), ch) for call in ch.calls: if call.number=='' and call.name=='': continue entry=call_history.CallHistoryEntry() entry.folder=folder entry.datetime=((call.GPStime)) if call.number=='': # restricted calls have no number entry.number=call.name else: entry.number=call.number res[entry.id]=entry except com_brew.BrewNoSuchFileException: pass # do nothing if file doesn't exist return def getwallpaperindices(self, results): return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index') def getringtoneindices(self, results): return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index') def getphonebook(self,result): """Reads the phonebook data. The L{getfundamentals} information will already be in result.""" # Read speed dials first speeds={} try: if self.protocolclass.NUMSPEEDDIALS: self.log("Reading speed dials") buf=prototypes.buffer(self.getfilecontents("pim/pbspeed.dat")) sd=self.protocolclass.speeddials() sd.readfrombuffer(buf) for i in range(self.protocolclass.FIRSTSPEEDDIAL, self.protocolclass.LASTSPEEDDIAL+1): if sd.speeddials[i].entry<0 or sd.speeddials[i].entry>self.protocolclass.NUMPHONEBOOKENTRIES: continue l=speeds.get(sd.speeddials[i].entry, []) l.append((i, sd.speeddials[i].number)) speeds[sd.speeddials[i].entry]=l except com_brew.BrewNoSuchFileException: pass pbook={} # Bug in the phone. if you repeatedly read the phone book it starts # returning a random number as the number of entries. We get around # this by switching into brew mode which clears that. self.mode=self.MODENONE self.setmode(self.MODEBREW) self.log("Reading number of phonebook entries") req=self.protocolclass.pbinforequest() res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) numentries=res.numentries if numentries<0 or numentries>1000: self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") numentries=0 firstserial=None loop=xrange(0,1000) hardway=True else: self.log("There are %d entries" % (numentries,)) loop=xrange(0, numentries) hardway=False # reset cursor self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) problemsdetected=False dupecheck={} for i in loop: if hardway: numentries+=1 req=self.protocolclass.pbreadentryrequest() res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) self.log("Read entry "+`i`+" - "+res.entry.name) entry=self.extractphonebookentry(res.entry, speeds, result) if hardway and firstserial is None: firstserial=res.entry.serial1 pbook[i]=entry if res.entry.serial1 in dupecheck: self.log("Entry %s has same serial as entry %s. This will cause problems." % (`entry`, dupecheck[res.entry.serial1])) problemsdetected=True else: dupecheck[res.entry.serial1]=entry self.progress(i, numentries, res.entry.name) #### Advance to next entry req=self.protocolclass.pbnextentryrequest() res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) if hardway: # look to see if we have looped if res.serial==firstserial or res.serial==0: break self.progress(numentries, numentries, "Phone book read completed") if problemsdetected: self.log("There are duplicate serial numbers. See above for details.") raise common.IntegrityCheckFailed(self.desc, "Data in phonebook is inconsistent. There are multiple entries with the same serial number. See the log.") result['phonebook']=pbook cats=[] for i in result['groups']: if result['groups'][i]['name']!='No Group': cats.append(result['groups'][i]['name']) result['categories']=cats print "returning keys",result.keys() return pbook def savegroups(self, data): groups=data['groups'] keys=groups.keys() keys.sort() g=self.protocolclass.pbgroups() for k in keys: e=self.protocolclass.pbgroup() e.icon=groups[k]['icon'] e.name=groups[k]['name'] g.groups.append(e) buffer=prototypes.buffer() g.writetobuffer(buffer) self.logdata("New group file", buffer.getvalue(), g) self.writefile("pim/pbgroup.dat", buffer.getvalue()) def savephonebook(self, data): "Saves out the phonebook" self.savegroups(data) progressmax=len(data['phonebook'].keys()) # if we are going to write out speeddials, we have to re-read the entire # phonebook again if data.get('speeddials',None) is not None: progressmax+=len(data['phonebook'].keys()) # To write the phone book, we scan through all existing entries # and record their record number and serials. # We then delete any entries that aren't in data # We then write out our records, using overwrite or append # commands as necessary serialupdates=[] existingpbook={} # keep track of the phonebook that is on the phone self.mode=self.MODENONE self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary self.setmode(self.MODEPHONEBOOK) # similar loop to reading req=self.protocolclass.pbinforequest() res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) numexistingentries=res.numentries if numexistingentries<0 or numexistingentries>1000: self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") numexistingentries=0 firstserial=None loop=xrange(0,1000) hardway=True else: self.log("There are %d existing entries" % (numexistingentries,)) progressmax+=numexistingentries loop=xrange(0, numexistingentries) hardway=False progresscur=0 # reset cursor self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) for i in loop: ### Read current entry if hardway: numexistingentries+=1 progressmax+=1 req=self.protocolclass.pbreadentryrequest() res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) entry={ 'number': res.entry.entrynumber, 'serial1': res.entry.serial1, 'serial2': res.entry.serial2, 'name': res.entry.name} assert entry['serial1']==entry['serial2'] # always the same self.log("Reading entry "+`i`+" - "+entry['name']) if hardway and firstserial is None: firstserial=res.entry.serial1 existingpbook[i]=entry self.progress(progresscur, progressmax, "existing "+entry['name']) #### Advance to next entry req=self.protocolclass.pbnextentryrequest() res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) progresscur+=1 if hardway: # look to see if we have looped if res.serial==firstserial or res.serial==0: break # we have now looped around back to begining # Find entries that have been deleted pbook=data['phonebook'] dellist=[] for i in range(0, numexistingentries): ii=existingpbook[i] serial=ii['serial1'] item=self._findserial(serial, pbook) if item is None: dellist.append(i) progressmax+=len(dellist) # more work to do # Delete those entries for i in dellist: progresscur+=1 numexistingentries-=1 # keep count right ii=existingpbook[i] self.log("Deleting entry "+`i`+" - "+ii['name']) req=self.protocolclass.pbdeleteentryrequest() req.serial1=ii['serial1'] req.serial2=ii['serial2'] req.entrynumber=ii['number'] self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse) self.progress(progresscur, progressmax, "Deleting "+ii['name']) # also remove them from existingpbook del existingpbook[i] # counter to keep track of record number (otherwise appends don't work) counter=0 # Now rewrite out existing entries keys=existingpbook.keys() existingserials=[] keys.sort() # do in same order as existingpbook for i in keys: progresscur+=1 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)] self.log("Rewriting entry "+`i`+" - "+ii['name']) self.progress(progresscur, progressmax, "Rewriting "+ii['name']) entry=self.makeentry(counter, ii, data) counter+=1 existingserials.append(existingpbook[i]['serial1']) req=self.protocolclass.pbupdateentryrequest() req.entry=entry res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse) serialupdates.append( ( ii["bitpimserial"], {'sourcetype': self.serialsname, 'serial1': res.serial1, 'serial2': res.serial1, 'sourceuniqueid': data['uniqueserial']}) ) assert ii['serial1']==res.serial1 # serial should stay the same # Finally write out new entries keys=pbook.keys() keys.sort() for i in keys: ii=pbook[i] if ii['serial1'] in existingserials: continue # already wrote this one out progresscur+=1 entry=self.makeentry(counter, ii, data) counter+=1 self.log("Appending entry "+ii['name']) self.progress(progresscur, progressmax, "Writing "+ii['name']) req=self.protocolclass.pbappendentryrequest() req.entry=entry res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse) serialupdates.append( ( ii["bitpimserial"], {'sourcetype': self.serialsname, 'serial1': res.newserial, 'serial2': res.newserial, 'sourceuniqueid': data['uniqueserial']}) ) data["serialupdates"]=serialupdates # deal with the speeddials if data.get("speeddials",None) is not None: # Yes, we have to read the ENTIRE phonebook again. This # is because we don't know which entry numbers actually # got assigned to the various entries, and we need the # actual numbers to assign to the speed dials newspeeds={} if len(data['speeddials']): # Move cursor to begining of phonebook self.mode=self.MODENONE self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary self.setmode(self.MODEPHONEBOOK) self.log("Searching for speed dials") self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) for i in range(len(pbook)): ### Read current entry req=self.protocolclass.pbreadentryrequest() res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) self.log("Scanning "+res.entry.name) progresscur+=1 # we have to turn the entry serial number into a bitpim serial serial=res.entry.serial1 found=False for bps, serials in serialupdates: if serials['serial1']==serial: # found the entry for sd in data['speeddials']: xx=data['speeddials'][sd] if xx[0]==bps: found=True if(getattr(self.protocolclass, 'SPEEDDIALINDEX', 0)==0): newspeeds[sd]=(res.entry.entrynumber, xx[1]) else: newspeeds[sd]=(res.entry.entrynumber, res.entry.numbertypes[xx[1]].numbertype) nt=self.protocolclass.numbertypetab[res.entry.numbertypes[xx[1]].numbertype] self.log("Speed dial #%d = %s (%s/%d)" % (sd, res.entry.name, nt, xx[1])) self.progress(progresscur, progressmax, "Speed dial #%d = %s (%s/%d)" % (sd, res.entry.name, nt, xx[1])) if not found: self.progress(progresscur, progressmax, "Scanning "+res.entry.name) # move to next entry self.sendpbcommand(self.protocolclass.pbnextentryrequest(), self.protocolclass.pbnextentryresponse) self.progress(progressmax, progressmax, "Finished scanning") print "new speed dials is",newspeeds req=self.protocolclass.speeddials() for i in range(self.protocolclass.NUMSPEEDDIALS): sd=self.protocolclass.speeddial() if i in newspeeds: sd.entry=newspeeds[i][0] sd.number=newspeeds[i][1] req.speeddials.append(sd) buffer=prototypes.buffer() req.writetobuffer(buffer) # We check the existing speed dial file as changes require a reboot self.log("Checking existing speed dials") if buffer.getvalue()!=self.getfilecontents("pim/pbspeed.dat"): self.logdata("New speed dial file", buffer.getvalue(), req) self.writefile("pim/pbspeed.dat", buffer.getvalue()) self.log("Your phone has to be rebooted due to the speed dials changing") self.progress(progressmax, progressmax, "Rebooting phone") data["rebootphone"]=True else: self.log("No changes to speed dials") return data def _findserial(self, serial, dict): """Searches dict to find entry with matching serial. If not found, returns None""" for i in dict: if dict[i]['serial1']==serial: return i return None def getcalendar(self,result): res={} # Read exceptions file first try: buf=prototypes.buffer(self.getfilecontents("sch/schexception.dat")) ex=self.protocolclass.scheduleexceptionfile() ex.readfrombuffer(buf) self.logdata("Calendar exceptions", buf.getdata(), ex) exceptions={} for i in ex.items: try: exceptions[i.pos].append( (i.year,i.month,i.day) ) except KeyError: exceptions[i.pos]=[ (i.year,i.month,i.day) ] except com_brew.BrewNoSuchFileException: exceptions={} # Now read schedule try: buf=prototypes.buffer(self.getfilecontents("sch/schedule.dat")) 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) for event in sc.events: entry={} entry['pos']=event.pos if entry['pos']==-1: continue # blanked entry # normal fields for field in 'start','end','daybitmap','changeserial','snoozedelay','ringtone','description': entry[field]=getattr(event,field) # calculated ones try: entry['repeat']=self._calrepeatvalues[event.repeat] except KeyError: entry['repeat']=None min=event.alarmminutes hour=event.alarmhours if min==100 or hour==100: entry['alarm']=None # no alarm set else: entry['alarm']=hour*60+min # Exceptions if exceptions.has_key(event.pos): entry['exceptions']=exceptions[event.pos] res[event.pos]=entry assert sc.numactiveitems==len(res) except com_brew.BrewNoSuchFileException: pass # do nothing if file doesn't exist result['calendar']=res return result def savecalendar(self, dict, merge): # ::TODO:: obey merge param # what will be written to the files eventsf=self.protocolclass.schedulefile() exceptionsf=self.protocolclass.scheduleexceptionfile() # what are we working with cal=dict['calendar'] newcal={} keys=cal.keys() keys.sort() # number of entries eventsf.numactiveitems=len(keys) # play with each entry for k in keys: # entry is what we will return to user entry=cal[k] data=self.protocolclass.scheduleevent() data.pos=eventsf.packetsize() entry['pos']=data.pos # simple copy of these fields for field in 'start', 'end', 'daybitmap', 'changeserial', 'snoozedelay','ringtone','description': v=entry[field] if field == "description": v=v[:self.protocolclass.MAXCALENDARDESCRIPTION] setattr(data,field,v) # And now the special ones repeat=None for k,v in self._calrepeatvalues.items(): if entry['repeat']==v: repeat=k break assert repeat is not None data.repeat=repeat # alarm 100 indicates not set if entry['alarm'] is None or entry['alarm']<0: hour=100 min=100 else: assert entry['alarm']>=0 hour=entry['alarm']/60 min=entry['alarm']%60 data.alarmminutes=min data.alarmhours=hour # update exceptions if needbe if entry.has_key('exceptions'): for y,m,d in entry['exceptions']: de=self.protocolclass.scheduleexception() de.pos=data.pos de.day=d de.month=m de.year=y exceptionsf.items.append(de) # put entry in nice shiny new dict we are building entry=copy.copy(entry) newcal[data.pos]=entry eventsf.events.append(data) # scribble everything out buf=prototypes.buffer() eventsf.writetobuffer(buf) self.logdata("Writing calendar", buf.getvalue(), eventsf) self.writefile("sch/schedule.dat", buf.getvalue()) buf=prototypes.buffer() exceptionsf.writetobuffer(buf) self.logdata("Writing calendar exceptions", buf.getvalue(), exceptionsf) self.writefile("sch/schexception.dat", buf.getvalue()) # fix passed in dict dict['calendar']=newcal return dict _calrepeatvalues={ 0x10: None, 0x11: 'daily', 0x12: 'monfri', 0x13: 'weekly', 0x14: 'monthly', 0x15: 'yearly' } def _normaliseindices(self, d): "turn all negative keys into positive ones for index" res={} keys=d.keys() keys.sort() keys.reverse() for k in keys: if k<0: for c in range(999999): if c not in keys and c not in res: break res[c]=d[k] else: res[k]=d[k] return res def extractphonebookentry(self, entry, speeds, fundamentals): """Return a phonebook entry in BitPim format. This is called from getphonebook.""" res={} # serials res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 'serial2': entry.serial2, 'sourceuniqueid': fundamentals['uniqueserial']} ] # only one name res['names']=[ {'full': entry.name} ] # only one category cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name'] if cat!="No Group": res['categories']=[ {'category': cat} ] # emails res['emails']=[] for i in entry.emails: if len(i.email): res['emails'].append( {'email': i.email} ) if not len(res['emails']): del res['emails'] # it was empty # urls if 'url' in entry.getfields() and len(entry.url): res['urls']=[ {'url': entry.url} ] # private if 'secret' in entry.getfields() and entry.secret: # we only supply secret if it is true res['flags']=[ {'secret': entry.secret } ] # memos if 'memo' in entry.getfields() and len(entry.memo): res['memos']=[ {'memo': entry.memo } ] # wallpapers if entry.wallpaper!=self.protocolclass.NOWALLPAPER: try: paper=fundamentals['wallpaper-index'][entry.wallpaper]['name'] res['wallpapers']=[ {'wallpaper': paper, 'use': 'call'} ] except: print "can't find wallpaper for index",entry.wallpaper pass # ringtones res['ringtones']=[] if 'ringtone' in entry.getfields() and entry.ringtone!=self.protocolclass.NORINGTONE: try: tone=fundamentals['ringtone-index'][entry.ringtone]['name'] res['ringtones'].append({'ringtone': tone, 'use': 'call'}) except: print "can't find ringtone for index",entry.ringtone if 'msgringtone' in entry.getfields() and entry.msgringtone!=self.protocolclass.NOMSGRINGTONE: try: tone=fundamentals['ringtone-index'][entry.msgringtone]['name'] res['ringtones'].append({'ringtone': tone, 'use': 'message'}) except: print "can't find ringtone for index",entry.msgringtone if len(res['ringtones'])==0: del res['ringtones'] if(getattr(self.protocolclass, 'SPEEDDIALINDEX', 0)==0): res=self._assignpbtypeandspeeddialsbyposition(entry, speeds, res) else: res=self._assignpbtypeandspeeddialsbytype(entry, speeds, res) return res def _assignpbtypeandspeeddialsbyposition(self, entry, speeds, res): # numbers res['numbers']=[] for i in range(self.protocolclass.NUMPHONENUMBERS): num=entry.numbers[i].number type=entry.numbertypes[i].numbertype if len(num): t=self.protocolclass.numbertypetab[type] if t[-1]=='2': t=t[:-1] res['numbers'].append({'number': num, 'type': t}) # speed dials if entry.entrynumber in speeds: for speeddial,numberindex in speeds[entry.entrynumber]: try: res['numbers'][numberindex]['speeddial']=speeddial except IndexError: print "speed dial refers to non-existent number\n",res['numbers'],"\n",numberindex,speeddial return res def _assignpbtypeandspeeddialsbytype(self, entry, speeds, res): # for some phones (e.g. vx8100) the speeddial numberindex is really the numbertype (now why would LG want to change this!) res['numbers']=[] for i in range(self.protocolclass.NUMPHONENUMBERS): num=entry.numbers[i].number type=entry.numbertypes[i].numbertype if len(num): t=self.protocolclass.numbertypetab[type] if t[-1]=='2': t=t[:-1] res['numbers'].append({'number': num, 'type': t}) # if this is a speeddial number set it if entry.entrynumber in speeds and speeds[entry.entrynumber][0][1]==entry.numbertypes[i].numbertype: res['numbers'][i]['speeddial']=speeds[entry.entrynumber][0][0] return res def _findmediainindex(self, index, name, pbentryname, type): if type=="ringtone": default=self.protocolclass.NORINGTONE elif type=="message ringtone": default=self.protocolclass.NOMSGRINGTONE elif type=="wallpaper": default=self.protocolclass.NOWALLPAPER else: assert False, "unknown type "+type if name is None: return default for i in index: if index[i]['name']==name: return i self.log("%s: Unable to find %s %s in the index. Setting to default." % (pbentryname, type, name)) return default def makeentry(self, counter, entry, data): """Creates pbentry object @param counter: The new entry number @param entry: The phonebook object (as returned from convertphonebooktophone) that we are using as the source @param data: The main dictionary, which we use to get access to media indices amongst other things """ e=self.protocolclass.pbentry() e.entrynumber=counter for k in entry: # special treatment for lists if k in ('emails', 'numbers', 'numbertypes'): l=getattr(e,k) for item in entry[k]: l.append(item) elif k=='ringtone': e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') elif k=='msgringtone': e.msgringtone=self._findmediainindex(data['ringtone-index'], entry['msgringtone'], entry['name'], 'message ringtone') elif k=='wallpaper': e.wallpaper=self._findmediainindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper') elif k in e.getfields(): # everything else we just set setattr(e,k,entry[k]) return e def phonize(str): """Convert the phone number into something the phone understands All digits, P, T, * and # are kept, everything else is removed""" return re.sub("[^0-9PT#*]", "", str) parentprofile=com_phone.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname WALLPAPER_WIDTH=120 WALLPAPER_HEIGHT=98 MAX_WALLPAPER_BASENAME_LENGTH=19 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ." WALLPAPER_CONVERT_FORMAT="bmp" MAX_RINGTONE_BASENAME_LENGTH=19 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ." # which usb ids correspond to us usbids_straight=( ( 0x1004, 0x6000, 2), )# VID=LG Electronics, PID=LG VX4400/VX6000 -internal USB diagnostics interface usbids_usbtoserial=( ( 0x067b, 0x2303, None), # VID=Prolific, PID=USB to serial ( 0x0403, 0x6001, None), # VID=FTDI, PID=USB to serial ( 0x0731, 0x2003, None), # VID=Susteen, PID=Universal USB to serial ) usbids=usbids_straight+usbids_usbtoserial # which device classes we are. not we are not modem! deviceclasses=("serial",) # nb we don't allow save to camera so it isn't listed here 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': 120, 'height': 98, 'format': "BMP"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", {'width': 120, 'height': 98, 'format': "BMP"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", {'width': 120, 'height': 133, 'format': "BMP"})) def GetTargetsForImageOrigin(self, origin): return self.imagetargets def __init__(self): parentprofile.__init__(self) def _getgroup(self, name, groups): for key in groups: if groups[key]['name']==name: return key,groups[key] return None,None def normalisegroups(self, helper, data): "Assigns groups based on category data" pad=[] keys=data['groups'].keys() keys.sort() for k in keys: if k: # ignore key 0 which is 'No Group' name=data['groups'][k]['name'] pad.append(name) groups=helper.getmostpopularcategories(10, data['phonebook'], ["No Group"], 22, pad) # alpha sort groups.sort() # newgroups newgroups={} # put in No group newgroups[0]={'name': 'No Group', 'icon': 0} # populate for name in groups: # existing entries remain unchanged if name=="No Group": continue key,value=self._getgroup(name, data['groups']) if key is not None and key!=0: newgroups[key]=value # new entries get whatever numbers are free for name in groups: key,value=self._getgroup(name, newgroups) if key is None: for key in range(1,100000): if key not in newgroups: newgroups[key]={'name': name, 'icon': 1} break # yay, done if data['groups']!=newgroups: data['groups']=newgroups data['rebootphone']=True def convertphonebooktophone(self, helper, data): """Converts the data to what will be used by the phone @param data: contains the dict returned by getfundamentals as well as where the results go""" results={} speeds={} self.normalisegroups(helper, data) for pbentry in data['phonebook']: if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: break e={} # entry out entry=data['phonebook'][pbentry] # entry in try: # serials serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0) serial2=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial2', serial1) e['serial1']=serial1 e['serial2']=serial2 for ss in entry["serials"]: if ss["sourcetype"]=="bitpim": e['bitpimserial']=ss assert e['bitpimserial'] # name e['name']=helper.getfullname(entry.get('names', []),1,1,22)[0] # categories/groups cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,22), None) if cat is None: e['group']=0 else: key,value=self._getgroup(cat, data['groups']) if key is not None: e['group']=key else: # sorry no space for this category e['group']=0 # email addresses emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,48) e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") # url e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "") # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") # phone numbers # there must be at least one email address or phonenumber minnumbers=1 if len(emails): minnumbers=0 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) e['numbertypes']=[] e['numbers']=[] for numindex in range(len(numbers)): num=numbers[numindex] # deal with type b4=len(e['numbertypes']) type=num['type'] for i,t in enumerate(self.protocolclass.numbertypetab): if type==t: # some voodoo to ensure the second home becomes home2 if i in e['numbertypes'] and t[-1]!='2': type+='2' continue e['numbertypes'].append(i) break if t=='none': # conveniently last entry e['numbertypes'].append(i) break if len(e['numbertypes'])==b4: # we couldn't find a type for the number continue # deal with number number=phonize(num['number']) if len(number)==0: # no actual digits in the number continue if len(number)>48: # get this number from somewhere sensible # ::TODO:: number is too long and we have to either truncate it or ignore it? number=number[:48] # truncate for moment e['numbers'].append(number) # deal with speed dial sd=num.get("speeddial", -1) if self.protocolclass.NUMSPEEDDIALS: if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: speeds[sd]=(e['bitpimserial'], numindex) e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0) e['numbers']=helper.filllist(e['numbers'], 5, "") # ringtones, wallpaper e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) e['msgringtone']=helper.getringtone(entry.get('ringtones', []), 'message', None) e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) # flags e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False) results[pbentry]=e except helper.ConversionFailed: continue if self.protocolclass.NUMSPEEDDIALS: data['speeddials']=speeds data['phonebook']=results return data _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 ('call_history', 'read', None),# all call history list reading ('sms', 'read', None), # all SMS list 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'), ('sms', 'write', 'OVERWRITE'), # all SMS list writing ) def QueryAudio(self, origin, currentextension, afi): # we don't modify any of these if afi.format in ("MIDI", "QCP", "PMD"): return currentextension, afi # examine mp3 if afi.format=="MP3": if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: return currentextension, afi # convert it return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050}))
com_lgvx6100.py
(application/octet-stream, 9.4 KB)
### BITPIM ### ### Copyright (C) 2003-2004 Roger Binns <[email protected]> ### Copyright (C) 2004 John O'Shaughnessy <[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_lgvx6100.py,v 1.9 2005/05/02 17:15:05 djpham Exp $ """Communicate with the LG VX6100 cell phone The VX6100 is substantially similar to the VX4400 except that it supports more image formats, has wallpapers in no less than 5 locations and puts things in slightly different directories. The code in this file mainly inherits from VX4400 code and then extends where the 6100 has extra functionality """ # standard modules import time import cStringIO import sha # my modules import common import commport import copy import p_brew import p_lgvx6100 import com_lgvx4400 import com_brew import com_phone import com_lg import prototypes import call_history class Phone(com_lgvx4400.Phone): "Talk to the LG VX6100 cell phone" desc="LG-VX6100" protocolclass=p_lgvx6100 serialsname='lgvx6100' # more VX6100 indices imagelocations=( # offset, index file, files location, type, maximumentries ( 16, "download/dloadindex/brewImageIndex.map", "brew/shared", "images", 60) , ( 202, "download/dloadindex/mmsImageIndex.map", "brew/shared/mms", "mms", 30), ( 240, "download/dloadindex/mmsDrmImageIndex.map", "brew/shared/mms/d", "drm", 20), ( 130, None, None, "camera", 60) # nb camera must be last ) ringtonelocations=( # offset, index file, files location, type, maximumentries ( 50, "download/dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 60), ( 150, "download/dloadindex/mmsRingerIndex.map", "mms/sound", "mms", 20), ( 180, "download/dloadindex/mmsDrmRingerIndex.map", "mms/sound/drm", "drm", 20) ) builtinimages= ('Sport', 'Butterfly', 'Cake', 'Niagara Falls', 'Rockefeller', 'Statue of Liberty', 'The Capital', 'Scenary','White Bear', 'Yacht' ) builtinringtones= ('Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5', 'VZW Default Tone', 'Farewell', 'Arabesque', 'Piano Sonata', 'Latin', 'When The Saints', 'Bach Cello Suite', 'Speedy Way', 'Cancan', 'Sting', 'Toccata and Fugue', 'Mozart Symphony 40', 'Nutcracker March', 'Funiculi', 'Polka', 'Hallelujah', 'Mozart Aria', 'Leichte', 'Spring', 'Slavonic', 'Fantasy', 'Chimes High', 'Chimes Low', 'Ding', 'Tada', 'Notify', 'Drum', 'Claps', 'Fanfare', 'Chord High', 'Chord Low') def __init__(self, logtarget, commport): com_lgvx4400.Phone.__init__(self,logtarget,commport) self.mode=self.MODENONE def makeentry(self, counter, entry, dict): e=com_lgvx4400.Phone.makeentry(self, counter, entry, dict) e.entrysize=0x202 return e def getcameraindex(self): buf=prototypes.buffer(self.getfilecontents("cam/pics.dat")) index={} g=self.protocolclass.campicsdat() g.readfrombuffer(buf) for i in g.items: if len(i.name): # index[i.index]={'name': i.name, 'date': i.taken, 'origin': 'camera' } # we currently use the filesystem name rather than rename in camera # since the latter doesn't include the file extension which then makes # life less pleasant once the file ends up on the computer index[i.index]={'name': "pic%02d.jpg"%(i.index,), 'date': i.taken, 'origin': 'camera' } return index def _setmodebrew(self): req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue for i in range(3): try: self.sendbrewcommand(req, respc, callsetmode=False) return True except com_phone.modeignoreerrortypes: pass return False is_mode_brew=_setmodebrew def get_detect_data(self, res): # try to gather detect data for this phone # try to check for our model (VX6100) try: s=self.getfilecontents('brew/version.txt') except: # failed to read, abort! return if s[:6]=='VX6100': # found it !! print "in get_detect!!!! " +s[:6] res['model']='VX6100' res['manufacturer']='LG Electronics Inc' # attempt the get the ESN try: s=self.getfilecontents("nvm/$SYS.ESN")[85:89] res['esn']='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0])) except: res['esn']=None def detectphone(coms, likely_ports, res): if not likely_ports: # cannot detect any likely ports return None for port in likely_ports: print "in detect" if not res.has_key(port): res[port]={ 'mode_modem': None, 'mode_brew': None, 'manufacturer': None, 'model': None, 'firmware_version': None, 'esn': None, 'firmwareresponse': None } try: if res[port]['mode_brew']==False or \ res[port]['model']: # either phone is not in BREW, or a model has already # been found, not much we can do now continue p=Phone(None, commport.CommConnection(None, port, timeout=1)) if p.is_mode_brew(): res[port]['mode_brew']=True p.get_detect_data(res[port]) else: res[port]['mode_brew']=False except: pass return detectphone=staticmethod(detectphone) def getphoneinfo(self, phone_info): print "in phone info" self.log('Getting Phone Info') #try: s=self.getfilecontents('brew/version.txt') if s[:6]=='VX6100': phone_info.append('Model:', "VX6100") req=p_brew.firmwarerequest() res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) phone_info.append('Firmware Version:', res.firmware) s=self.getfilecontents("nvm/$SYS.ESN")[85:89] txt='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0])) phone_info.append('ESN:', txt) txt=self.getfilecontents("nvm/nvm/nvm_0000")[577:587] phone_info.append('Phone Number:', txt) #except: # pass return parentprofile=com_lgvx4400.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname phone_manufacturer='LG Electronics Inc' phone_model='VX6100' WALLPAPER_WIDTH=132 WALLPAPER_HEIGHT=148 MAX_WALLPAPER_BASENAME_LENGTH=24 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ ." WALLPAPER_CONVERT_FORMAT="jpg" MAX_RINGTONE_BASENAME_LENGTH=24 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ ." # nb we don't allow save to camera so it isn't listed here imageorigins={} imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) imageorigins.update(common.getkv(parentprofile.stockimageorigins, "mms")) imageorigins.update(common.getkv(parentprofile.stockimageorigins, "drm")) def GetImageOrigins(self): return self.imageorigins # our targets are the same for all origins imagetargets={} imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", {'width': 132, 'height': 148, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", {'width': 132, 'height': 148, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", {'width': 128, 'height': 160, 'format': "JPEG"})) # can the outside lcd display images? #imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", # {'width': 96, 'height': 64, 'format': "JPEG"})) _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 ('call_history', 'read', None),# all call history list reading # ('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'), ) def __init__(self): parentprofile.__init__(self)
com_lgvx8100.py
(application/octet-stream, 19.6 KB)
### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> ### Copyright (C) 2005 Simon Capper <[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. ### """Communicate with the LG VX8100 cell phone The VX8100 is substantially similar to the VX7000 but also supports video. """ # standard modules import re import time import cStringIO import sha # my modules import common import commport import copy import com_lgvx4400 import p_brew import p_lgvx8100 import com_lgvx7000 import com_brew import com_phone import com_lg import prototypes import bpcalendar import call_history import sms import memo from prototypes import * class Phone(com_lgvx7000.Phone): "Talk to the LG VX8100 cell phone" desc="LG-VX8100" protocolclass=p_lgvx8100 serialsname='lgvx8100' builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps', 'VZW Default Ringtone') + \ 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/my_ringtone.dat', 'dload/my_ringtonesize.dat', 'brew/16452/lk/mr', 100, 150, 1), ) builtinwallpapers = () # none wallpaperlocations= ( ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0), ) def __init__(self, logtarget, commport): com_lgvx4400.Phone.__init__(self, logtarget, commport) self.mode=self.MODENONE def getfundamentals(self, results): """Gets information fundamental to interopating with the phone and UI. Currently this is: - 'uniqueserial' a unique serial number representing the phone - 'groups' the phonebook groups - 'wallpaper-index' map index numbers to names - 'ringtone-index' map index numbers to ringtone names This method is called before we read the phonebook data or before we write phonebook data. """ # use a hash of ESN and other stuff (being paranoid) self.log("Retrieving fundamental phone information") self.log("Phone serial number") results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() # now read groups self.log("Reading group information") buf=prototypes.buffer(self.getfilecontents("pim/pbgroup.dat")) g=self.protocolclass.pbgroups() g.readfrombuffer(buf) self.logdata("Groups read", buf.getdata(), g) groups={} for i in range(len(g.groups)): if len(g.groups[i].name): # sometimes have zero length names groups[i]={'name': g.groups[i].name } results['groups']=groups self.getwallpaperindices(results) self.getringtoneindices(results) self.log("Fundamentals retrieved") return results def savegroups(self, data): groups=data['groups'] keys=groups.keys() keys.sort() g=self.protocolclass.pbgroups() for k in keys: e=self.protocolclass.pbgroup() e.name=groups[k]['name'] g.groups.append(e) buffer=prototypes.buffer() g.writetobuffer(buffer) self.logdata("New group file", buffer.getvalue(), g) self.writefile("pim/pbgroup.dat", buffer.getvalue()) def getmemo(self, result): # read the memo file try: buf=prototypes.buffer(self.getfilecontents("sch/neomemo.dat")) text_memo=self.protocolclass.textmemofile() text_memo.readfrombuffer(buf) res={} for m in text_memo.items: entry=memo.MemoEntry() entry.text=m.text entry.set_date_isostr("%d%02d%02dT%02d%02d00" % ((m.memotime))) 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 t=time.strptime(memo_dict[k].date, '%b %d, %Y %H:%M') entry.memotime=(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min) text_memo.items.append(entry) buf=prototypes.buffer() text_memo.writetobuffer(buf) self.writefile("sch/neomemo.dat", buf.getvalue()) return result def getcalendar(self,result): res={} # Read exceptions file first try: buf=prototypes.buffer(self.getfilecontents("sch/newschexception.dat")) ex=self.protocolclass.scheduleexceptionfile() ex.readfrombuffer(buf) self.logdata("Calendar exceptions", buf.getdata(), ex) exceptions={} for i in ex.items: try: exceptions[i.pos].append( (i.year,i.month,i.day) ) except KeyError: exceptions[i.pos]=[ (i.year,i.month,i.day) ] except com_brew.BrewNoSuchFileException: exceptions={} # Now read schedule try: buf=prototypes.buffer(self.getfilecontents("sch/newschedule.dat")) if len(buf.getdata())<3: # 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) for event in sc.events: # the vx8100 has a bad entry when the calender is empty # stop processing the calender when we hit this record if event.pos==0: #invalid entry break entry=bpcalendar.CalendarEntry() entry.description=event.description entry.start=event.start entry.end=event.end entry.vibrate=~(event.alarmindex_vibrate&0x1) # vibarate bit is inverted in phone 0=on, 1=off entry.repeat = self.makerepeat(event.repeat) min=event.alarmminutes hour=event.alarmhours if min==0x64 or hour==0x64: entry.alarm=None # no alarm set else: entry.alarm=hour*60+min entry.ringtone=result['ringtone-index'][event.ringtone]['name'] entry.snoozedelay=0 # check for exceptions and remove them if event.repeat[3] and exceptions.has_key(event.pos): for year, month, day in exceptions[event.pos]: entry.suppress_repeat_entry(year, month, day) res[entry.id]=entry assert sc.numactiveitems==len(res) except com_brew.BrewNoSuchFileException: pass # do nothing if file doesn't exist result['calendar']=res return result def makerepeat(self, repeat): # get all the variables out of the repeat tuple # and convert into a bpcalender RepeatEntry type,dow,interval,exceptions=repeat if type==0: repeat_entry=None else: repeat_entry=bpcalendar.RepeatEntry() if type==1: #daily repeat_entry.repeat_type=repeat_entry.daily repeat_entry.interval=interval elif type==5: #'monfri' repeat_entry.repeat_type=repeat_entry.daily repeat_entry.interval=0 elif type==2: #'weekly' repeat_entry.repeat_type=repeat_entry.weekly repeat_entry.dow=dow repeat_entry.interval=interval elif type==3: #'monthly' repeat_entry.repeat_type=repeat_entry.monthly repeat_entry.interval=interval repeat_entry.dow=0 elif type==6: #'monthly' #Xth Y day (e.g. 2nd friday each month) repeat_entry.repeat_type=repeat_entry.monthly repeat_entry.interval=interval #X repeat_entry.dow=dow #Y else: # =4 'yearly' repeat_entry.repeat_type=repeat_entry.yearly return repeat_entry def savecalendar(self, dict, merge): # ::TODO:: # what will be written to the files eventsf=self.protocolclass.schedulefile() exceptionsf=self.protocolclass.scheduleexceptionfile() # what are we working with cal=dict['calendar'] newcal={} keys=cal.keys() keys.sort() pos=1 # number of entries eventsf.numactiveitems=len(keys) # play with each entry for k in keys: # entry is what we will return to user entry=cal[k] data=self.protocolclass.scheduleevent() data.pos=eventsf.packetsize() data.description=entry.description data.start=entry.start data.end=entry.end self.setalarm(entry, data) data.ringtone=0 for i in dict['ringtone-index']: if dict['ringtone-index'][i]['name']==entry.ringtone: data.ringtone=i # check for exceptions and add them to the exceptions list exceptions=0 if entry.repeat!=None: for i in entry.repeat.suppressed: de=self.protocolclass.scheduleexception() de.pos=data.pos de.day=i.date.day de.month=i.date.month de.year=i.date.year exceptions=1 exceptionsf.items.append(de) if entry.repeat != None: data.repeat=(self.getrepeattype(entry, exceptions)) else: data.repeat=((0,0,0,0)) data.unknown1=0 data.unknown2=0 # put entry in nice shiny new dict we are building entry=copy.copy(entry) newcal[data.pos]=entry eventsf.events.append(data) # scribble everything out buf=prototypes.buffer() eventsf.writetobuffer(buf) self.logdata("Writing calendar", buf.getvalue(), eventsf) self.writefile("sch/newschedule.dat", buf.getvalue()) buf=prototypes.buffer() exceptionsf.writetobuffer(buf) self.logdata("Writing calendar exceptions", buf.getvalue(), exceptionsf) self.writefile("sch/newschexception.dat", buf.getvalue()) # fix passed in dict dict['calendar']=newcal return dict def getrepeattype(self, entry, exceptions): #convert the bpcalender type into vx8100 type repeat_entry=bpcalendar.RepeatEntry() if entry.repeat.repeat_type==repeat_entry.monthly: dow=entry.repeat.dow if entry.repeat.dow==0: # set interval for month type 4 to start day of month, (required by vx8100) interval=entry.start[2] type=3 else: interval=entry.repeat.interval type=6 elif entry.repeat.repeat_type==repeat_entry.daily: dow=entry.repeat.dow interval=entry.repeat.interval if entry.repeat.interval==0: type=5 else: type=1 elif entry.repeat.repeat_type==repeat_entry.weekly: dow=entry.repeat.dow interval=entry.repeat.interval type=2 elif entry.repeat.repeat_type==repeat_entry.yearly: # set interval to start day of month, (required by vx8100) interval=entry.start[2] # set dow to start month, (required by vx8100) dow=entry.start[1] type=4 return (type, dow, interval, exceptions) def setalarm(self, entry, data): # vx8100 only allows certain repeat intervals, adjust to fit, it also stores an index to the interval if entry.alarm>=2880: entry.alarm=2880 data.alarmminutes=0 data.alarmhours=48 data.alarmindex_vibrate=0x10 elif entry.alarm>=1440: entry.alarm=1440 data.alarmminutes=0 data.alarmhours=24 data.alarmindex_vibrate=0xe elif entry.alarm>=120: entry.alarm=120 data.alarmminutes=0 data.alarmhours=2 data.alarmindex_vibrate=0xc elif entry.alarm>=60: entry.alarm=60 data.alarmminutes=0 data.alarmhours=1 data.alarmindex_vibrate=0xa elif entry.alarm>=15: entry.alarm=15 data.alarmminutes=15 data.alarmhours=0 data.alarmindex_vibrate=0x8 elif entry.alarm>=10: entry.alarm=10 data.alarmminutes=10 data.alarmhours=0 data.alarmindex_vibrate=0x6 elif entry.alarm>=5: entry.alarm=5 data.alarmminutes=10 data.alarmhours=0 data.alarmindex_vibrate=0x4 elif entry.alarm>=0: entry.alarm=0 data.alarmminutes=0 data.alarmhours=0 data.alarmindex_vibrate=0x2 else: # no alarm data.alarmminutes=0x64 data.alarmhours=0x64 data.alarmindex_vibrate=1 # set the vibrate bit if data.alarmindex_vibrate > 1 and entry.vibrate==0: data.alarmindex_vibrate+=1 return def _setmodebrew(self): req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue for i in range(3): try: self.sendbrewcommand(req, respc, callsetmode=False) return True except com_phone.modeignoreerrortypes: pass return False is_mode_brew=_setmodebrew def get_detect_data(self, res): # try to gather detect data for this phone # try to check for our model (VX8100) try: s=self.getfilecontents('brew/version.txt') except: # failed to read, abort! return if s[:6]=='VX8100': # found it !! res['model']='VX8100' res['manufacturer']='LG Electronics Inc' # attempt the get the ESN try: s=self.getfilecontents("nvm/$SYS.ESN")[85:89] res['esn']='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0])) except: res['esn']=None def detectphone(coms, likely_ports, res): if not likely_ports: # cannot detect any likely ports return None for port in likely_ports: if not res.has_key(port): res[port]={ 'mode_modem': None, 'mode_brew': None, 'manufacturer': None, 'model': None, 'firmware_version': None, 'esn': None, 'firmwareresponse': None } try: if res[port]['mode_brew']==False or \ res[port]['model']: # either phone is not in BREW, or a model has already # been found, not much we can do now continue p=Phone(None, commport.CommConnection(None, port, timeout=1)) if p.is_mode_brew(): res[port]['mode_brew']=True p.get_detect_data(res[port]) else: res[port]['mode_brew']=False except: pass return detectphone=staticmethod(detectphone) def getphoneinfo(self, phone_info): self.log('Getting Phone Info') try: s=self.getfilecontents('brew/version.txt') if s[:6]=='VX8100': phone_info.append('Model:', "VX8100") req=p_brew.firmwarerequest() res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) phone_info.append('Firmware Version:', res.firmware) s=self.getfilecontents("nvm/$SYS.ESN")[85:89] txt='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0])) phone_info.append('ESN:', txt) txt=self.getfilecontents("nvm/nvm/nvm_cdma")[180:190] phone_info.append('Phone Number:', txt) except: pass return parentprofile=com_lgvx7000.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname BP_Calendar_Version=3 phone_manufacturer='LG Electronics Inc' phone_model='VX8100' 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 ." # there is an origin named 'aod' - no idea what it is for except maybe # 'all other downloads' # the 8100 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 ('call_history', 'read', None),# all call history list reading ('sms', 'read', None), # all SMS list reading ('memo', 'read', None), # all memo list 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'), ('sms', 'write', 'OVERWRITE'), # all SMS list writing ('memo', 'write', 'OVERWRITE'), # all memo list writing )
comm_lgvx4400.py.diff
(application/octet-stream, 12.2 KB)
Index: com_lgvx4400.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/com_lgvx4400.py,v
retrieving revision 1.130
diff -u -r1.130 com_lgvx4400.py
--- com_lgvx4400.py 22 Aug 2005 12:13:36 -0000 1.130
+++ com_lgvx4400.py 23 Aug 2005 05:21:27 -0000
@@ -24,6 +24,8 @@
import com_lg
import prototypes
import fileinfo
+import call_history
+import sms
class Phone(com_phone.Phone,com_brew.BrewProtocol,com_lg.LGPhonebook,com_lg.LGIndexedMedia):
"Talk to the LG VX4400 cell phone"
@@ -99,7 +101,235 @@
self.log("Fundamentals retrieved")
return results
+ 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("sms/mediacan000.dat", buf.getvalue())
+ return
+
+ _smspatterns={'Inbox': re.compile(r"^.*/inbox[0-9][0-9][0-9]\.dat$"),
+ 'Sent': re.compile(r"^.*/outbox[0-9][0-9][0-9]\.dat$"),
+ 'Saved': re.compile(r"^.*/sf[0-9][0-9]\.dat$"),
+ }
+
+ def getsms(self, result):
+ res={}
+ # get the quicktext (LG name for canned messages)
+ result['canned_msg']=self._getquicktext()
+ # 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._smspatterns.items():
+ if pat.match(item['name']):
+ folder=f
+ break
+ if folder=='Inbox':
+ self._getinboxmessage(item['name'], res)
+ elif folder=='Sent':
+ self._getoutboxmessage(item['name'], res)
+ elif folder=='Saved':
+ self._getsavedmessage(item['name'], res)
+ result['sms']=res
+ return result
+
+ 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, file, res):
+ buf=prototypes.buffer(self.getfilecontents(file))
+ sf=self.protocolclass.sms_in()
+ sf.readfrombuffer(buf)
+ self.logdata("SMS message in file "+file, buf.getdata(), 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
+ res[entry.id]=entry
+ return
+
+ def _getoutboxmessage(self, file, res):
+ buf=prototypes.buffer(self.getfilecontents(file))
+ sf=self.protocolclass.sms_out()
+ sf.readfrombuffer(buf)
+ self.logdata("SMS message in file "+file, buf.getdata(), sf)
+ entry=sms.SMSEntry()
+ entry.folder=entry.Folder_Sent
+ entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent))
+ # add all the recipients
+ for i in range(10):
+ if sf.recipients[i].number!="":
+ confirmed=(sf.recipients[i].status==5)
+ confirmed_date=""
+ if confirmed:
+ confirmed_date="%d%02d%02dT%02d%02d00" % ((sf.recipients[i].timereceived))
+ entry.add_recipient(sf.recipients[i].number, confirmed, confirmed_date)
+ entry.subject=sf.subject
+ txt=""
+ if sf.num_msg_elements==1 and sf.messages[0].unknown2==0:
+ 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
+ res[entry.id]=entry
+ return
+
+ def _getsavedmessage(self, file, res):
+ buf=prototypes.buffer(self.getfilecontents(file))
+ sf=self.protocolclass.sms_saved()
+ sf.readfrombuffer(buf)
+ self.logdata("SMS message in file "+file, buf.getdata(), sf)
+ entry=sms.SMSEntry()
+ entry.folder=entry.Folder_Saved
+ entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent))
+ # add all the recipients, unlike the outbox all 10 are the same format
+ for i in range(10):
+ if sf.recipients[i].number!="":
+ confirmed=(sf.recipients[i].status==5)
+ confirmed_date=""
+ if confirmed:
+ confirmed_date="%d%02d%02dT%02d%02d00" % ((sf.recipients[i].timereceived))
+ entry.add_recipient(sf.recipients[i].number, confirmed, confirmed_date)
+ entry.subject=sf.subject
+ txt=""
+ if sf.num_msg_elements==1 and sf.messages[0].unknown2==0:
+ 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
+ res[entry.id]=entry
+ return
+
+ 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
+
+ def getcallhistory(self, result):
+ res={}
+ # read the incoming call history file
+ self._readhistoryfile("pim/missed_log.dat", 'Missed', res)
+ self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res)
+ self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res)
+ result['call_history']=res
+ return result
+
+ def _readhistoryfile(self, fname, folder, res):
+ try:
+ buf=prototypes.buffer(self.getfilecontents(fname))
+ ch=self.protocolclass.callhistory()
+ ch.readfrombuffer(buf)
+ self.logdata("Call History", buf.getdata(), ch)
+ for call in ch.calls:
+ if call.number=='' and call.name=='':
+ continue
+ entry=call_history.CallHistoryEntry()
+ entry.folder=folder
+ entry.datetime=((call.GPStime))
+ if call.number=='': # restricted calls have no number
+ entry.number=call.name
+ else:
+ entry.number=call.number
+ res[entry.id]=entry
+ except com_brew.BrewNoSuchFileException:
+ pass # do nothing if file doesn't exist
+ return
def getwallpaperindices(self, results):
return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index')
@@ -715,31 +945,6 @@
return e
- smspatterns={'Inbox': re.compile(r"^.*/inbox[0-9][0-9][0-9]\.dat$"),
- 'Sent': re.compile(r"^.*/outbox[0-9][0-9][0-9]\.dat$"),
- 'Saved': re.compile(r"^.*/sf[0-9][0-9]\.dat$"),
- }
-
- def getsms(self, results):
- for item in self.getfilesystem("sms").values():
- if item['type']=='file':
- folder=None
- for f,pat in self.smspatterns.items():
- print item['name']
- if pat.match(item['name']):
- folder=f
- break
- if folder is None: continue
- buf=prototypes.buffer(self.getfilecontents(item['name']))
- sf=self.protocolclass.SMSFile()
- sf.readfrombuffer(buf)
- self.logdata("SMS message in file "+item['name'], buf.getdata(), sf)
-
- return results
-
-
-
-
def phonize(str):
"""Convert the phone number into something the phone understands
@@ -964,13 +1169,15 @@
('calendar', 'read', None), # all calendar reading
('wallpaper', 'read', None), # all wallpaper reading
('ringtone', 'read', None), # all ringtone reading
- #('sms', 'read', None),
+ ('call_history', 'read', None),# all call history list reading
+ ('sms', 'read', None), # all SMS list 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'),
+ ('sms', 'write', 'OVERWRITE'), # all SMS list writing
)
def QueryAudio(self, origin, currentextension, afi):
comm_lgvx6100.py.diff
(application/octet-stream, 4.7 KB)
Index: com_lgvx6100.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/com_lgvx6100.py,v
retrieving revision 1.9
diff -u -r1.9 com_lgvx6100.py
--- com_lgvx6100.py 2 May 2005 17:15:05 -0000 1.9
+++ com_lgvx6100.py 23 Aug 2005 05:31:25 -0000
@@ -26,13 +26,16 @@
# my modules
import common
+import commport
import copy
+import p_brew
import p_lgvx6100
import com_lgvx4400
import com_brew
import com_phone
import com_lg
import prototypes
+import call_history
class Phone(com_lgvx4400.Phone):
"Talk to the LG VX6100 cell phone"
@@ -95,10 +98,99 @@
index[i.index]={'name': "pic%02d.jpg"%(i.index,), 'date': i.taken, 'origin': 'camera' }
return index
+ def _setmodebrew(self):
+ req=p_brew.memoryconfigrequest()
+ respc=p_brew.memoryconfigresponse
+
+ for baud in 0, 38400,115200:
+ if baud:
+ if not self.comm.setbaudrate(baud):
+ continue
+ for i in range(3):
+ try:
+ self.sendbrewcommand(req, respc, callsetmode=False)
+ return True
+ except com_phone.modeignoreerrortypes:
+ pass
+ return False
+
+ is_mode_brew=_setmodebrew
+
+ def get_detect_data(self, res):
+ # try to gather detect data for this phone
+ # try to check for our model (VX6100)
+ try:
+ s=self.getfilecontents('brew/version.txt')
+ except:
+ # failed to read, abort!
+ return
+ if s[:6]=='VX6100':
+ # found it !!
+ print "in get_detect!!!! " +s[:6]
+ res['model']='VX6100'
+ res['manufacturer']='LG Electronics Inc'
+ # attempt the get the ESN
+ try:
+ s=self.getfilecontents("nvm/$SYS.ESN")[85:89]
+ res['esn']='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]),
+ ord(s[1]), ord(s[0]))
+ except:
+ res['esn']=None
+
+ def detectphone(coms, likely_ports, res):
+ if not likely_ports:
+ # cannot detect any likely ports
+ return None
+ for port in likely_ports:
+ print "in detect"
+ if not res.has_key(port):
+ res[port]={ 'mode_modem': None, 'mode_brew': None,
+ 'manufacturer': None, 'model': None,
+ 'firmware_version': None, 'esn': None,
+ 'firmwareresponse': None }
+ try:
+ if res[port]['mode_brew']==False or \
+ res[port]['model']:
+ # either phone is not in BREW, or a model has already
+ # been found, not much we can do now
+ continue
+ p=Phone(None, commport.CommConnection(None, port, timeout=1))
+ if p.is_mode_brew():
+ res[port]['mode_brew']=True
+ p.get_detect_data(res[port])
+ else:
+ res[port]['mode_brew']=False
+ except:
+ pass
+ return
+
+ detectphone=staticmethod(detectphone)
+
+ def getphoneinfo(self, phone_info):
+ print "in phone info"
+ self.log('Getting Phone Info')
+ #try:
+ s=self.getfilecontents('brew/version.txt')
+ if s[:6]=='VX6100':
+ phone_info.append('Model:', "VX6100")
+ req=p_brew.firmwarerequest()
+ res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse)
+ phone_info.append('Firmware Version:', res.firmware)
+ s=self.getfilecontents("nvm/$SYS.ESN")[85:89]
+ txt='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0]))
+ phone_info.append('ESN:', txt)
+ txt=self.getfilecontents("nvm/nvm/nvm_0000")[577:587]
+ phone_info.append('Phone Number:', txt)
+ #except:
+ # pass
+ return
+
parentprofile=com_lgvx4400.Profile
class Profile(parentprofile):
protocolclass=Phone.protocolclass
serialsname=Phone.serialsname
+ phone_manufacturer='LG Electronics Inc'
+ phone_model='VX6100'
WALLPAPER_WIDTH=132
WALLPAPER_HEIGHT=148
@@ -136,6 +228,7 @@
('wallpaper', 'read', None), # all wallpaper reading
('ringtone', 'read', None), # all ringtone reading
('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook
+ ('call_history', 'read', None),# all call history list reading
# ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar
('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper
('wallpaper', 'write', 'OVERWRITE'),
comm_lgvx8100.py.diff
(application/octet-stream, 15.6 KB)
Index: com_lgvx8100.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/com_lgvx8100.py,v
retrieving revision 1.3
diff -u -r1.3 com_lgvx8100.py
--- com_lgvx8100.py 20 Aug 2005 04:48:26 -0000 1.3
+++ com_lgvx8100.py 23 Aug 2005 05:24:23 -0000
@@ -21,7 +21,10 @@
# my modules
import common
+import commport
import copy
+import com_lgvx4400
+import p_brew
import p_lgvx8100
import com_lgvx7000
import com_brew
@@ -31,6 +34,7 @@
import bpcalendar
import call_history
import sms
+import memo
from prototypes import *
@@ -51,7 +55,7 @@
ringtonelocations= (
# type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major
- ( 'ringers', 'dload/my_ringtone.dat', 'dload/my_ringtonesize.dat', 'brew/16452/ms', 100, 50, 1),
+ ( 'ringers', 'dload/my_ringtone.dat', 'dload/my_ringtonesize.dat', 'brew/16452/lk/mr', 100, 150, 1),
)
builtinwallpapers = () # none
@@ -60,6 +64,10 @@
( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0),
)
+ def __init__(self, logtarget, commport):
+ com_lgvx4400.Phone.__init__(self, logtarget, commport)
+ self.mode=self.MODENONE
+
def getfundamentals(self, results):
"""Gets information fundamental to interopating with the phone and UI.
@@ -108,184 +116,39 @@
self.logdata("New group file", buffer.getvalue(), g)
self.writefile("pim/pbgroup.dat", buffer.getvalue())
- _smspatterns={'Inbox': re.compile(r"^.*/inbox[0-9][0-9][0-9]\.dat$"),
- 'Outbox': re.compile(r"^.*/outbox[0-9][0-9][0-9]\.dat$"),
- 'Saved': re.compile(r"^.*/sf[0-9][0-9]\.dat$"),
- }
-
- def getsms(self, result):
- 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.smspatterns.items():
- if pat.match(item['name']):
- folder=f
- break
- if folder=='Inbox':
- self._getinboxmessage(item['name'], res)
- elif folder=='Sent':
- self._getoutboxmessage(item['name'], res)
- elif folder=='Saved':
- self._getsavedmessage(item['name'], res)
- result['sms']=res
- return result
-
- def _getinboxmessage(self, file, res):
- buf=prototypes.buffer(self.getfilecontents(file))
- sf=self.protocolclass.sms_in()
- sf.readfrombuffer(buf)
- self.logdata("SMS message in file "+file, buf.getdata(), 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
- 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
- res[entry.id]=entry
- return
-
- def _getoutboxmessage(self, file, res):
- buf=prototypes.buffer(self.getfilecontents(file))
- sf=self.protocolclass.sms_out()
- sf.readfrombuffer(buf)
- self.logdata("SMS message in file "+file, buf.getdata(), sf)
- entry=sms.SMSEntry()
- entry.folder=entry.Folder_Sent
- entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime)
- entry._to=""
- first=1
- # add all the recipients to the _to field, separated with a :
- for i in range(9):
- if sf.recipients[i].number!="":
- if first:
- first=0
- else:
- entry._to+=": "
- entry._to+=sf.recipients[i].number
- entry.subject=sf.subject
- txt=""
- if sf.num_msg_elements==1 and sf.messages[0].unknown2==0:
- 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')
- entry.callback=sf.callback
- res[entry.id]=entry
- return
-
- def _getsavedmessage(self, file, res):
- buf=prototypes.buffer(self.getfilecontents(file))
- sf=self.protocolclass.sms_saved()
- sf.readfrombuffer(buf)
- self.logdata("SMS message in file "+file, buf.getdata(), sf)
- entry=sms.SMSEntry()
- entry.folder=entry.Folder_Saved
- entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime)
- first=1
- # add all the recipients to the _to field, separated with a :
- for i in range(10):
- if sf.recipients[i].number!="":
- if first:
- first=0
- else:
- entry._to+=": "
- entry._to+=sf.recipients[i].number
- entry.subject=sf.subject
- txt=""
- if sf.num_msg_elements==1 and sf.messages[0].unknown2==0:
- 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')
- entry.callback=sf.callback
- res[entry.id]=entry
- return
-
- 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((num_septets*7)/8+7):
- if(i%7==0):
- 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(num_septets+7):
- if(i%8==0):
- 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
-
- def getcallhistory(self, result):
- res={}
- # read the incoming call history file
- self._readhistoryfile("pim/missed_log.dat", 'Missed', res)
- self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res)
- self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res)
- result['call_history']=res
- return result
-
- def _readhistoryfile(self, fname, folder, res):
+ def getmemo(self, result):
+ # read the memo file
try:
- buf=prototypes.buffer(self.getfilecontents(fname))
- ch=self.protocolclass.callhistory()
- ch.readfrombuffer(buf)
- self.logdata("Call History", buf.getdata(), ch)
- for call in ch.calls:
- if call.number=='': #empty record
- break
- entry=call_history.CallHistoryEntry()
- entry.folder=folder
- entry.datetime=((call.GPStime))
- entry.number=call.number
+ buf=prototypes.buffer(self.getfilecontents("sch/neomemo.dat"))
+ text_memo=self.protocolclass.textmemofile()
+ text_memo.readfrombuffer(buf)
+ res={}
+ for m in text_memo.items:
+ entry=memo.MemoEntry()
+ entry.text=m.text
+ entry.set_date_isostr("%d%02d%02dT%02d%02d00" % ((m.memotime)))
res[entry.id]=entry
except com_brew.BrewNoSuchFileException:
- pass # do nothing if file doesn't exist
- return
+ 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
+ t=time.strptime(memo_dict[k].date, '%b %d, %Y %H:%M')
+ entry.memotime=(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min)
+ text_memo.items.append(entry)
+ buf=prototypes.buffer()
+ text_memo.writetobuffer(buf)
+ self.writefile("sch/neomemo.dat", buf.getvalue())
+ return result
def getcalendar(self,result):
res={}
@@ -476,7 +339,6 @@
def setalarm(self, entry, data):
# vx8100 only allows certain repeat intervals, adjust to fit, it also stores an index to the interval
- # bitpim does not support vibrate so turn it off for all alarms
if entry.alarm>=2880:
entry.alarm=2880
data.alarmminutes=0
@@ -527,12 +389,99 @@
data.alarmindex_vibrate+=1
return
+ def _setmodebrew(self):
+ req=p_brew.memoryconfigrequest()
+ respc=p_brew.memoryconfigresponse
+
+ for baud in 0, 38400,115200:
+ if baud:
+ if not self.comm.setbaudrate(baud):
+ continue
+ for i in range(3):
+ try:
+ self.sendbrewcommand(req, respc, callsetmode=False)
+ return True
+ except com_phone.modeignoreerrortypes:
+ pass
+ return False
+
+ is_mode_brew=_setmodebrew
+
+ def get_detect_data(self, res):
+ # try to gather detect data for this phone
+ # try to check for our model (VX8100)
+ try:
+ s=self.getfilecontents('brew/version.txt')
+ except:
+ # failed to read, abort!
+ return
+ if s[:6]=='VX8100':
+ # found it !!
+ res['model']='VX8100'
+ res['manufacturer']='LG Electronics Inc'
+ # attempt the get the ESN
+ try:
+ s=self.getfilecontents("nvm/$SYS.ESN")[85:89]
+ res['esn']='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]),
+ ord(s[1]), ord(s[0]))
+ except:
+ res['esn']=None
+
+ def detectphone(coms, likely_ports, res):
+ if not likely_ports:
+ # cannot detect any likely ports
+ return None
+ for port in likely_ports:
+ if not res.has_key(port):
+ res[port]={ 'mode_modem': None, 'mode_brew': None,
+ 'manufacturer': None, 'model': None,
+ 'firmware_version': None, 'esn': None,
+ 'firmwareresponse': None }
+ try:
+ if res[port]['mode_brew']==False or \
+ res[port]['model']:
+ # either phone is not in BREW, or a model has already
+ # been found, not much we can do now
+ continue
+ p=Phone(None, commport.CommConnection(None, port, timeout=1))
+ if p.is_mode_brew():
+ res[port]['mode_brew']=True
+ p.get_detect_data(res[port])
+ else:
+ res[port]['mode_brew']=False
+ except:
+ pass
+ return
+
+ detectphone=staticmethod(detectphone)
+
+ def getphoneinfo(self, phone_info):
+ self.log('Getting Phone Info')
+ try:
+ s=self.getfilecontents('brew/version.txt')
+ if s[:6]=='VX8100':
+ phone_info.append('Model:', "VX8100")
+ req=p_brew.firmwarerequest()
+ res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse)
+ phone_info.append('Firmware Version:', res.firmware)
+ s=self.getfilecontents("nvm/$SYS.ESN")[85:89]
+ txt='%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), ord(s[1]), ord(s[0]))
+ phone_info.append('ESN:', txt)
+ txt=self.getfilecontents("nvm/nvm/nvm_cdma")[180:190]
+ phone_info.append('Phone Number:', txt)
+ except:
+ pass
+ return
+
+
parentprofile=com_lgvx7000.Profile
class Profile(parentprofile):
protocolclass=Phone.protocolclass
serialsname=Phone.serialsname
BP_Calendar_Version=3
+ phone_manufacturer='LG Electronics Inc'
+ phone_model='VX8100'
WALLPAPER_WIDTH=176
WALLPAPER_HEIGHT=184
@@ -546,7 +495,7 @@
# there is an origin named 'aod' - no idea what it is for except maybe
# 'all other downloads'
- # the 8010 doesn't have seperate origins - they are all dumped in "images"
+ # the 8100 doesn't have seperate origins - they are all dumped in "images"
imageorigins={}
imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images"))
def GetImageOrigins(self):
@@ -571,16 +520,19 @@
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
- ('sms', 'read', None), # all SMS list reading
+ ('phonebook', 'read', None), # all phonebook reading
+ ('calendar', 'read', None), # all calendar reading
+ ('wallpaper', 'read', None), # all wallpaper reading
+ ('ringtone', 'read', None), # all ringtone reading
+ ('call_history', 'read', None),# all call history list reading
+ ('sms', 'read', None), # all SMS list reading
+ ('memo', 'read', None), # all memo list 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'),
- ('call_history', 'read', None),
+ ('sms', 'write', 'OVERWRITE'), # all SMS list writing
+ ('memo', 'write', 'OVERWRITE'), # all memo list writing
)
\ No newline at end of file
p_lgvx4400.p
(application/octet-stream, 11.2 KB) - not displayed
p_lgvx4400.p.diff
(application/octet-stream, 7.9 KB)
Index: p_lgvx4400.p
===================================================================
RCS file: /cvsroot/bitpim/bitpim/p_lgvx4400.p,v
retrieving revision 1.20
diff -u -r1.20 p_lgvx4400.p
--- p_lgvx4400.p 7 Feb 2005 05:41:57 -0000 1.20
+++ p_lgvx4400.p 23 Aug 2005 05:10:22 -0000
@@ -12,6 +12,7 @@
"""Various descriptions of data specific to LG VX4400"""
from prototypes import *
+from prototypeslg import *
# Make all lg stuff available in this module as well
from p_lg import *
@@ -34,6 +35,8 @@
NOWALLPAPER=0
MEMOLENGTH=33
+SMS_CANNED_MAX_ITEMS=18
+SMS_CANNED_MAX_LENGTH=101
numbertypetab=( 'home', 'home2', 'office', 'office2', 'cell', 'cell2',
'pager', 'fax', 'fax2', 'none' )
@@ -99,7 +102,6 @@
2 UINT {'default': 0xffff} +index
40 STRING {'default': ""} +name
-
PACKET indexfile:
"Used for tracking wallpaper and ringtones"
# A bit of a silly design again. Entries with an index of 0xffff are
@@ -144,7 +146,156 @@
1 UINT ringtone
39 STRING {'raiseonunterminatedread': False} description
-
PACKET schedulefile:
2 UINT numactiveitems
* LIST {'elementclass': scheduleevent} +events
+
+PACKET call:
+ 4 GPSDATE GPStime #no. of seconds since 0h 1-6-80, based off local time.
+ 4 UINT unknown1 # different for each call
+ 4 UINT duration #seconds, not certain about length of this field
+ 49 STRING {'raiseonunterminatedread': False} number
+ 36 STRING {'raiseonunterminatedread': False} name
+ 1 UINT numberlength # length of phone number
+ 1 UINT unknown2 # set to 1 on some calls
+ 1 UINT pbnumbertype # 1=cell, 2=home, 3=office, 4=cell2, 5=fax, 6=vmail, 0xFF=not in phone book
+ 2 UINT unknown3 # always seems to be 0
+ 1 UINT pbentrynum #entry number in phonebook
+
+PACKET callhistory:
+ 4 UINT numcalls
+ 1 UINT unknown1
+ * LIST {'elementclass': call} +calls
+
+###
+### 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 unknown1 # 0
+ 1 UINT unknown2 # 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': 219} +msg:
+ 1 UINT byte "individual byte of message"
+
+PACKET recipient_record:
+ 2 UINT unknown1
+ 49 STRING number
+ 1 UINT status # 1 when sent, 5 when received, 2 failed to send
+ 4 LGCALDATE timesent
+ 4 LGCALDATE timereceived
+ 8 DATA unknown2
+
+PACKET sms_saved:
+ 4 UINT unknown1 # set to 1
+ 4 LGCALDATE timesent # time the message was saved, identical to timesent2
+ 4 DATA unknown2
+ 1 UINT locked # 1=locked
+ 3 UINT unknown1 # zero
+ 4 LGCALDATE timesent2 # time the message was sent
+ 21 STRING subject
+ 151 UINT unknown4
+ 1 UINT num_msg_elements # up to 10
+ * LIST {'elementclass': msg_record, 'length': 10} +messages
+ 1 DATA unknown5
+ 1 UINT priority # 0=normal, 1=high
+ 14 DATA unknown7
+ 49 STRING callback
+ * LIST {'elementclass': recipient_record, 'length': 10} +recipients
+
+PACKET sms_out:
+ 4 UINT index # starting from 1, unique
+ 1 UINT locked # 1=locked
+ 3 UINT unknown1 # zero
+ 4 LGCALDATE timesent # time the message was sent
+ 21 STRING subject
+ 151 DATA unknown2
+ 1 UINT num_msg_elements # up to 10
+ * LIST {'elementclass': msg_record, 'length': 10} +messages
+ 1 UINT unknown5
+ 1 UINT priority # 0=normal, 1=high
+ 14 DATA unknown7
+ 49 STRING callback
+ * LIST {'elementclass': recipient_record,'length': 10} +recipients
+
+PACKET SMSINBOXMSGFRAGMENT:
+ * LIST {'length': 181} +msg: # this size could be wrong
+ 1 UINT byte "individual byte of message"
+
+PACKET sms_in:
+ 4 UINT msg_index1
+ 4 UINT msg_index2 # equal to the numerical part of the filename eg inbox002.dat
+ 2 UINT unknown2 # set to 0 for simple message and 3 for binary, 9 for page
+ 4 UINT unknown3 # set to 0
+ 6 SMSDATE timesent
+ 3 UINT unknown
+ 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"
+ 15 DATA unknown4 # set to zeros
+ 4 LGCALDATE lg_time # time the message was sent
+ 4 GPSDATE GPStime # num seconds since 0h 1-6-80, time message received by phone
+ 2 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
+ 2 UINT unknown8 # zero
+ 1 UINT priority # 1 if the message is high priority, 0 otherwise
+ 5 DATA flags # message flags, read, priority, locked etc
+ 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
+ 2 UINT unknown6 # 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"
+ * 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
+ 437 DATA unknown5
+ 33 STRING senders_name
+ 169 DATA unknown6 # ?? inlcudes senders phone number in ascii
+
+PACKET sms_quick_text:
+# the vx4400 has variable length NULL terminated strings null terminated in it's canned messages
+# file sms/mediacan000.dat, not sure about the max
+ * LIST {} +msgs:
+ * STRING {} msg #
+
p_lgvx6100.p
(application/octet-stream, 5.8 KB) - not displayed
p_lgvx6100.p.diff
(application/octet-stream, 1.6 KB)
Index: p_lgvx6100.p
===================================================================
RCS file: /cvsroot/bitpim/bitpim/p_lgvx6100.p,v
retrieving revision 1.3
diff -u -r1.3 p_lgvx6100.p
--- p_lgvx6100.p 14 Feb 2005 06:30:04 -0000 1.3
+++ p_lgvx6100.p 21 Aug 2005 21:07:12 -0000
@@ -13,6 +13,7 @@
"""Various descriptions of data specific to LG VX6100"""
from prototypes import *
+from prototypeslg import *
# Make all lg stuff available in this module as well
from p_lg import *
@@ -150,3 +151,29 @@
PACKET campicsdat:
"the cam/pics.dat file"
* LIST {'length': 60, 'elementclass': camindexentry, 'createdefault': True} +items
+
+PACKET call:
+ 4 GPSDATE GPStime #no. of seconds since 0h 1-6-80, based off local time.
+ 4 UINT unknown1 # different for each call
+ 4 UINT duration #seconds, not certain about length of this field
+ 49 STRING {'raiseonunterminatedread': False} number
+ 36 STRING {'raiseonunterminatedread': False} name
+ 1 UINT numberlength # length of phone number
+ 1 UINT unknown2 # set to 1 on some calls
+ 1 UINT pbnumbertype # 1=cell, 2=home, 3=office, 4=cell2, 5=fax, 6=vmail, 0xFF=not in phone book
+ 2 UINT unknown3 # always seems to be 0
+ 2 UINT pbentrynum #entry number in phonebook
+
+PACKET callhistory:
+ 4 UINT numcalls
+ 1 UINT unknown1
+ * LIST {'elementclass': call} +calls
+
+PACKET firmwareresponse:
+ 1 UINT command
+ 11 STRING {'terminator': None} date1
+ 8 STRING {'terminator': None} time1
+ 11 STRING {'terminator': None} date2
+ 8 STRING {'terminator': None} time2
+ 8 STRING {'terminator': None} firmware
+
p_lgvx8100.p
(application/octet-stream, 10.3 KB) - not displayed
p_lgvx8100.p.diff
(application/octet-stream, 3.5 KB)
Index: p_lgvx8100.p
===================================================================
RCS file: /cvsroot/bitpim/bitpim/p_lgvx8100.p,v
retrieving revision 1.3
diff -u -r1.3 p_lgvx8100.p
--- p_lgvx8100.p 20 Aug 2005 04:48:26 -0000 1.3
+++ p_lgvx8100.p 23 Aug 2005 05:03:00 -0000
@@ -30,6 +30,9 @@
SPEEDDIALINDEX=1
MAXCALENDARDESCRIPTION=32
+SMS_CANNED_MAX_ITEMS=18
+SMS_CANNED_MAX_LENGTH=101
+
%}
PACKET indexentry:
@@ -151,17 +154,19 @@
1 UINT byte "individual byte of message"
PACKET recipient_record:
+ 45 DATA unknown1
49 STRING number
1 UINT status # 1 when sent, 5 when received
4 LGCALDATE timesent
4 LGCALDATE timereceived
- 1 UINT unknown1 # 0 when not received, set to 1 when received
- 85 DATA unknown2
+ 1 UINT unknown2 # 0 when not received, set to 1 when received
+ 40 DATA unknown3
PACKET sms_saved:
4 UINT unknown1 # set to 1
4 GPSDATE GPStime # num seconds since 0h 1-6-80, time message received by phone
- 6 DATA unknown2
+ 5 DATA unknown2
+ 1 UINT locked
4 LGCALDATE timesent # time the message was sent
6 DATA unknown3
21 STRING subject
@@ -172,13 +177,14 @@
1 UINT priority # 0=normal, 1=high
12 DATA unknown7
3 DATA unknown8 # set to 01,00,01
- 68 STRING callback
+ 23 STRING callback
* LIST {'elementclass': recipient_record, 'length': 10} +recipients
- 937 DATA unknown9 # all zeros
+ 982 DATA unknown9 # all zeros
PACKET sms_out:
4 UINT index # starting from 1, unique
- 2 UINT unknown1 # zero
+ 1 UINT unknown1 # zero
+ 1 UINT locked # zero
4 LGCALDATE timesent # time the message was sent
2 UINT unknown2 # zero
4 GPSDATE GPStime # num seconds since 0h 1-6-80, time message received by phone
@@ -190,14 +196,8 @@
1 UINT priority # 0=normal, 1=high
12 DATA unknown7
3 DATA unknown8 # set to 01,00,01
- 68 STRING callback
- * LIST {'elementclass': recipient_record,'length': 9} +recipients
- 49 STRING recipient10number
- 1 UINT recipient10status # 1 when sent, 5 when received
- 4 LGCALDATE recipient10sent
- 4 LGCALDATE recipient10received
- 1 UINT recipient10unknown1 # 0, set to 1 when received
- 42 DATA unknown9
+ 23 STRING callback
+ * LIST {'elementclass': recipient_record,'length': 10} +recipients
PACKET SMSINBOXMSGFRAGMENT:
* LIST {'length': 181} +msg:
@@ -243,4 +243,28 @@
# than a simple SMS
60 DATA unknown12
33 STRING senders_name
- 169 DATA unknown9 # ?? inlcudes senders phone number in ascii
\ No newline at end of file
+ 169 DATA unknown9 # ?? inlcudes senders phone number in ascii
+
+PACKET sms_quick_text:
+ * LIST {'length': SMS_CANNED_MAX_ITEMS, 'createdefault': True} +msgs:
+ 101 STRING {'default': ""} +msg # include terminating NULL
+
+# Text Memos. LG memo support is weak, it only supports the raw text and none of
+# the features that other phones support, when you run bitpim you see loads of
+# options that do not work in the vx8100 on the memo page
+PACKET textmemo:
+ 151 STRING { 'raiseonunterminatedread': False, 'raiseontruncate': False } text
+ 4 LGCALDATE memotime # time the memo was writen
+
+PACKET textmemofile:
+ 4 UINT itemcount
+ * LIST { 'elementclass': textmemo } +items
+
+PACKET firmwareresponse:
+ 1 UINT command
+ 11 STRING {'terminator': None} date1
+ 8 STRING {'terminator': None} time1
+ 11 STRING {'terminator': None} date2
+ 8 STRING {'terminator': None} time2
+ 8 STRING {'terminator': None} firmware
+