RE: Updates to vx8100
"Simon" <[email protected]>
| Newsgroups | gmane.comp.mobile.bitpim.devel |
|---|---|
| Message-ID | <000d01c59d78$338815c0$6400a8c0@HOME> |
Roger, com_lgvx4400.py I have looked at this again and I screwed up on this :(. I did not mean to change the comment or the unset alarm. I also found out the changes I made did not work after some more testing. The 8100 changed the meaning of the speeddial PACKET slighlty. The second field 'number' now represents the numbertype instead of the numberindex. This means that you have to match with the numbertype in the phonebook entry instead of the position index. I scrapped my original change and introduced a new one which I tested more heavily, this allows the phone implementation to choose the meaning of the 'number' field in the speeddial PACKET using a new protocol paramter SPEEDDIALINDEX, this is set to 0 for the old meaning and 1 for the new vx8100 meaning. I felt a generic change was better than overriding the phonebook reading functions in the 8100 class, this way other new phones that work this way can take advantage of common code. com_lgvx8100.py The file really is called newschedule.dat. I'm not sure if it is similar to the other phones as I have not looked at one of them, although the scheduleevent PACKET is very different from the 6100 and 4400. I don't know what the analyser format is, is it documented?, I will write a desription of the calender based on what I have observed in the phone once I figure this out, where would this be stored? The vibrate is a flag (1 bit), it can be set independently of the ringtone for a given alarm. It would be great to add a new field to the calender entry so that it does not get lost, even better if a check box could be added to the alarm dialog so the user can manipulate it. In this mail I also included support for call_history. A change is required to the call_history.py for this to ensure unique IDs. I think this is a temporary patch as Joe is looking into what the problem is. p_lgvx8100.p I agree is makes sense to create a new lg protocol file as you suggested, this will touch a lot of files, probably too many files for me to manage via e-mail checkin. Can you do this once you are comfortable with my other changes? New code attached (incl. help) with diffs from CVS. (all updates are in this mail, discard all others) Simon -----Original Message----- From: [email protected] [mailto:[email protected]]On Behalf Of Roger Binns Sent: Monday, August 08, 2005 9:25 PM To: [email protected] Subject: Re: [BitPim-devel] Updates to vx8100 > Source attached. (com_lgvx4400.py, p_lgvx4400.p, com_lgvx8100.py, > p_lgvx8100.p) In future please use diff -u as detailed on the developer page http://bitpim.org/developer.html com_lgvx4400.py: What is the purpose of speeddialoffset? Is the 8100 doing something bizarre like not starting the file at one? (The other phones that use one for voicemail ignore the entry but it is there.) I also don't like the change of the unset alarm for all LG models. The LG calendar code does need to be redone, probably from scratch. If you have the time and inclinatio, I would suggest doing that and we can make values like that be model dependent. Removing the comment doesn't remove what it states. You cannot save to an origin of camera. Assuming the 8100 is like the 8000 and 7000, then there is no seperate origin for camera so that is all immaterial. com_lgvx8100.py: You seem to have figured out the calendar on that phone. However it seems to be reading and writing newschedule.dat. Is that really the name of the file (ie the new prefix). It would be great if you can document the format and we can try to get unified improved LG calendar support. Unless the format is completely different - ie new :-) You can save it in analyser format to make life easy. It also looks like you can seperately set the alarm for a calendar event to vibrate. Is that in addition to the ringtone? Anyway it seems like you give vibrate special treatment. My concern is that if a user read their calendar into BitPim and then wrote it back out, the vibrate information would be stripped. Post here what semantics you need (eg is vibrate just another ringtone or is it a flag) and Joe can provide a solution to preserving the value. p_lgvx4400.p: Just awaiting the speed dial clarification. p_lgvx8100.py: That class needs to go into a seperate file. We'll start a file named prototypeslg.py for the LG protocol specific types (LGCALDATE can also be pulled out of prototypes.py). (BTW prototypes is short for protocol types not the C header sense). ------ I have merged any of the above yet, awaiting your feedback ... Roger ------------------------------------------------------- SF.Net email is Sponsored by the Better Software Conference & EXPO September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf _______________________________________________ BitPim-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/bitpim-devel
call_history.py
(application/octet-stream, 14.8 KB)
### BITPIM ### ### Copyright (C) 2005 Joe Pham <[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: call_history.py,v 1.4 2005/07/29 03:25:30 djpham Exp $ """ Code to handle Call History data storage and display. The format of the Call History is standardized. It is an object with the following attributes: folder: string (where this item belongs) datetime: string 'YYYYMMDDThhmmss' or (y,m,d,h,m,s) number: string (the phone number of this call) To implement Call History feature for a phone module: Add an entry into Profile._supportedsyncs: ('call_history', 'read', None), Implement the following method in your Phone class: def getcallhistory(self, result, merge): ... return result The result dict key is 'call_history'. """ # standard modules import copy import sha import time # wx modules import wx import wx.lib.scrolledpanel as scrolled # BitPim modules import database import phonenumber import pubsub import today #------------------------------------------------------------------------------- class CallHistoryDataobject(database.basedataobject): _knownproperties=['folder', 'datetime', 'number' ] _knownlistproperties=database.basedataobject._knownlistproperties.copy() def __init__(self, data=None): if data is None or not isinstance(data, CallHistoryEntry): return; self.update(data.get_db_dict()) callhistoryobjectfactory=database.dataobjectfactory(CallHistoryDataobject) #------------------------------------------------------------------------------- class CallHistoryEntry(object): Folder_Incoming='Incoming' Folder_Outgoing='Outgoing' Folder_Missed='Missed' Folder_Data='Data' Valid_Folders=(Folder_Incoming, Folder_Outgoing, Folder_Missed, Folder_Data) _folder_key='folder' _datetime_key='datetime' _number_key='number' _unknown_datetime='YYYY-MM-DD hh:mm:ss' def __init__(self): self._data={ 'serials': [] } self._create_id() def __eq__(self, rhs): return self.folder==rhs.folder and self.datetime==rhs.datetime and\ self.number==rhs.number def __ne__(self, rhs): return self.folder!=rhs.folder or self.datetime!=rhs.datetime or\ self.number!=rhs.number def get(self): return copy.deepcopy(self._data, {}) def set(self, d): self._data={} self._data.update(d) def get_db_dict(self): return self.get() def set_db_dict(self, d): self.set(d) def _create_id(self): "Create a BitPim serial for this entry" self._data.setdefault("serials", []).append(\ {"sourcetype": "bitpim", "id": str(time.time())}) def _get_id(self): s=self._data.get('serials', []) for n in s: if n.get('sourcetype', None)=='bitpim': return n.get('id', None) return None def _set_id(self, id): s=self._data.get('serials', []) for n in s: if n.get('sourcetype', None)=='bitpim': n['id']=id return self._data['serials'].append({'sourcetype': 'bitpim', 'id': id } ) id=property(fget=_get_id, fset=_set_id) def _set_or_del(self, key, v, v_list=[]): if v is None or v in v_list: if self._data.has_key(key): del self._data[key] else: self._data[key]=v def _get_folder(self): return self._data.get(self._folder_key, '') def _set_folder(self, v): if v is None: if self._data.has_key(self._folder_key): del self._data[self._folder_key] return if not isinstance(v, (str, unicode)): raise TypeError,'not a string or unicode type' if v not in self.Valid_Folders: raise ValueError,'not a valid folder' self._data[self._folder_key]=v folder=property(fget=_get_folder, fset=_set_folder) def _get_number(self): return self._data.get(self._number_key, '') def _set_number(self, v): self._set_or_del(self._number_key, v, ['']) number=property(fget=_get_number, fset=_set_number) def _get_datetime(self): return self._data.get(self._datetime_key, '') def _set_datetime(self, v): # this routine supports 2 formats: # (y,m,d,h,m,s) and 'YYYYMMDDThhmmss' # check for None and delete manually if v is None: if self._data.has_key(self._datetime_key): del self._data[self._datetime_key] return if isinstance(v, (tuple, list)): if len(v)!=6: raise ValueError,'(y, m, d, h, m, s)' s='%04d%02d%02dT%02d%02d%02d'%tuple(v) elif isinstance(v, (str, unicode)): # some primitive validation if len(v)!=15 or v[8]!='T': raise ValueError,'value must be in format YYYYMMDDThhmmss' s=v else: raise TypeError self._data[self._datetime_key]=s datetime=property(fget=_get_datetime, fset=_set_datetime) def get_repr(self, name=None): # return a string representing this item in the format of # YYYY-MM-DD hh:mm:ss <Number/Name> f=self.folder[0].upper() s=self.datetime if not len(s): s=f+'['+self._unknown_datetime+']' else: s=f+'['+s[:4]+'-'+s[4:6]+'-'+s[6:8]+' '+s[9:11]+':'+s[11:13]+':'+s[13:]+'] - ' if name is not None: s+=name else: s+=phonenumber.format(self.number) return s def summary(self, name=None): # return a short summary for this entry in the format of # MM/DD hh:mm <Number/Name> s=self.datetime if s: s=s[4:6]+'/'+s[6:8]+' '+s[9:11]+':'+s[11:13]+' ' else: s='**/** **:** ' if name: s+=name else: s+=phonenumber.format(self.number) return s def _get_date_str(self): s=self.datetime if not len(s): return '****-**-**' else: return s[:4]+'-'+s[4:6]+'-'+s[6:8] date_str=property(fget=_get_date_str) #------------------------------------------------------------------------------- class CallHistoryWidget(scrolled.ScrolledPanel): _data_key='call_history' _by_type=0 _by_date=1 _by_number=2 def __init__(self, mainwindow, parent): super(CallHistoryWidget, self).__init__(parent, -1) self._main_window=mainwindow self._data={} self._node_dict={} self._name_map={} self._by_mode=self._by_type self._display_func=(self._display_by_type, self._display_by_date, self._display_by_number) # main box sizer vbs=wx.BoxSizer(wx.VERTICAL) self._item_list=wx.TreeCtrl(self, wx.NewId()) vbs.Add(self._item_list, 1, wx.EXPAND|wx.ALL, 5) self._root=self._item_list.AddRoot('Call History') self._nodes={} # context menu organize_menu=wx.Menu() organize_menu_data=( ('Type', self._OnOrganizedByType), ('Date', self._OnOrganizedByDate), ('Number', self._OnOrganizedByNumber)) for e in organize_menu_data: id=wx.NewId() organize_menu.AppendRadioItem(id, e[0]) wx.EVT_MENU(self, id, e[1]) context_menu_data=( ('Expand All', self._OnExpandAll), ('Collapse All', self._OnCollapseAll)) self._bgmenu=wx.Menu() self._bgmenu.AppendMenu(wx.NewId(), 'Organize Items by', organize_menu) for e in context_menu_data: id=wx.NewId() self._bgmenu.Append(id, e[0]) wx.EVT_MENU(self, id, e[1]) # event handlers pubsub.subscribe(self._OnPBLookup, pubsub.RESPONSE_PB_LOOKUP) wx.EVT_RIGHT_UP(self._item_list, self._OnRightClick) # all done self.SetSizer(vbs) self.SetAutoLayout(True) vbs.Fit(self) self.SetupScrolling() # populate data self._populate() def _OnPBLookup(self, msg): d=msg.data k=d.get('item', None) name=d.get('name', None) if k is None: return self._name_map[k]=name def _OnRightClick(self, evt): self._item_list.PopupMenu(self._bgmenu, evt.GetPosition()) def _OnOrganizedByType(self, evt): evt.GetEventObject().Check(evt.GetId(), True) if self._by_mode!=self._by_type: self._by_mode=self._by_type self._display_func[self._by_type]() self._expand_all() def _OnOrganizedByDate(self, evt): evt.GetEventObject().Check(evt.GetId(), True) if self._by_mode!=self._by_date: self._by_mode=self._by_date self._display_func[self._by_date]() self._expand_all() def _OnOrganizedByNumber(self, evt): evt.GetEventObject().Check(evt.GetId(), True) if self._by_mode!=self._by_number: self._by_mode=self._by_number self._display_func[self._by_number]() self._expand_all() def _expand_all(self, sel_id=None): if sel_id is None: sel_id=self._root self._item_list.Expand(sel_id) id, cookie=self._item_list.GetFirstChild(sel_id) while id.IsOk(): self._item_list.Expand(id) id, cookie=self._item_list.GetNextChild(sel_id, cookie) def _OnExpandAll(self, _): sel_id=self._item_list.GetSelection() if not sel_id.IsOk(): sel_id=self._root self._expand_all(sel_id) def _OnCollapseAll(self, _): sel_id=self._item_list.GetSelection() if not sel_id.IsOk(): sel_id=self._root self._item_list.Collapse(sel_id) id, cookie=self._item_list.GetFirstChild(sel_id) while id.IsOk(): self._item_list.Collapse(id) id, cookie=self._item_list.GetNextChild(sel_id, cookie) def _clear(self): self._item_list.Collapse(self._root) for k,e in self._nodes.items(): self._item_list.DeleteChildren(e) def _display_by_date(self): self._item_list.CollapseAndReset(self._root) self._nodes={} # go through our data to collect the dates date_list=[] for k,e in self._data.items(): if e.date_str not in date_list: date_list.append(e.date_str) date_list.sort() for s in date_list: self._nodes[s]=self._item_list.AppendItem(self._root, s) # build the tree for k,e in self._data.items(): i=self._item_list.AppendItem(self._nodes[e.date_str], e.get_repr(self._name_map.get(e.number, None))) self._item_list.SetItemPyData(i, k) def _display_by_number(self): self._item_list.CollapseAndReset(self._root) self._nodes={} # go through our data to collect the numbers number_list=[] for k,e in self._data.items(): s=phonenumber.format(e.number) if s not in number_list: number_list.append(s) number_list.sort() for s in number_list: self._nodes[s]=self._item_list.AppendItem(self._root, s) # build the tree for k,e in self._data.items(): i=self._item_list.AppendItem(self._nodes[phonenumber.format(e.number)], e.get_repr(self._name_map.get(e.number, None))) self._item_list.SetItemPyData(i, k) def _display_by_type(self): self._item_list.CollapseAndReset(self._root) self._nodes={} for s in CallHistoryEntry.Valid_Folders: self._nodes[s]=self._item_list.AppendItem(self._root, s) node_dict={} for k,e in self._data.items(): node_dict[e.get_repr(self._name_map.get(e.number, None))]=k keys=node_dict.keys() keys.sort() for k in keys: data_key=node_dict[k] n=self._data[data_key] i=self._item_list.AppendItem(self._nodes[n.folder], k) self._item_list.SetItemPyData(i, data_key) def _publish_today_data(self): keys=self._data.keys() keys.sort() keys.reverse() today_event=today.TodayIncomingCallsEvent() today_event.names=[self._data[k].summary(self._name_map.get(self._data[k].number, None))\ for k in keys \ if self._data[k].folder==CallHistoryEntry.Folder_Incoming] today_event.broadcast() today_event=today.TodayMissedCallsEvent() today_event.names=[self._data[k].summary(self._name_map.get(self._data[k].number, None))\ for k in keys \ if self._data[k].folder==CallHistoryEntry.Folder_Missed] today_event.broadcast() def _populate(self): self._clear() self._node_dict={} # lookup phone book for names for k,e in self._data.items(): if not self._name_map.has_key(e.number): pubsub.publish(pubsub.REQUEST_PB_LOOKUP, { 'item': e.number } ) self._display_func[self._by_mode]() self._publish_today_data() def OnDelete(self, _): sel_idx=self._item_list.GetSelection() if not sel_idx.Ok(): return k=self._item_list.GetPyData(sel_idx) if k is None: # this is not a leaf node return self._item_list.Delete(sel_idx) del self._data[k] self._save_to_db(self._data) def getdata(self, dict, want=None): dict[self._data_key]=copy.deepcopy(self._data) def populate(self, dict): self._data=dict.get(self._data_key, {}) self._populate() def _save_to_db(self, dict): db_rr={} for k,e in dict.items(): db_rr[k]=CallHistoryDataobject(e) database.ensurerecordtype(db_rr, callhistoryobjectfactory) self._main_window.database.savemajordict(self._data_key, db_rr) def populatefs(self, dict): self._save_to_db(dict.get(self._data_key, {})) return dict def getfromfs(self, result): dict=self._main_window.database.\ getmajordictvalues(self._data_key, callhistoryobjectfactory) r={} for k,e in dict.items(): ce=CallHistoryEntry() ce.set_db_dict(e) r[ce.id]=ce result.update({ self._data_key: r}) return result def merge(self, dict): d=dict.get(self._data_key, {}) l=[e for k,e in self._data.items()] for k,e in d.items(): if e not in l: self._data[e.id]=e self._save_to_db(self._data) self._populate()
call_history.py.diff
(application/octet-stream, 862 B)
Index: call_history.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/call_history.py,v
retrieving revision 1.4
diff -u -r1.4 call_history.py
--- call_history.py 29 Jul 2005 03:25:30 -0000 1.4
+++ call_history.py 9 Aug 2005 04:26:18 -0000
@@ -98,7 +98,14 @@
if n.get('sourcetype', None)=='bitpim':
return n.get('id', None)
return None
- id=property(fget=_get_id)
+ def _set_id(self, id):
+ s=self._data.get('serials', [])
+ for n in s:
+ if n.get('sourcetype', None)=='bitpim':
+ n['id']=id
+ return
+ self._data['serials'].append({'sourcetype': 'bitpim', 'id': id } )
+ id=property(fget=_get_id, fset=_set_id)
def _set_or_del(self, key, v, v_list=[]):
if v is None or v in v_list:
com_lgvx4400.py
(application/octet-stream, 40.9 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.128 2005/05/23 05:58:29 rogerb 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 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 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(self.protocolclass.SPEEDDIALINDEX==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(self.protocolclass.SPEEDDIALINDEX==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 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 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 ) 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 #('sms', 'read', None), ('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'), ) 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_lgvx4400.py.diff
(application/octet-stream, 3.4 KB)
Index: com_lgvx4400.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/com_lgvx4400.py,v
retrieving revision 1.128
diff -u -r1.128 com_lgvx4400.py
--- com_lgvx4400.py 23 May 2005 05:58:29 -0000 1.128
+++ com_lgvx4400.py 10 Aug 2005 05:44:14 -0000
@@ -370,7 +370,10 @@
xx=data['speeddials'][sd]
if xx[0]==bps:
found=True
- newspeeds[sd]=(res.entry.entrynumber, xx[1])
+ if(self.protocolclass.SPEEDDIALINDEX==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]))
@@ -625,7 +628,14 @@
print "can't find ringtone for index",entry.msgringtone
if len(res['ringtones'])==0:
del res['ringtones']
+
+ if(self.protocolclass.SPEEDDIALINDEX==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):
@@ -636,6 +646,7 @@
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]:
@@ -643,8 +654,26 @@
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
com_lgvx8100.py
(application/octet-stream, 15.9 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 time import cStringIO import sha # my modules import common import copy import p_lgvx8100 import com_lgvx7000 import com_brew import com_phone import com_lg import prototypes import bpcalendar import call_history 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/ms', 100, 50, 1), ) builtinwallpapers = () # none wallpaperlocations= ( ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0), ) 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 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=='': #empty record break entry=call_history.CallHistoryEntry() entry.folder=folder # convert from GPS to unix time unixtime=315964800+call.GPStime t=time.gmtime(315964800+call.GPStime) entry.datetime=((t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)) entry.number=call.number #use the timestamp as the ID, it is unique entry.id=call.GPStime res[entry.id]=entry except com_brew.BrewNoSuchFileException: pass # do nothing if file doesn't exist return 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.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 # bitpim does not support vibrate so turn it off for all alarms vibrate=1 if entry.alarm>=2880: entry.alarm=2880 data.alarmminutes=0 data.alarmhours=48 data.alarmindex_vibrate=0x10+vibrate if entry.alarm>=1440: entry.alarm=1440 data.alarmminutes=0 data.alarmhours=24 data.alarmindex_vibrate=0xe+vibrate if entry.alarm>=120: entry.alarm=120 data.alarmminutes=0 data.alarmhours=2 data.alarmindex_vibrate=0xc+vibrate elif entry.alarm>=60: entry.alarm=60 data.alarmminutes=0 data.alarmhours=1 data.alarmindex_vibrate=0xa+vibrate elif entry.alarm>=15: entry.alarm=15 data.alarmminutes=15 data.alarmhours=0 data.alarmindex_vibrate=0x8+vibrate elif entry.alarm>=10: entry.alarm=10 data.alarmminutes=10 data.alarmhours=0 data.alarmindex_vibrate=0x6+vibrate elif entry.alarm>=5: entry.alarm=5 data.alarmminutes=10 data.alarmhours=0 data.alarmindex_vibrate=0x4+vibrate elif entry.alarm>=0: entry.alarm=0 data.alarmminutes=0 data.alarmhours=0 data.alarmindex_vibrate=0x2+vibrate else: # no alarm data.alarmminutes=0x64 data.alarmhours=0x64 data.alarmindex_vibrate=1 return parentprofile=com_lgvx7000.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname BP_Calendar_Version=3 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 8010 doesn't have seperate origins - they are all dumped in "images" imageorigins={} imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) def GetImageOrigins(self): return self.imageorigins # our targets are the same for all origins imagetargets={} imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", {'width': 176, 'height': 184, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", {'width': 176, 'height': 184, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", {'width': 96, 'height': 80, 'format': "JPEG"})) imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", {'width': 176, 'height': 220, 'format': "JPEG"})) def GetTargetsForImageOrigin(self, origin): return self.imagetargets def __init__(self): parentprofile.__init__(self) _supportedsyncs=( ('phonebook', 'read', None), # all phonebook reading ('calendar', 'read', None), # all calendar reading ('wallpaper', 'read', None), # all wallpaper reading ('ringtone', 'read', None), # all ringtone reading ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper ('wallpaper', 'write', 'OVERWRITE'), ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone ('ringtone', 'write', 'OVERWRITE'), ('call_history', 'read', None), )
com_lgvx8100.py.diff
(application/octet-stream, 12.9 KB)
Index: com_lgvx8100.py =================================================================== RCS file: /cvsroot/bitpim/bitpim/com_lgvx8100.py,v retrieving revision 1.1 diff -u -r1.1 com_lgvx8100.py --- com_lgvx8100.py 1 Aug 2005 06:08:52 -0000 1.1 +++ com_lgvx8100.py 10 Aug 2005 05:20:37 -0000 @@ -1,7 +1,7 @@ ### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> -### Copyright (C) 2005 Simon <[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. @@ -27,6 +27,13 @@ import com_phone import com_lg import prototypes +import bpcalendar +import call_history + + +from prototypes import * + + class Phone(com_lgvx7000.Phone): "Talk to the LG VX8100 cell phone" @@ -84,7 +91,7 @@ self.getringtoneindices(results) self.log("Fundamentals retrieved") return results - + def savegroups(self, data): groups=data['groups'] keys=groups.keys() @@ -100,13 +107,282 @@ self.logdata("New group file", buffer.getvalue(), g) self.writefile("pim/pbgroup.dat", buffer.getvalue()) + 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=='': #empty record + break + entry=call_history.CallHistoryEntry() + entry.folder=folder + # convert from GPS to unix time + unixtime=315964800+call.GPStime + t=time.gmtime(315964800+call.GPStime) + entry.datetime=((t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)) + entry.number=call.number + #use the timestamp as the ID, it is unique + entry.id=call.GPStime + res[entry.id]=entry + except com_brew.BrewNoSuchFileException: + pass # do nothing if file doesn't exist + return + + + 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.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 + # bitpim does not support vibrate so turn it off for all alarms + vibrate=1 + if entry.alarm>=2880: + entry.alarm=2880 + data.alarmminutes=0 + data.alarmhours=48 + data.alarmindex_vibrate=0x10+vibrate + if entry.alarm>=1440: + entry.alarm=1440 + data.alarmminutes=0 + data.alarmhours=24 + data.alarmindex_vibrate=0xe+vibrate + if entry.alarm>=120: + entry.alarm=120 + data.alarmminutes=0 + data.alarmhours=2 + data.alarmindex_vibrate=0xc+vibrate + elif entry.alarm>=60: + entry.alarm=60 + data.alarmminutes=0 + data.alarmhours=1 + data.alarmindex_vibrate=0xa+vibrate + elif entry.alarm>=15: + entry.alarm=15 + data.alarmminutes=15 + data.alarmhours=0 + data.alarmindex_vibrate=0x8+vibrate + elif entry.alarm>=10: + entry.alarm=10 + data.alarmminutes=10 + data.alarmhours=0 + data.alarmindex_vibrate=0x6+vibrate + elif entry.alarm>=5: + entry.alarm=5 + data.alarmminutes=10 + data.alarmhours=0 + data.alarmindex_vibrate=0x4+vibrate + elif entry.alarm>=0: + entry.alarm=0 + data.alarmminutes=0 + data.alarmhours=0 + data.alarmindex_vibrate=0x2+vibrate + else: # no alarm + data.alarmminutes=0x64 + data.alarmhours=0x64 + data.alarmindex_vibrate=1 + return parentprofile=com_lgvx7000.Profile class Profile(parentprofile): protocolclass=Phone.protocolclass serialsname=Phone.serialsname + BP_Calendar_Version=3 + WALLPAPER_WIDTH=176 WALLPAPER_HEIGHT=184 MAX_WALLPAPER_BASENAME_LENGTH=32 @@ -145,13 +421,14 @@ _supportedsyncs=( ('phonebook', 'read', None), # all phonebook reading - # ('calendar', 'read', None), # all calendar reading + ('calendar', 'read', None), # all calendar reading ('wallpaper', 'read', None), # all wallpaper reading ('ringtone', 'read', None), # all ringtone reading ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook - # ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar + ('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), )
p_lgvx4400.p
(application/octet-stream, 4.5 KB) - not displayed
p_lgvx4400.p.diff
(application/octet-stream, 452 B)
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 10 Aug 2005 05:22:26 -0000 @@ -23,6 +23,7 @@ NUMSPEEDDIALS=100 FIRSTSPEEDDIAL=1 LASTSPEEDDIAL=99 +SPEEDDIALINDEX=0 #0=by position, 1=by type NUMPHONEBOOKENTRIES=200 MAXCALENDARDESCRIPTION=38
p_lgvx8100.p
(application/octet-stream, 7.7 KB) - not displayed
p_lgvx8100.p.diff
(application/octet-stream, 7.5 KB)
Index: p_lgvx8100.p =================================================================== RCS file: /cvsroot/bitpim/bitpim/p_lgvx8100.p,v retrieving revision 1.1 diff -u -r1.1 p_lgvx8100.p --- p_lgvx8100.p 1 Aug 2005 06:08:52 -0000 1.1 +++ p_lgvx8100.p 10 Aug 2005 05:20:33 -0000 @@ -1,7 +1,7 @@ ### BITPIM ### ### Copyright (C) 2003-2005 Roger Binns <[email protected]> -### Copyright (C) 2005 Simon <[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. @@ -25,6 +25,116 @@ UINT=UINTlsb BOOL=BOOLlsb +# vx8100 uses a type based index for speed dials instead of positional like the vx4400 +SPEEDDIALINDEX=1 +MAXCALENDARDESCRIPTION=32 + + +class LGCALREPEAT(UINTlsb): + def __init__(self, *args, **kwargs): + """A 32-bit bitmapped value used to store repeat info for events in the LG calendar""" + super(LGCALREPEAT,self).__init__(*args, **kwargs) + + # The meaning of the bits in this field + # MSB LSB + # 3 2 1 + # 10987654321098765432109876543210 + # 210 repeat_type + # 0 exceptions, set to 1 if there are exceptions + # 6543210 dow_weekly (weekly repeat type) + # 210 dow (monthly repeat type) + # 543210 interval + # 3210 month_index + # 543210 day_index + + # repeat_type: 0=none, 1=daily, 2=weekly, 3=monthly, 4=yearly, 5=weekdays, 6=XthDayEachMonth(e.g. 3rd Friday each month) + # dow_weekly: Weekly repeat type only. Identical to bpcalender dow bits, multiple selections allowed(Bit0=sun,Bit1=mon,Bit2=tue,Bit3=wed,Bit4=thur,Bit5=fri,Bit6=sat) + # dow_monthly: Monthly repeat type 6 only. (0=sun,1=mon,2=tue,3=wed,4=thur,5=fri,6=sat) + # interval: repeat interval, eg. every 1 week, 2 weeks 4 weeks etc. Also be used for months, but bp does not support this. + # month_index: For type 4 this is the month the event starts in + # day_index: For type 6 this represents the number of the day that is the repeat, e.g. "2"nd tuesday + # For type 3&4 this is the day of the month that the repeat occurs, usually the same as the start date. + # bp does not support this not being the support date + + dict={'sizeinbytes': 4} + dict.update(kwargs) + + if self._ismostderived(LGCALREPEAT): + self._update(args,kwargs) + + def _update(self, args, kwargs): + for k in 'constant', 'default', 'value': + if kwargs.has_key(k): + kwargs[k]=self._converttoint(kwargs[k]) + if len(args)==0: + pass + elif len(args)==1: + args=(self._converttoint(args[0]),) + else: + raise TypeError("expected (type, dow, interval) as arg") + + super(LGCALREPEAT,self)._update(args, kwargs) # we want the args + self._complainaboutunusedargs(LGCALDATE,kwargs) + assert self._sizeinbytes==4 + + def getvalue(self): + val=super(LGCALREPEAT,self).getvalue() + # get repeat type + type=val&0x7 # 3 bits + val>>=4 + exceptions=val&0x1 + val>>=1 + #get day of week, only valid for some repeat types + #format of data is also different for different repeat types + if type==6: # for monthly repeats + dow=1<<(val&3) #day of month, valid for monthly repeat types, need to convert to bitpim format + elif type==2: #weekly + dow=val&0x7f # 7 bits, already matched bpcalender format + else: + dow=0 + # get interval + if type==6: + val>>=20 + interval=val&0x1f # day_index + else: + val>>=9 + interval=val&0x3f + return (type, dow, interval, exceptions) + + _caldomvalues={ + 0x01: 0x0, #sun + 0x02: 0x1, #mon + 0x04: 0x2, #tue + 0x08: 0x3, #wed + 0x10: 0x4, #thur + 0x20: 0x5, #fri + 0x40: 0x6 #sat + } + + def _converttoint(self, repeat): + assert len(repeat)==4 + type,dow,interval,exceptions=repeat + val=0 + # construct bitmapped value for repeat + # look for weekday type + val=interval + if type==6 or type==3: + val<<=11 + val|=1 # force monthly interval to 1 + if type==4: #yearly + val<<=11 + val|=dow + val<<=9 + if type==2: + val|=dow + elif type==6: + val|=self._caldomvalues[dow] + val<<=1 + val|=exceptions + val<<=4 + val|=type + return val + %} PACKET indexentry: @@ -46,3 +156,64 @@ PACKET pbgroups: "Phonebook groups" * LIST {'elementclass': pbgroup} +groups + +### +### The calendar +### +# +# The calendar consists of one file listing events and an exception +# file that lists exceptions. These exceptions suppress a particular +# instance of a repeated event. For example, if you setup something +# to happen monthly, but changed the 1st february event, then the +# schedule will contain the repeating event, and the 1st feb one, +# and the suppresions/exceptions file will point to the repeating +# event and suppress the 1st feb. +# The phone uses the position within the file to give an event an id + +PACKET scheduleexception: + 4 UINT pos "Refers to event id (position in schedule file) that this suppresses" + 1 UINT day + 1 UINT month + 2 UINT year + +PACKET scheduleexceptionfile: + * LIST {'elementclass': scheduleexception} +items + +PACKET scheduleevent: + 4 UINT pos "position within file, used as an event id" + 33 STRING {'raiseonunterminatedread': False} description + 4 LGCALDATE start + 4 LGCALDATE end + 4 LGCALREPEAT repeat # complicated bit mapped field + 1 UINT alarmindex_vibrate #LSBit of this set vibrate ON(0)/OFF(1), the 7 MSBits are the alarm index + #the alarmindex is the index into the amount of time in advance of the + #event to notify the user. It is directly related to the alarmminutes + #and alarmhours below, valid values are + # 8=2days, 7=1day, 6=2hours, 5=1hour, 4=15mins, 3=10mins, 2=5mins, 1=0mins, 0=NoAlarm + 1 UINT ringtone + 1 UINT unknown1 + 1 UINT alarmminutes "a value of 0xFF indicates not set" + 1 UINT alarmhours "a value of 0xFF indicates not set" + 1 UINT unknown2 + + +PACKET schedulefile: + 2 UINT numactiveitems + * LIST {'elementclass': scheduleevent} +events + +PACKET call: + 4 UINT GPStime #no. of seconds since 0h 1-6-80, based off local time. + 4 UINT unknown2 # 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 + 2 UINT numberlength # length of phone number + 1 UINT pbnumbertype # 1=cell, 2=home, 3=office, 4=cell2, 5=fax, 6=vmail, 0xFF=not in phone book + 3 UINT unknown2 # always seems to be 0 + 2 UINT pbentrynum #entry number in phonebook + +PACKET callhistory: + 4 UINT numcalls + 1 UINT unknown1 + * LIST {'elementclass': call} +calls +
phone-lgvx8100.htd
(application/octet-stream, 575 B) - not displayed
phone-lgvx8100-cables.htd
(application/octet-stream, 999 B) - not displayed
phone-lgvx8100-notes.htd
(application/octet-stream, 951 B) - not displayed