0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004 Joe Pham <djpham@netzero.com> 0004 ### Copyright (C) 2004 Stephen Wood <saw@bitpim.org> 0005 ### 0006 ### This program is free software; you can redistribute it and/or modify 0007 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0008 ### 0009 ### $Id: com_samsung.py 4365 2007-08-17 21:11:59Z djpham $ 0010 0011 """Communicate with a Samsung SCH-Axx phone using AT commands""" 0012 # standard modules 0013 import copy 0014 import datetime 0015 import re 0016 import time 0017 0018 # site-packages 0019 from thirdparty import DSV 0020 0021 # BitPim modules 0022 import bpcalendar 0023 import com_brew 0024 import com_phone 0025 import common 0026 import commport 0027 import memo 0028 import p_brew 0029 import phoneinfo 0030 import sms 0031 import todo 0032 0033 class Phone(com_phone.Phone,com_brew.BrewProtocol): 0034 "Talk to a Samsung phone using AT commands" 0035 0036 desc="Samsung SCH-Axx phone" 0037 0038 MODEPHONEBOOK="modephonebook" 0039 0040 _AT_str="AT" 0041 _OK_str="\r\nOK\r\n" 0042 _Error_str="\r\nERROR\r\n" 0043 _read_timeout=0.1 0044 # Calendar class vars 0045 _cal_entries_range=xrange(20) 0046 _cal_max_events=20 0047 _cal_max_events_per_day=9 0048 _cal_num_of_read_fields=7 0049 _cal_num_of_write_fields=6 0050 _cal_entry=0 0051 _cal_start_datetime=1 0052 _cal_end_datetime=2 0053 # if your phone does not support and end-datetime, set this to a default value 0054 # if it does support end-datetime, set this to None 0055 _cal_end_datetime_value='19800106T000000' 0056 _cal_datetime_stamp=3 0057 _cal_alarm_type=4 0058 _cal_read_name=6 0059 _cal_write_name=5 0060 _cal_alarm_values={ 0061 '0': -1, '1': 0, '2': 10, '3': 30, '4': 60 } 0062 _cal_max_name_len=32 0063 _switch_mode_cmd='\x44\x58\xf4\x7e' 0064 0065 def __init__(self, logtarget, commport): 0066 "Call all the contructors and sets initial modes" 0067 com_phone.Phone.__init__(self, logtarget, commport) 0068 com_brew.BrewProtocol.__init__(self) 0069 self.mode=self.MODENONE 0070 0071 def _setmodephonebooktobrew(self): 0072 self.setmode(self.MODEMODEM) 0073 self.setmode(self.MODEBREW) 0074 return True 0075 0076 def _setmodemodemtobrew(self): 0077 self.log('Switching from modem to BREW') 0078 try: 0079 self.comm.sendatcommand('$QCDMG') 0080 return True 0081 except: 0082 pass 0083 # give it another try 0084 self.log('Retry switching from modem to BREW') 0085 try: 0086 self.comm.sendatcommand('$QCDMG') 0087 return True 0088 except commport.ATError: 0089 return False 0090 except: 0091 if __debug__: 0092 self.log('Got an excepetion') 0093 return False 0094 0095 def _setmodebrew(self): 0096 # switch from None to BREW 0097 self.log('Switching from None to BREW') 0098 # do it the long, but sure, way: 1st try to switch to modem 0099 if not self._setmodemodem(): 0100 # can't switch to modem, give up 0101 return False 0102 # then switch from modem to BREW 0103 return self._setmodemodemtobrew() 0104 0105 def _setmodebrewtomodem(self): 0106 self.log('Switching from BREW to modem') 0107 try: 0108 self.comm.write(self._switch_mode_cmd, False) 0109 self.comm.readsome(numchars=5, log=False) 0110 return True 0111 except: 0112 pass 0113 # give it a 2nd try 0114 try: 0115 self.comm.write(self._switch_mode_cmd, False) 0116 self.comm.readsome(numchars=5, log=False) 0117 return True 0118 except: 0119 return False 0120 0121 def _setmodemodemtophonebook(self): 0122 self.log('Switching from modem to phonebook') 0123 response=self.comm.sendatcommand("#PMODE=1") 0124 return True 0125 0126 def _setmodemodem(self): 0127 self.log('Switching to modem') 0128 try: 0129 self.comm.sendatcommand('E0V1') 0130 return True 0131 except: 0132 pass 0133 # could be in BREW mode, try switch over 0134 self.log('trying to switch from BREW mode') 0135 if not self._setmodebrewtomodem(): 0136 return False 0137 try: 0138 self.comm.sendatcommand('E0V1') 0139 return True 0140 except: 0141 return False 0142 0143 def _setmodephonebook(self): 0144 self.setmode(self.MODEMODEM) 0145 self.setmode(self.MODEPHONEBOOK) 0146 return True 0147 0148 def _setmodephonebooktomodem(self): 0149 self.log('Switching from phonebook to modem') 0150 response=self.comm.sendatcommand("#PMODE=0") 0151 return True 0152 0153 def _get_at_response(self): 0154 s=self.comm.read(1, False) 0155 if not len(s): 0156 return '' 0157 0158 # got at least one char, try to read the rest with short timeout 0159 0160 i=self.comm.ser.getTimeout() 0161 self.comm.ser.setTimeout(self._read_timeout) 0162 while True: 0163 s1=self.comm.read(1, False) 0164 if len(s1): 0165 s += s1 0166 else: 0167 break 0168 0169 self.comm.ser.setTimeout(i) 0170 return s 0171 0172 def is_online(self): 0173 self.setmode(self.MODEPHONEBOOK) 0174 try: 0175 self.comm.sendatcommand("E0V1") 0176 return True 0177 except commport.ATError: 0178 return False 0179 0180 def get_esn(self): 0181 try: 0182 s=self.comm.sendatcommand("+gsn") 0183 if len(s): 0184 return ' '.join(s[0].split(": ")[1:]) 0185 except commport.ATError: 0186 pass 0187 return '' 0188 0189 def _send_and_get(self, at_command): 0190 try: 0191 s=self.comm.sendatcommand(str(at_command)) 0192 if len(s): 0193 return self.splitandunescape(s[0]) 0194 except commport.ATError: 0195 pass 0196 return None 0197 0198 def get_model(self): 0199 return ','.join(self._send_and_get('+GMM')) 0200 def get_manufacturer(self): 0201 return ','.join(self._send_and_get('+GMI')) 0202 def get_phone_number(self): 0203 return self._send_and_get('+MIN?')[0] 0204 def get_firmware_version(self): 0205 return ','.join(self._send_and_get('+GMR')) 0206 def get_battery_level(self): 0207 s=self._send_and_get('+CBC?') 0208 if s is not None and len(s)==2: 0209 return s[1]+'%' 0210 def get_signal_quality(self): 0211 s=self._send_and_get('+CSQ?') 0212 if s is not None and len(s)==2: 0213 return str(100*int(s[0])/31)+'%' 0214 def get_analog_digital(self): 0215 d={ '0': 'Analog', '1': 'Digital' } 0216 s=self._send_and_get('+CAD?') 0217 return d.get(s[0], '<Unknown>') 0218 0219 def get_groups(self, groups_range): 0220 0221 g=[] 0222 for i in groups_range: 0223 try: 0224 s=self.comm.sendatcommand("#PBGRR=%d" % i) 0225 if len(s): 0226 g.append(s[0].split(': ')[1].split(',')[1].strip('"')) 0227 else: 0228 g.append('') 0229 except commport.ATError: 0230 g.append('') 0231 return g 0232 0233 def get_phone_entry(self, entry_index, alias_column=-1, num_columns=-1): 0234 try: 0235 s=self.comm.sendatcommand("#PBOKR=%d" % entry_index) 0236 if len(s): 0237 line=s[0] 0238 if alias_column >= 0 and alias_column < num_columns: 0239 line=self.defrell(line, alias_column, num_columns) 0240 return self.splitandunescape(line) 0241 except commport.ATError: 0242 pass 0243 return [] 0244 0245 def del_phone_entry(self, entry_index): 0246 try: 0247 s=self.comm.sendatcommand("#PBOKW=%d" % entry_index) 0248 return True 0249 except commport.ATError: 0250 return False 0251 0252 def save_phone_entry(self, entry_str): 0253 try: 0254 s=self.comm.sendatcommand("#PBOKW="+entry_str) 0255 return True 0256 except commport.ATError: 0257 return False 0258 0259 def get_time_stamp(self): 0260 0261 now = time.localtime(time.time()) 0262 return "%04d%02d%02dT%02d%02d%02d" % now[0:6] 0263 0264 def phonize(self, str): 0265 """Convert the phone number into something the phone understands 0266 All digits, P, T, * and # are kept, everything else is removed""" 0267 0268 return re.sub("[^0-9PT#*]", "", str) 0269 0270 def get_calendar_entry(self, entry_index): 0271 try: 0272 s=self.comm.sendatcommand('#PISHR=%d' % entry_index) 0273 if len(s): 0274 return self.splitandunescape(s[0]) 0275 except commport.ATError: 0276 pass 0277 return [] 0278 0279 def save_calendar_entry(self, entry_str): 0280 try: 0281 self.comm.sendatcommand('#PISHW='+entry_str) 0282 return True 0283 except: 0284 return False 0285 0286 def get_memo_entry(self, entry_index): 0287 try: 0288 s=self.comm.sendatcommand('#PIMMR=%d'%entry_index) 0289 if len(s): 0290 return self.splitandunescape(s[0]) 0291 except commport.ATError: 0292 pass 0293 return [] 0294 0295 def save_memo_entry(self, entry_str): 0296 try: 0297 self.comm.sendatcommand('#PIMMW='+entry_str) 0298 return True 0299 except: 0300 return False 0301 0302 def get_todo_entry(self, entry_index): 0303 try: 0304 s=self.comm.sendatcommand('#PITDR=%d' % entry_index) 0305 if len(s): 0306 return self.splitandunescape(s[0]) 0307 except commport.ATError: 0308 pass 0309 return [] 0310 0311 def save_todo_entry(self, entry_str): 0312 try: 0313 self.comm.sendatcommand("#PITDW="+entry_str) 0314 return True 0315 except: 0316 return False 0317 0318 def get_sms_inbox(self, entry_index): 0319 try: 0320 s=self.comm.sendatcommand('#psrmr=%d'%entry_index) 0321 if len(s): 0322 return self.splitandunescape(s[0]) 0323 except commport.ATError: 0324 pass 0325 return [] 0326 def get_sms_saved(self, entry_index): 0327 try: 0328 s=self.comm.sendatcommand('#psfmr=%d'%entry_index) 0329 if len(s): 0330 return self.splitandunescape(s[0]) 0331 except commport.ATError: 0332 pass 0333 return [] 0334 def get_sms_sent(self, entry_index): 0335 try: 0336 s=self.comm.sendatcommand('#pssmr=%d'%entry_index) 0337 if len(s): 0338 return self.splitandunescape(s[0]) 0339 except commport.ATError: 0340 pass 0341 return [] 0342 def get_canned_msg(self, entry_index): 0343 try: 0344 s=self.comm.sendatcommand('#psstr=%d'%entry_index) 0345 if len(s): 0346 return self.splitandunescape(s[0]) 0347 except commport.ATError: 0348 pass 0349 return [] 0350 def save_canned_msg(self, entry_str): 0351 try: 0352 self.comm.sendatcommand('#psstw='+entry_str) 0353 return True 0354 except: 0355 return False 0356 0357 def extract_timedate(self, td): 0358 # extract samsung timedate 'YYYYMMDDTHHMMSS' to (y, m, d, h, m) 0359 return (int(td[:4]), int(td[4:6]), int(td[6:8]), int(td[9:11]), int(td[11:13])) 0360 0361 def encode_timedate(self, td): 0362 # reverse if extract_timedate 0363 return "%04d%02d%02dT%02d%02d00" % tuple(td) 0364 0365 def splitandunescape(self, line): 0366 """Split fields and unescape double quote and right brace""" 0367 # Should unescaping be done on fields that are not surrounded by 0368 # double quotes? DSV strips these quotes, so we have to do it to 0369 # all fields. 0370 col=line.find(": ") 0371 print line[col+2:] 0372 e=DSV.importDSV([line[col+2:]])[0] 0373 i=0 0374 while i<len(e): 0375 item=e[i] 0376 item=item.replace("}\002",'"') 0377 item=item.replace("}]","}") 0378 e[i]=item 0379 i+=1 0380 0381 return e 0382 0383 def samsungescape(self, s): 0384 """Escape double quotes and }'s in a string""" 0385 #s=s.replace("}","}]") 0386 #s=s.replace('"','}\002') 0387 return s 0388 0389 def defrell(self, s, acol, ncol): 0390 """Fixes up phonebook responses with the alias field. The alias field 0391 does not have quotes around it, but can still contain commas""" 0392 # Example with A670 self.defrell(s, 17, 26) 0393 if acol<0 or acol>=ncol: # Invalid alias column, do nothing 0394 return s 0395 e=s.split(",") 0396 i=0 0397 0398 while i<len(e): 0399 # Recombine when ,'s in quotes 0400 if len(e[i]) and e[i][0]=='"' and e[i][-1]!='"': 0401 while i+1<len(e) and (len(e[i+1])==0 or e[i+1][-1]!='"'): 0402 e[i] += ","+e[i+1] 0403 del e[i+1] 0404 else: 0405 if i+1<len(e): 0406 e[i] += ","+e[i+1] 0407 del e[i+1] 0408 i+=1 0409 0410 if len(e)<=ncol: # Return original string if no excess commas 0411 return s 0412 0413 for k in range(len(e)-ncol): 0414 e[acol]+=","+e[acol+1] 0415 del e[acol+1] 0416 0417 e[acol]='"'+e[acol]+'"' # quote the string 0418 0419 res=e[0] 0420 for item in e[1:]: # Rejoin the columns 0421 res+=","+item 0422 0423 return res 0424 0425 0426 def csvsplit(self, line): 0427 """Parse a Samsung comma separated list.""" 0428 e=line.split(",") 0429 i=0 0430 print len(e) 0431 result=[] 0432 while i<len(e): 0433 # Recombine when ;'s in quotes 0434 if len(e[i]) and e[i][0]=='"' and e[i][-1]!='"': 0435 while i+1<len(e) and (len(e[i+1])==0 or e[i+1][-1]!='"'): 0436 e[i] = e[i]+","+e[i+1] 0437 del e[i+1] 0438 else: 0439 if i+1<len(e): 0440 e[i] = e[i]+","+e[i+1] 0441 del e[i+1] 0442 0443 0444 # Identify type of each item 0445 # Strip quotes on strings 0446 # Un escape escaped characters 0447 item=e[i] 0448 if len(item)==0: 0449 t=0 0450 elif item[0]=='"' or item[-1]=='"': 0451 mo=re.match('^"?(.*?)"?$',item) 0452 item=mo.group(1) 0453 item=item.replace("}\002",'"') 0454 item=item.replace("}]","}") 0455 t='string' 0456 elif re.match('^\d+T\d+$',item): 0457 t='timestamp' 0458 elif re.match('^[\dPT]+$',item): 0459 # Number or phone number 0460 t='number' 0461 elif re.match('^\(\d+-\d+\)',item): 0462 t='range' 0463 elif re.match('^\d\d?/\d\d?/\d\d(\d\d)?$',item): 0464 t='date' 0465 else: 0466 t='other' 0467 0468 if t: 0469 result.append({'type':t, 'value':item}) 0470 else: 0471 result.append(0) 0472 0473 i+=1 0474 0475 return result 0476 0477 def getcalendar(self, result): 0478 self.log("Getting calendar entries") 0479 self.setmode(self.MODEPHONEBOOK) 0480 res={} 0481 l=len(self._cal_entries_range) 0482 cal_cnt=0 0483 for k in self._cal_entries_range: 0484 r=self.get_calendar_entry(k) 0485 if not len(r): 0486 # blank, no entry 0487 self.progress(k+1, l, "Getting blank entry: %d"%k) 0488 continue 0489 self.progress(k+1, l, "Getting "+r[self._cal_read_name]) 0490 0491 # build a calendar entry 0492 entry=bpcalendar.CalendarEntry() 0493 0494 # start time date 0495 entry.start=self.extract_timedate(r[self._cal_start_datetime]) 0496 0497 0498 if self._cal_end_datetime_value is None: 0499 # valid end time 0500 entry.end=self.extract_timedate(r[self._cal_end_datetime]) 0501 else: 0502 # no end time, end time=start time 0503 entry.end=entry.start 0504 0505 # description 0506 entry.description=r[self._cal_read_name] 0507 0508 # alarm 0509 try: 0510 alarm=self._cal_alarm_values[r[self._cal_alarm_type]] 0511 except: 0512 alarm=None 0513 entry.alarm=alarm 0514 0515 # update calendar dict 0516 res[entry.id]=entry 0517 cal_cnt += 1 0518 result['calendar']=res 0519 self.setmode(self.MODEMODEM) 0520 return result 0521 0522 def process_calendar(self, dict): 0523 """ Optimize and expand calendar data suitable for phone download 0524 """ 0525 # first go thru the dict to organize events by date 0526 # and also determine the latest event date 0527 r={} 0528 rp=[] 0529 today=datetime.date.today() 0530 last_date=today 0531 if __debug__: 0532 print 'original calendar:' 0533 for k,e in dict.items(): 0534 if __debug__: 0535 print e.description,':',e.start 0536 sd=datetime.date(*e.start[:3]) 0537 ed=datetime.date(*e.end[:3]) 0538 if ed>last_date: 0539 last_date=ed 0540 if e.repeat is None: 0541 if sd>=today: 0542 r.setdefault(e.start[:3], []).append(Samsung_Calendar(e)) 0543 else: 0544 if ed>=today: 0545 rp.append(e) 0546 # go through and expand on the repeated events 0547 delta_1=datetime.timedelta(1) 0548 for n in rp: 0549 current_date=today 0550 end_date=datetime.date(*n.end[:3]) 0551 cnt=0 0552 while current_date<=end_date: 0553 if n.is_active(current_date.year, current_date.month, 0554 current_date.day): 0555 cd_l=(current_date.year, current_date.month, 0556 current_date.day) 0557 r.setdefault(cd_l, []).append(\ 0558 Samsung_Calendar(n, cd_l)) 0559 cnt+=1 0560 if cnt>self._cal_max_events: 0561 # enough for this one, breaking out 0562 break 0563 current_date+=delta_1 0564 # and put them all into a list 0565 res=[] 0566 keys=r.keys() 0567 # sort by date 0568 keys.sort() 0569 for k in keys: 0570 # sort by time within this date 0571 r[k].sort() 0572 # clip by max events/day 0573 if len(r[k])>self._cal_max_events_per_day: 0574 res+=r[k][:self._cal_max_events_per_day] 0575 else: 0576 res+=r[k] 0577 # clip by max events 0578 if len(res)>self._cal_max_events: 0579 res=res[:self._cal_max_events] 0580 return res 0581 0582 def savecalendar(self, dict, merge): 0583 0584 self.log("Sending calendar entries") 0585 0586 cal=self.process_calendar(dict['calendar']) 0587 0588 # testing 0589 if __debug__: 0590 print 'processed calendar: ', len(cal), ' items' 0591 for c in cal: 0592 print c.description,':', c.start 0593 # testing 0594 self.setmode(self.MODEPHONEBOOK) 0595 self.log("Saving calendar entries") 0596 cal_cnt=0 0597 l=self._cal_max_events 0598 for c in cal: 0599 # Save this entry to phone 0600 e=['']*self._cal_num_of_write_fields 0601 0602 # pos 0603 e[self._cal_entry]=`cal_cnt` 0604 0605 # start date time 0606 e[self._cal_start_datetime]=self.encode_timedate(c.start) 0607 0608 # end date time 0609 if self._cal_end_datetime_value is None: 0610 # valid end-datetime 0611 e[self._cal_end_datetime]=self.encode_timedate(c.end) 0612 else: 0613 # no end-datetime, set to start-datetime 0614 e[self._cal_end_datetime]=self._cal_end_datetime_value 0615 0616 # time stamp 0617 e[self._cal_datetime_stamp]=self.get_time_stamp() 0618 0619 # Alarm type 0620 e[self._cal_alarm_type]=c.alarm 0621 0622 # Name, check for bad char & proper length 0623 name=c.description.replace('"', '') 0624 if len(name)>self._cal_max_name_len: 0625 name=name[:self._cal_max_name_len] 0626 e[self._cal_write_name]='"'+name+'"' 0627 0628 # and save it 0629 self.progress(cal_cnt+1, l, "Updating "+name) 0630 if not self.save_calendar_entry(",".join(e)): 0631 self.log("Failed to save item: "+name) 0632 else: 0633 cal_cnt += 1 0634 0635 # delete the rest of the 0636 self.log('Deleting unused entries') 0637 for k in range(cal_cnt, l): 0638 self.progress(k, l, "Deleting entry %d" % k) 0639 self.save_calendar_entry(`k`) 0640 0641 self.setmode(self.MODEMODEM) 0642 0643 return dict 0644 # common methods for individual phones if they can use them w/o changes 0645 def _getmemo(self, result): 0646 self.setmode(self.MODEPHONEBOOK) 0647 m=MemoList(self) 0648 m.read() 0649 m_dict=m.get() 0650 result['memo']=m_dict 0651 self.setmode(self.MODEMODEM) 0652 return m_dict 0653 0654 def _savememo(self, result, merge): 0655 self.setmode(self.MODEPHONEBOOK) 0656 m=MemoList(self) 0657 r=result.get('memo', {}) 0658 m.set(r) 0659 m.write() 0660 self.setmode(self.MODEMODEM) 0661 return r 0662 0663 def _gettodo(self, result): 0664 self.log("Getting todo entries") 0665 self.setmode(self.MODEPHONEBOOK) 0666 td_l=TodoList(self) 0667 td_l.read() 0668 result['todo']=td_l.get() 0669 self.setmode(self.MODEMODEM) 0670 return result 0671 0672 def _savetodo(self, result, merge): 0673 self.log("Saving todo entries") 0674 self.setmode(self.MODEPHONEBOOK) 0675 td_l=TodoList(self, result.get('todo', {})) 0676 td_l.validate() 0677 td_l.write() 0678 self.setmode(self.MODEMODEM) 0679 return result 0680 0681 def _getsms(self, result): 0682 self.log("Getting SMS entries") 0683 self.setmode(self.MODEPHONEBOOK) 0684 sms_l=SMSList(self) 0685 sms_l.read() 0686 result['sms']=sms_l.get() 0687 sms_canned=CannedMsgList(self) 0688 sms_canned.read() 0689 result['canned_msg']=sms_canned.get() 0690 self.setmode(self.MODEMODEM) 0691 return result 0692 def _savesms(self, result, merge): 0693 self.log("Saving SMS Canned Messages") 0694 self.setmode(self.MODEPHONEBOOK) 0695 canned_msg=CannedMsgList(self, result.get('canned_msg', {})) 0696 canned_msg.write() 0697 self.setmode(self.MODEMODEM) 0698 return result 0699 def _getphoneinfo(self, phone_info): 0700 self.log('Getting Phone Info') 0701 self.setmode(self.MODEPHONEBOOK) 0702 for e in phoneinfo.PhoneInfo.standard_keys: 0703 f=getattr(self, 'get_'+e[0]) 0704 setattr(phone_info, e[0], f()) 0705 phone_info.append('Analog/Digital:', self.get_analog_digital()) 0706 self.setmode(self.MODEMODEM) 0707 0708 def _send_at_and_get(self, cmd): 0709 try: 0710 resp=self.comm.sendatcommand(cmd) 0711 return ': '.join(resp[0].split(': ')[1:]) 0712 except: 0713 return None 0714 0715 def is_mode_modem(self): 0716 try: 0717 resp=self.comm.sendatcommand('E0V1') 0718 return True 0719 except: 0720 return False 0721 0722 def get_detect_data(self, r): 0723 # get detection data 0724 r['manufacturer']=self._send_at_and_get('+GMI') 0725 r['model']=self._send_at_and_get('+GMM') 0726 r['firmware_version']=self._send_at_and_get('+GMR') 0727 r['esn']=self._send_at_and_get('+GSN') 0728 0729 @classmethod 0730 def detectphone(_, coms, likely_ports, res, _module, _log): 0731 if not len(likely_ports): 0732 return None 0733 for port in likely_ports: 0734 if not res.has_key(port): 0735 res[port]={ 'mode_modem': None, 'mode_brew': None, 0736 'manufacturer': None, 'model': None, 0737 'firmware_version': None, 'esn': None, 0738 'firmwareresponse': None } 0739 try: 0740 if res[port]['mode_modem']==False or \ 0741 res[port]['model']: 0742 continue 0743 p=Phone(_log, commport.CommConnection(_log, port, timeout=1)) 0744 if p.is_mode_modem(): 0745 res[port]['mode_modem']=True 0746 p.get_detect_data(res[port]) 0747 else: 0748 res[port]['mode_modem']=False 0749 except: 0750 # this port is not available 0751 if __debug__: 0752 raise 0753 0754 #------------------------------------------------------------------------------ 0755 class Profile(com_phone.Profile): 0756 0757 BP_Calendar_Version=3 0758 0759 serialsname='samsung' 0760 0761 usbids=( ( 0x04e8, 0x6601, 1), # Samsung internal USB interface 0762 ) 0763 0764 # which device classes we are. 0765 deviceclasses=("modem", "serial") 0766 0767 _supportedsyncs=() 0768 0769 def __init__(self): 0770 com_phone.Profile.__init__(self) 0771 0772 #------------------------------------------------------------------------------ 0773 class Samsung_Calendar: 0774 _cal_alarm_values={ 0775 '0': -1, '1': 0, '2': 10, '3': 30, '4': 60 } 0776 0777 def __init__(self, calendar_entry, new_date=None): 0778 self._start=self._end=self._alarm=self._desc=None 0779 self._extract_cal_info(calendar_entry, new_date) 0780 0781 def _extract_cal_info(self, cal_entry, new_date): 0782 s=cal_entry.start 0783 if new_date is not None: 0784 s=new_date[:3]+s[3:] 0785 self._start=s 0786 self._end=cal_entry.end 0787 self._desc=cal_entry.description 0788 # approximate the alarm value 0789 self._alarm='0' 0790 alarm=cal_entry.alarm 0791 _keys=self._cal_alarm_values.keys() 0792 _keys.sort() 0793 _keys.reverse() 0794 for k in _keys: 0795 if alarm>=self._cal_alarm_values[k]: 0796 self._alarm=k 0797 break 0798 0799 def __lt__(self, rhs): 0800 return self.start<rhs.start 0801 def __le__(self, rhs): 0802 return self.start<=rhs.start 0803 def __eq__(self, rhs): 0804 return self.start==rhs.start 0805 def __ne__(self, rhs): 0806 return self.start!=rhs.start 0807 def __gt__(self, rhs): 0808 return self.start>rhs.start 0809 def __ge__(self, rhs): 0810 return self.start>=rhs.start 0811 0812 def _get_start(self): 0813 return self._start 0814 start=property(fget=_get_start) 0815 0816 def _get_end(self): 0817 return self._end 0818 end=property(fget=_get_end) 0819 0820 def _get_desc(self): 0821 return self._desc 0822 description=property(fget=_get_desc) 0823 0824 def _get_alarm(self): 0825 return self._alarm 0826 alarm=property(fget=_get_alarm) 0827 0828 #------------------------------------------------------------------------------- 0829 class MemoList(object): 0830 # class constants 0831 _max_num_entries=10 0832 _range_entries=xrange(_max_num_entries) 0833 _max_text_len=60 0834 _max_subject_len=12 0835 _max_num_of_fields=4 0836 _text_index=3 0837 _date_index=1 0838 _max_write_fields=3 0839 _write_entry_index=0 0840 _write_date_index=1 0841 _write_text_index=2 0842 _continuation_char='-' 0843 0844 def __init__(self, phone): 0845 self._phone=phone 0846 self._data={} 0847 0848 def get(self): 0849 return copy.deepcopy(self._data, {}) 0850 0851 def read(self): 0852 self._data={} 0853 text='' 0854 for i in self._range_entries: 0855 try: 0856 self._phone.progress(i, self._max_num_entries, 0857 'Reading Memo Entry: '+str(i)) 0858 s=self._phone.get_memo_entry(i) 0859 if len(s)!=self._max_num_of_fields: 0860 continue 0861 t=s[self._text_index] 0862 if len(t)==self._max_text_len and \ 0863 t[-1]==self._continuation_char: 0864 # contination to the next record 0865 text+=t[:len(t)-1] 0866 continue 0867 # new record 0868 text+=t 0869 m=memo.MemoEntry() 0870 m.text=text 0871 m.set_date_isostr(s[self._date_index]) 0872 self._data[m.id]=m 0873 text='' 0874 except: 0875 if __debug__: raise 0876 0877 def write(self): 0878 keys=self._data.keys() 0879 keys.sort() 0880 count=0 0881 for k in keys: 0882 if count>=self._max_num_entries: 0883 self._phone.log('Max number of memos sent') 0884 break 0885 n=self._data[k] 0886 text=n.text 0887 subj=n.subject 0888 l=min(self._max_subject_len, len(text)) 0889 if subj[:l]!=text[:l]: 0890 text=subj+':'+text 0891 text.replace('"', '') 0892 while len(text) and count<self._max_num_entries: 0893 if len(text)>self._max_text_len: 0894 sub_text=text[:self._max_text_len-1]+self._continuation_char 0895 text=text[self._max_text_len-1:] 0896 else: 0897 sub_text=text 0898 text='' 0899 entry_str=['']*self._max_write_fields 0900 entry_str[self._write_entry_index]=`count` 0901 entry_str[self._write_date_index]=self._phone.get_time_stamp() 0902 entry_str[self._write_text_index]='"'+sub_text+'"' 0903 self._phone.progress(count, self._max_num_entries, 0904 'Writing Memo Entry: '+str(count)) 0905 if self._phone.save_memo_entry(','.join(entry_str)): 0906 self._phone.log('Sent memo %s to the phone'%subj) 0907 count+=1 0908 else: 0909 self._phone.log("Failed to send memo"+subj) 0910 # clear out the rest of the slots 0911 for k in xrange(count, self._max_num_entries): 0912 self._phone.progress(k, self._max_num_entries, 0913 'Deleing Memo Entry: '+str(k)) 0914 self._phone.save_memo_entry(`k`) 0915 0916 def set(self, data): 0917 self._data={} 0918 self._data.update(data) 0919 0920 #------------------------------------------------------------------------------- 0921 class TodoList(object): 0922 _td_max_read_fields=6 0923 _td_max_write_fields=5 0924 _td_max_entries=20 0925 _td_entry=0 0926 _td_priority=1 0927 _td_due_datetime=2 0928 _td_datetime_stamp=3 0929 _td_status=4 0930 _td_subject=5 0931 _td_write_subject=4 0932 _td_max_len_name=32 0933 0934 def __init__(self, phone, data={}): 0935 self._phone=phone 0936 self._data=data 0937 0938 def get(self): 0939 return copy.deepcopy(self._data, {}) 0940 0941 def _extract_fields(self, s): 0942 entry=todo.TodoEntry() 0943 i=int(s[self._td_priority]) 0944 if i: 0945 entry.priority=1 0946 else: 0947 entry.priority=10 0948 entry.due_date=s[self._td_due_datetime][:8] 0949 entry.summary=s[self._td_subject] 0950 return entry 0951 0952 def read(self): 0953 self._data={} 0954 cnt=0 0955 for i in xrange(self._td_max_entries): 0956 s=self._phone.get_todo_entry(i) 0957 if not len(s): 0958 self._phone.progress(i+1, self._td_max_entries, 0959 'Getting blank entry: '+str(i)) 0960 continue 0961 self._phone.progress(i+1, self._td_max_entries, s[self._td_subject]) 0962 e=self._extract_fields(s) 0963 self._data[e.id]=e 0964 0965 def _encode_fields(self, i, entry): 0966 e=['']*self._td_max_write_fields 0967 e[self._td_entry]=`i` 0968 if entry.priority is not None and entry.priority<5: 0969 e[self._td_priority]='1' 0970 else: 0971 e[self._td_priority]='0' 0972 s=entry.due_date 0973 if s is None or not len(s): 0974 s=self._phone.get_time_stamp() 0975 else: 0976 s+='T000000' 0977 e[self._td_due_datetime]=s 0978 e[self._td_datetime_stamp]=self._phone.get_time_stamp() 0979 e[self._td_write_subject]='"'+entry.summary+'"' 0980 return ','.join(e) 0981 0982 def _write_entry(self, i, entry): 0983 return self._phone.save_todo_entry(self._encode_fields(i, entry)) 0984 0985 def validate(self): 0986 for k,n in self._data.items(): 0987 name=n.summary.replace('"', '') 0988 if len(name)>self._td_max_len_name: 0989 name=name[:self._td_max_len_name] 0990 n.summary=name 0991 0992 def write(self): 0993 keys=self._data.keys() 0994 keys.sort() 0995 cnt=0 0996 for k in keys: 0997 n=self._data[k] 0998 if cnt>self._td_max_entries: 0999 break 1000 if self._write_entry(cnt, n): 1001 cnt += 1 1002 else: 1003 self._phone.log('Failed to save todo entry '+str(k)) 1004 self._phone.progress(cnt, self._td_max_entries, 'Saving entry: '+n.summary) 1005 for i in xrange(cnt, self._td_max_entries): 1006 self._phone.progress(i, self._td_max_entries, 'Deleting entry: '+str(i)) 1007 self._phone.save_todo_entry(`i`) 1008 1009 #------------------------------------------------------------------------------- 1010 class SMS_Generic_List(object): 1011 def __init__(self, phone): 1012 self._phone=phone 1013 self._data={} 1014 def get(self): 1015 return self._data.copy() 1016 def read(self): 1017 raise NotImplementedError 1018 1019 #------------------------------------------------------------------------------- 1020 class SMS_Inbox_List(SMS_Generic_List): 1021 _max_entries=100 1022 _valid_range=xrange(_max_entries) 1023 _datetime_index=3 1024 _body_index=4 1025 _callback_index=5 1026 _field_num=6 1027 def __init__(self, phone): 1028 super(SMS_Inbox_List, self).__init__(phone) 1029 def read(self): 1030 for i in self._valid_range: 1031 self._phone.progress(i, self._max_entries, 1032 'Reading SMS Inbox Entry '+str(i)) 1033 s=self._phone.get_sms_inbox(i) 1034 if len(s)==self._field_num: 1035 e=sms.SMSEntry() 1036 e.folder=e.Folder_Inbox 1037 e.datetime=s[self._datetime_index] 1038 e._from, e.subject, txt=self._extract_body(s[self._body_index]) 1039 e.text=unicode(txt, errors='ignore') 1040 e.callback=s[self._callback_index] 1041 self._data[e.id]=e 1042 1043 def _extract_body(self, s): 1044 try: 1045 # extract different components from the main text body 1046 _from=None 1047 l=s.split(' ') 1048 ss=l[0] 1049 if ss.find('@') != -1: 1050 # this the 'from' email address 1051 _from=ss 1052 l=l[1:] 1053 ss=l[0] 1054 _subj=[] 1055 if ss[0]=='(': 1056 while l: 1057 _subj.append(ss) 1058 l=l[1:] 1059 if ss[-1]==')': 1060 break 1061 if l: 1062 ss=l[0] 1063 if l: 1064 return (_from, ' '.join(_subj), ' '.join(l)) 1065 else: 1066 return (_from, '', ' '.join(_subj)) 1067 except: 1068 # something happend, just return the original text 1069 return (None, '', s) 1070 1071 #------------------------------------------------------------------------------- 1072 class SMS_Saved_List(SMS_Generic_List): 1073 _max_entries=20 1074 _valid_range=xrange(_max_entries) 1075 _field_num=5 1076 _datetime_index=1 1077 _from_index=2 1078 _body_index=4 1079 def __init__(self, phone): 1080 super(SMS_Saved_List, self).__init__(phone) 1081 def read(self): 1082 for i in self._valid_range: 1083 self._phone.progress(i, self._max_entries, 1084 'Reading SMS Saved Entry '+str(i)) 1085 s=self._phone.get_sms_saved(i) 1086 if len(s)==self._field_num: 1087 e=sms.SMSEntry() 1088 e.folder=e.Folder_Saved 1089 e.datetime=s[self._datetime_index] 1090 e._from=s[self._from_index] 1091 e.subject, txt=self._extract_body(s[self._body_index]) 1092 e.text=unicode(txt, errors='ignore') 1093 self._data[e.id]=e 1094 def _extract_body(self, s): 1095 # extract different components from the main text body 1096 try: 1097 l=s.split(' ') 1098 ss=l[0] 1099 _subj=[] 1100 if ss[0]=='(': 1101 while l: 1102 _subj.append(ss) 1103 l=l[1:] 1104 if ss[-1]==')': 1105 break 1106 if l: 1107 ss=l[0] 1108 if l: 1109 return (' '.join(_subj), ' '.join(l)) 1110 else: 1111 return ('', ' '.join(_subj)) 1112 except: 1113 return ('', s) 1114 1115 #------------------------------------------------------------------------------- 1116 class SMS_Sent_List(SMS_Generic_List): 1117 _max_entries=100 1118 _valid_range=xrange(_max_entries) 1119 _field_num=5 1120 _datetime_index=1 1121 _to_index=2 1122 _from_index=3 1123 _text_index=4 1124 def __init__(self, phone): 1125 super(SMS_Sent_List, self).__init__(phone) 1126 def read(self): 1127 for i in self._valid_range: 1128 self._phone.progress(i, self._max_entries, 1129 'Reading SMS Sent Entry '+str(i)) 1130 s=self._phone.get_sms_sent(i) 1131 if len(s)==self._field_num: 1132 e=sms.SMSEntry() 1133 e.folder=e.Folder_Sent 1134 e.datetime=s[self._datetime_index] 1135 e._to=s[self._to_index] 1136 e._from=s[self._from_index] 1137 e.text=unicode(s[self._text_index], errors='ignore') 1138 self._data[e.id]=e 1139 1140 #------------------------------------------------------------------------------- 1141 class SMSList(object): 1142 def __init__(self, phone): 1143 self._phone=phone 1144 self._inbox=SMS_Inbox_List(phone) 1145 self._saved=SMS_Saved_List(phone) 1146 self._sent=SMS_Sent_List(phone) 1147 self._data={} 1148 def get(self): 1149 return self._data.copy() 1150 def read(self): 1151 self._inbox.read() 1152 self._data.update(self._inbox.get()) 1153 self._saved.read() 1154 self._data.update(self._saved.get()) 1155 self._sent.read() 1156 self._data.update(self._sent.get()) 1157 1158 #------------------------------------------------------------------------------- 1159 class CannedMsgList(SMS_Generic_List): 1160 _max_entries=20 1161 _valid_range=xrange(_max_entries) 1162 _field_num=4 1163 _text_index=3 1164 _data_key='canned_msg' 1165 _max_write_fields=3 1166 _count_index=0 1167 _timestamp_index=1 1168 _write_text_index=2 1169 def __init__(self, phone, data={}): 1170 super(CannedMsgList, self).__init__(phone) 1171 self._data=data 1172 def read(self): 1173 msg_list=[] 1174 for i in self._valid_range: 1175 self._phone.progress(i, self._max_entries, 1176 'Reading SMS Canned Msg '+str(i)) 1177 s=self._phone.get_canned_msg(i) 1178 if len(s)==self._field_num: 1179 msg_list.append({'text': s[self._text_index], 1180 'type': sms.CannedMsgEntry.user_type }) 1181 self._data=msg_list 1182 def get(self): 1183 return copy.deepcopy(self._data, _nil=[]) 1184 def validate(self): 1185 pass 1186 def write(self): 1187 msg_lst=[x['text'] for x in self._data if x['type']==sms.CannedMsgEntry.user_type] 1188 k=None 1189 for k,n in enumerate(msg_lst): 1190 if k>=self._max_entries: 1191 # enough of that 1192 break 1193 n=n.replace('"', '') 1194 self._phone.progress(k, self._max_entries, 1195 'Writing SMS Canned Msg '+str(k)) 1196 s=`k`+','+self._phone.get_time_stamp()+',"'+n+'"' 1197 if not self._phone.save_canned_msg(s): 1198 self._phone.log('Failed to write SMS Canned Msg entry: '+str(k)) 1199 if k is None: 1200 k=0 1201 else: 1202 k+=1 1203 for i in xrange(k, self._max_entries): 1204 self._phone.progress(i, self._max_entries, 1205 'Deleting SMS Canned Msg entry: '+str(i)) 1206 self._phone.save_canned_msg(`i`) 1207 1208 #------------------------------------------------------------------------------- 1209
Generated by PyXR 0.9.4