conduit r1401 - in trunk: . conduit/modules/PhoneModule scripts



Author: jstowers
Date: Fri Mar 28 12:28:30 2008
New Revision: 1401
URL: http://svn.gnome.org/viewvc/conduit?rev=1401&view=rev

Log:
2008-03-29  John Stowers  <john stowers gmail com>

	* conduit/modules/PhoneModule/Data.py:
	* conduit/modules/PhoneModule/Gammu.py:
	* conduit/modules/PhoneModule/PhoneModule.py:
	* conduit/modules/PhoneModule/ScanThreads.py:
	* conduit/modules/PhoneModule/__init__.py: Add initial mobile phone support
	based on python-bluetooth/bluez and gammu. 
	
	* scripts/ListPhones.py: Utility script to list connected phones



Added:
   trunk/conduit/modules/PhoneModule/
   trunk/conduit/modules/PhoneModule/Data.py
   trunk/conduit/modules/PhoneModule/Gammu.py
   trunk/conduit/modules/PhoneModule/PhoneModule.py
   trunk/conduit/modules/PhoneModule/ScanThreads.py
   trunk/conduit/modules/PhoneModule/__init__.py
   trunk/scripts/ListPhones.py
Modified:
   trunk/ChangeLog

Added: trunk/conduit/modules/PhoneModule/Data.py
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/PhoneModule/Data.py	Fri Mar 28 12:28:30 2008
@@ -0,0 +1,306 @@
+# Generated from http://standards.ieee.org/regauth/oui/oui.txt
+MAC_PREFIXES = {
+    'Sony-Ericsson' : (
+        '00:01:EC',
+        '00:0A:D9',
+        '00:0E:07',
+        '00:0F:DE',
+        '00:12:EE',
+        '00:15:E0',
+        '00:16:20',
+        '00:16:B8',
+        '00:18:13',
+        '00:19:63',
+        '00:1A:75',
+        '00:1B:59',
+        '00:1C:A4',
+        '00:1D:28',
+        '00:1E:45',
+        '00:80:37',
+        ),
+    'Nokia' : (
+        '00:02:EE',
+        '00:0B:E1',
+        '00:0E:ED',
+        '00:0F:BB',
+        '00:10:B3',
+        '00:11:9F',
+        '00:12:62',
+        '00:13:70',
+        '00:13:FD',
+        '00:14:A7',
+        '00:15:2A',
+        '00:15:A0',
+        '00:15:DE',
+        '00:16:4E',
+        '00:16:BC',
+        '00:17:4B',
+        '00:17:B0',
+        '00:18:0F',
+        '00:18:42',
+        '00:18:8D',
+        '00:18:C5',
+        '00:19:2D',
+        '00:19:4F',
+        '00:19:79',
+        '00:19:B7',
+        '00:1A:16',
+        '00:1A:89',
+        '00:1A:DC',
+        '00:1B:33',
+        '00:1B:AF',
+        '00:1B:EE',
+        '00:1C:35',
+        '00:1C:9A',
+        '00:1C:D4',
+        '00:1C:D6',
+        '00:1D:3B',
+        '00:1D:6E',
+        '00:1D:98',
+        '00:1D:E9',
+        '00:1D:FD',
+        '00:1E:3A',
+        '00:1E:3B',
+        '00:1E:A3',
+        '00:1E:A4',
+        '00:40:43',
+        '00:A0:8E',
+        '00:E0:03',
+        ),
+    'Siemens' : (
+        '00:01:E3',
+        '00:05:19',
+        '00:0B:23',
+        '00:0B:A3',
+        '00:0D:41',
+        '00:0E:8C',
+        '00:0F:BB',
+        '00:11:06',
+        '00:11:33',
+        '00:13:A3',
+        '00:18:D1',
+        '00:19:28',
+        '00:19:99',
+        '00:1A:D0',
+        '00:1A:E8',
+        '00:1B:1B',
+        '00:1C:06',
+        '00:30:05',
+        '00:50:07',
+        '00:90:40',
+        '00:C0:E4',
+        '08:00:06',
+        ),
+    'Samsung' : (
+        '00:00:F0',
+        '00:02:78',
+        '00:09:18',
+        '00:0D:AE',
+        '00:0D:E5',
+        '00:0F:73',
+        '00:12:47',
+        '00:12:FB',
+        '00:13:77',
+        '00:15:99',
+        '00:15:B9',
+        '00:16:32',
+        '00:16:6B',
+        '00:16:6C',
+        '00:16:DB',
+        '00:17:C9',
+        '00:17:D5',
+        '00:18:AF',
+        '00:1A:8A',
+        '00:1B:98',
+        '00:1C:43',
+        '00:1D:25',
+        '00:1D:F6',
+        '00:1E:7D',
+        '00:E0:64',
+        ),
+    'LG' : (
+        '00:05:C9',
+        '00:0B:29',
+        '00:12:56',
+        '00:14:80',
+        '00:19:A1',
+        '00:1C:62',
+        '00:1E:75',
+        '00:1E:B2',
+        '00:50:CE',
+        '00:E0:91',
+        ),
+    'BenQ' : (
+        '00:03:9D',
+        '00:17:CA',
+        ),
+    'Motorola' : (
+        '00:01:AF',
+        '00:04:56',
+        '00:04:BD',
+        '00:08:0E',
+        '00:0A:28',
+        '00:0B:06',
+        '00:0C:E5',
+        '00:0E:5C',
+        '00:0E:C7',
+        '00:0F:9F',
+        '00:11:1A',
+        '00:11:80',
+        '00:11:AE',
+        '00:12:25',
+        '00:12:8A',
+        '00:12:C9',
+        '00:13:71',
+        '00:14:04',
+        '00:14:9A',
+        '00:14:E8',
+        '00:15:2F',
+        '00:15:9A',
+        '00:15:A8',
+        '00:16:26',
+        '00:16:75',
+        '00:16:B5',
+        '00:17:00',
+        '00:17:84',
+        '00:17:E2',
+        '00:17:EE',
+        '00:18:A4',
+        '00:18:C0',
+        '00:19:2C',
+        '00:19:5E',
+        '00:19:A6',
+        '00:19:C0',
+        '00:1A:1B',
+        '00:1A:66',
+        '00:1A:77',
+        '00:1A:AD',
+        '00:1A:DB',
+        '00:1A:DE',
+        '00:1B:52',
+        '00:1B:DD',
+        '00:1C:11',
+        '00:1C:12',
+        '00:1C:C1',
+        '00:1C:FB',
+        '00:1D:6B',
+        '00:1D:BE',
+        '00:1E:46',
+        '00:1E:5A',
+        '00:1E:8D',
+        '00:20:40',
+        '00:20:75',
+        '00:A0:BF',
+        '00:C0:F9',
+        '00:E0:0C',
+        ),
+    'Alcatel' : (
+        '00:07:72',
+        '00:08:9A',
+        '00:0E:86',
+        '00:0F:62',
+        '00:11:3F',
+        '00:11:8B',
+        '00:15:3F',
+        '00:16:4D',
+        '00:17:CC',
+        '00:19:8F',
+        '00:1A:F0',
+        '00:1C:8E',
+        '00:1D:4C',
+        '00:20:32',
+        '00:20:60',
+        '00:20:DA',
+        '00:80:21',
+        '00:80:39',
+        '00:80:9F',
+        '00:A0:81',
+        '00:C0:BE',
+        '00:D0:95',
+        '00:D0:F6',
+        '00:E0:B1',
+        '00:E0:DA',
+        ),
+    'Sharp' :   (
+        '00:17:5C',
+        '00:1C:EE',
+        '08:00:1F'
+        )
+}
+
+GAMMU_CONN_BLUETOOTH_ALL = (
+    'bluephonet',
+    'bluefbus',
+    'bluerfgnapbus',
+    'blueat',
+    'blueobex',
+    'bluerfobex',
+    'bluerfphonet',
+    'bluerffbus',
+    'bluerfat',
+)
+GAMMU_CONN_BLUETOOTH_NOKIA = (
+    'bluephonet',
+    'bluefbus',
+    'bluerfgnapbus',
+    'bluerfphonet',
+    'bluerffbus',
+    'blueat',
+    'bluerfat',
+    'blueobex',
+    'bluerfobex',
+)
+GAMMU_CONN_BLUETOOTH_STANDARD = (
+    'blueat',
+    'blueobex',
+    'bluerfgnapbus',
+)
+GAMMU_CONN_BLUETOOTH = {
+    'Sony-Ericsson' :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'Siemens'       :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'BenQ'          :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'Samsung'       :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'LG'            :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'Motorola'      :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'Nokia'         :   GAMMU_CONN_BLUETOOTH_NOKIA,
+    'Alcatel'       :   GAMMU_CONN_BLUETOOTH_STANDARD,
+    'Sharp'         :   GAMMU_CONN_BLUETOOTH_STANDARD,
+}
+
+GAMMU_CONN_CABLE = (
+    'at19200',
+    'at115200',
+    'fbusdlr3',
+    'fbus',
+    'mbus',
+    'fbuspl2303',
+    'phonetblue',
+    'fbusblue',
+)
+GAMMU_CONN_IRDA = (
+    'irdaphonet',
+    'at19200',
+)
+GAMMU_CONN_BLUERF = (
+    'at19200',
+)
+GAMMU_CONN_DEVICES = (
+    (('/dev/ttyS%d' % i for i in range(3)),      GAMMU_CONN_CABLE    ),
+    (('/dev/ttyUSB%d' % i for i in range(3)),    GAMMU_CONN_CABLE    ),
+    (('/dev/ttyACM%d' % i for i in range(3)),    GAMMU_CONN_CABLE    ),
+    (('/dev/rfrcomm%d' % i for i in range(1)),   GAMMU_CONN_BLUERF   ),
+    (('/dev/ircomm%d' % i for i in range(1)),    GAMMU_CONN_IRDA     ),
+    (('/dev/usb/tts/%d' % i for i in range(3)),  GAMMU_CONN_CABLE    ))
+
+def get_vendor_from_bluetooth_address(address):
+    for vendor in MAC_PREFIXES.keys():
+        if address[:8].upper() in MAC_PREFIXES[vendor]:
+            return vendor
+    return None
+    
+def get_connections_from_bluetooth_address(address):
+    vendor = get_vendor_from_bluetooth_address(address)
+    if vendor:
+        return GAMMU_CONN_BLUETOOTH[vendor]
+    return GAMMU_CONN_BLUETOOTH_ALL
+

Added: trunk/conduit/modules/PhoneModule/Gammu.py
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/PhoneModule/Gammu.py	Fri Mar 28 12:28:30 2008
@@ -0,0 +1,355 @@
+#FIXME: Proper license and attribution
+#Most code adapted from gammu (GPL2) and phonetooth (GPL2)
+
+import os
+import bluetooth
+import logging
+log = logging.getLogger("modules.Phone")
+
+import conduit.dataproviders.DataProvider as DataProvider
+import conduit.utils as Utils
+import ScanThreads
+import Data
+
+try:
+    import gammu
+    log.info("Module Information: %s" % Utils.get_module_information(gammu, "__version__"))
+    GAMMU_SUPPORTED = True
+except ImportError:
+    log.info("Gammu based phone support disabled")
+    GAMMU_SUPPORTED = False
+
+class GammuPhone:
+    """
+    Encapsulates the basic connection to a phone using gammu.
+    """
+    def __init__(self, address, connection, model=''):
+        """
+        Attempts to connect to the phone at address and connection and
+        get all available phone informatin, like the model, manufacturer, etc.
+        """
+        self.address = address
+        self.connection = connection
+        self.model = model
+        self.isKnownToGammu = False
+        self.info = {}
+        
+        self.sm = gammu.StateMachine()
+        self.sm.SetConfig(0, {
+            'StartInfo'             :   'no',
+            'UseGlobalDebugFile'    :   1,
+            'DebugFile'             :   '',
+            'SyncTime'              :   'no',
+            'Connection'            :   connection,
+            'LockDevice'            :   'no',
+            'DebugLevel'            :   'nothing',
+            'Device'                :   address,
+            'Localize'              :   None,
+            'Model'                 :   model}
+        )
+        #dont catch errors thrown here....
+        self.sm.Init()
+        #if we got here, then we managed a rudimentary connection
+        self.info['connection'] = connection
+        
+        #catch non fatal errors because some phones might not
+        #support these fields
+        try:
+            self.info['manufacturer'] = self.sm.GetManufacturer()
+        except gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED: 
+            self.info['manufacturer'] = "Unknown"
+            
+        model = "Unknown"
+        try:
+            m = self.sm.GetModel()
+            if m[0] == '' or m[0] == 'unknown':
+                model = m[1]
+            else:
+                model = m[0]
+                self.isKnownToGammu = True
+        except gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED:
+            model = "Unknown"
+        self.info['model'] = model
+        
+        try:
+            self.info['firmware'] = self.sm.GetFirmware()[0]
+        except gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED:    
+            self.info['firmware'] = "Unknown"
+        
+class Bluetooth(ScanThreads.ScanThread):
+    def __init__(self, foundCallback):
+        ScanThreads.ScanThread.__init__(self)
+        self.foundCallback = foundCallback
+        self.discovery = ScanThreads.DeviceDiscovererFilter(self)
+        
+        #keep a track of what phones we have seen, so we dont need to find
+        #their services over and over again
+        # address : name
+        self._phones = {}
+        
+    def lookup_model_information(self, address):
+        #try all bluetooth connections
+        for connection in Data.get_connections_from_bluetooth_address(address):
+            log.info("Connecting to %s Using: %s" % (address,connection))
+            try:
+                phone = GammuPhone(address, connection)
+                phone.sm.Terminate()
+                log.info("\t ---> OK (%s)" % ', '.join(phone.info.values()))
+                return phone.info
+                
+            except gammu.GSMError, val:
+                log.warn("\t ---> Failed")
+
+        #connection = None means we tried to connect and failed
+        return {
+            'connection'    :   None
+        }
+        
+    def run(self):
+        while self.is_cancelled() == False:
+            log.info("Beginning Bluetooth Scan")
+            try:
+                self.discovery.find_devices()
+                self.discovery.process_inquiry()
+                for address,info in self._found.items():
+                    if address in os.environ.get('PHONE_BLACKLIST',"").split(","):
+                        log.info("Skipping %s, blacklisted" % address)
+                        continue
+                    #Use gammu to lookup the model information. This also tests
+                    #if gammu can actually connect to the phone
+                    if not info.has_key('connection'):
+                        #gammu info
+                        info.update(
+                                self.lookup_model_information(address)
+                                )
+                        #services, which may be useful for things like obexftp,
+                        #and other dataproviders in the PhoneModule
+                        services = bluetooth.find_service(address=address)
+                        log.info("Supported services: %s" % ", ".join([i['name'] for i in services]))
+                        info['services'] = services
+                        
+                        self.foundCallback(address,info['name'],"bluetooth",info)
+            except bluetooth.BluetoothError:
+                log.warn("Error discovering services")
+
+            self.pause_scanning()
+
+    def cancel(self):
+        ScanThreads.ScanThread.cancel(self)
+        self.discovery.cancel_inquiry()
+
+class Contact:
+    def __init__(self, name, phoneNumber):
+        self.name = name
+        self.phoneNumber = phoneNumber
+        
+    def __str__(self):
+        return self.name + " - " + self.phoneNumber
+
+class GammuDataProvider(DataProvider.DataSource):
+
+    _name_ = "Contacts"
+    _module_type_ = "source"
+    _in_type_ = "contact"
+    _out_type_ = "contact"
+    
+    #FIXME: What does this mean??
+    MAX_EMPTY_GUESS = 5
+    MAX_EMPTY_KNOWN = 5
+
+    def __init__(self, address, connection):
+        DataProvider.DataSource.__init__(self)
+        self.address = address
+        self.connection = connection
+        #FIXME: Stupid sharp phone obex over bluetooth is broken, so
+        #default to at
+        self.model = "at"
+        self.phone = None
+
+    def get_UID(self):
+        return self.address
+        
+    def _guess_num_items(self):
+        return 200
+
+    def _get_first_entry(self):
+        '''
+        Initiates get next sequence.
+
+        Should be implemented in subclases.
+        '''
+        raise NotImplementedError
+
+    def _get_next_entry(self, location):
+        '''
+        Gets next entry.
+
+        Should be implemented in subclases.
+        '''
+        raise NotImplementedError
+
+    def _get_entry(self, location):
+        '''
+        Gets entry.
+
+        Should be implemented in subclases.
+        '''
+        raise NotImplementedError
+
+    def _get_num_items(self):
+        '''
+        Gets status of entries.
+
+        Should be implemented in subclases.
+        '''
+        raise NotImplementedError
+
+    def _parse_entry(self):
+        '''
+        Parses entry.
+
+        Should be implemented in subclases.
+        '''
+        raise NotImplementedError
+
+    def Send(self):
+        '''
+        Sends entries to parent.
+
+        Should be implemented in subclases.
+        '''
+        raise NotImplementedError
+
+    def Run(self):
+        '''
+        UNFINISHED PORT OF WAMMU's SUBCLASSABLE APPROACH FOR GETTING DATA
+        '''
+        guess = False
+        try:
+            total = self._get_num_items()
+        except gammu.GSMError, val:
+            guess = True
+            total = self._guess_num_items()
+
+        remain = total
+
+        data = []
+
+        try:
+            start = True
+            while remain > 0:
+                #if self.canceled:
+                #    self.Canceled()
+                #    return
+                try:
+                    if start:
+                        value = self._get_first_entry()
+                        start = False
+                    else:
+                        try:
+                            loc = value['Location']
+                        except TypeError:
+                            loc = value[0]['Location']
+                        value = self._get_next_entry(loc)
+                except gammu.ERR_CORRUPTED:
+                    log.warn('While reading, entry on location %d seems to be corrupted, ignoring it!' % loc)
+                    continue
+                except gammu.ERR_EMPTY:
+                    break
+
+                self._parse_entry(value)
+                if type(value) == list:
+                    for i in range(len(value)):
+                        value[i]['Synced'] = True
+                else:
+                    value['Synced'] = True
+                data.append(value)
+                remain = remain - 1
+        except (gammu.ERR_NOTSUPPORTED, gammu.ERR_NOTIMPLEMENTED):
+            location = 1
+            empty = 0
+            while remain > 0:
+                #if self.canceled:
+                #    self.Canceled()
+                #    return
+                try:
+                    value = self._get_entry(location)
+                    self._parse_entry(value)
+                    if type(value) == list:
+                        for i in range(len(value)):
+                            value[i]['Synced'] = True
+                    else:
+                        value['Synced'] = True
+                    data.append(value)
+                    remain = remain - 1
+                    # If we didn't know count and reached end, try some more entries
+                    if remain == 0 and guess:
+                        remain = 20
+                        total = total + 20
+                    empty = 0
+                except gammu.ERR_EMPTY, val:
+                    empty = empty + 1
+                    # If we didn't know count and saw many empty entries, stop right now
+                    if empty >= self.MAX_EMPTY_GUESS and guess:
+                        break
+                    # If we didn't read anything for long time, we bail out (workaround bad count reported by phone)
+                    if empty >= self.MAX_EMPTY_KNOWN and remain < 10:
+                        self.ShowError(val[0])
+                        remain = 0
+                except gammu.ERR_CORRUPTED:
+                    log.warn('While reading, entry on location %d seems to be corrupted, ignoring it!' % location)
+                    continue
+                except gammu.GSMError, val:
+                    log.critical(val[0])
+                    return
+                location = location + 1
+        except gammu.ERR_INVALIDLOCATION, val:
+            # if we reached end with guess, it is okay
+            if not guess:
+                log.critical(val[0])
+                return
+        except gammu.GSMError, val:
+            log.critical(val[0])
+            return
+
+        #self.Send(data)
+        
+    def refresh(self):
+        if not self.phone:
+            self.phone = GammuPhone(self.address, self.connection, self.model)
+        log.debug("Connected to phone: %s" % (self.phone.info['model']))
+        self.get_all()
+            
+    def get_all(self):
+        #phone = ME, sim = SM
+        location = "ME"
+        contactList = []
+        
+        status = self.phone.sm.GetMemoryStatus(Type=location)
+        remain = status['Used']
+        log.debug("%s contacts on phone" % remain)
+        
+        start = True
+        while remain > 0:
+            if start:
+                entry = self.phone.sm.GetNextMemory(Start=True, Type=location)
+                start = False
+            else:
+                entry = self.phone.sm.GetNextMemory(Location=entry['Location'], Type=location)
+            
+            remain = remain - 1
+            
+            contact = Contact('', '')
+            for v in entry['Entries']:
+                if v['Type'] == 'Number_General':
+                    contact.phoneNumber = v['Value']
+                elif v['Type'] == 'Text_Name':
+                    contact.name = v['Value']
+                
+            if len(contact.name) > 0 and len(contact.phoneNumber) > 0:
+                print "*"*20,contact
+                #contactList.append(contact)
+
+        return contactList
+
+

Added: trunk/conduit/modules/PhoneModule/PhoneModule.py
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/PhoneModule/PhoneModule.py	Fri Mar 28 12:28:30 2008
@@ -0,0 +1,130 @@
+import logging
+log = logging.getLogger("modules.Phone")
+
+import conduit.dataproviders.File as FileDataProvider
+import conduit.dataproviders.DataProvider as DataProvider
+import conduit.dataproviders.DataProviderCategory as DataProviderCategory
+import conduit.utils as Utils
+import conduit.Exceptions as Exceptions
+
+try:
+    import bluetooth
+    MODULES = {
+    "PhoneFactory" : { "type": "dataprovider-factory" },
+    }
+except ImportError:
+    MODULES = {}
+    log.info("Phone support disabled (bluez/python-bluetooth not installed)")
+
+Utils.dataprovider_add_dir_to_path(__file__, "")
+import Gammu
+
+class PhoneFactory(DataProvider.DataProviderFactory):
+    """ 
+    Looks for phones connected via bluetooth
+    """
+    def __init__(self, **kwargs):
+        DataProvider.DataProviderFactory.__init__(self, **kwargs)
+        self._cats = {}
+        self._phones = {}
+
+        #Scan multiple interfaces at once
+        self.threads = []
+        if Gammu.GAMMU_SUPPORTED:
+            self.threads.append(
+                Gammu.Bluetooth(self._found_phone_callback)
+                #Gammu.Cable
+                )
+        #else:
+        #   phonetooth based scan
+
+    def _found_phone_callback(self, address, name, driver, info):
+        #Get/create the named phone category
+        if name not in self._cats:
+            self._cats[name] = DataProviderCategory.DataProviderCategory(
+                                    name,
+                                    "phone",
+                                    address)    
+        category = self._cats[name]
+
+        #create the klass for controlling the phone
+        klass = None
+        if driver == "test":
+            self.emit_added(
+                        klass=Test,
+                        initargs=(address,),
+                        category=category
+                        ) 
+        elif driver == "bluetooth":
+            #check it supports obex file transfer class for file dps
+            done = False
+            for i in ObexFileDataProvider.SUPPORTED_BLUETOOTH_CLASSES:
+                if done: break
+                for service in info.get('services',()):
+                    if i in service['service-classes']:
+                        self.emit_added(
+                                klass=ObexFileDataProvider,
+                                initargs=(address,),
+                                category=category
+                                )
+                        done = True
+                        break             
+
+            #check that gammu found a working connection to the phone
+            if info.get('connection',None):
+                if Gammu.GAMMU_SUPPORTED:
+                    self.emit_added(
+                            klass=Gammu.GammuDataProvider,
+                            initargs=(address,info['connection']),
+                            category=category
+                            )
+                #else phonetooth based contacts
+                #
+
+        else:
+            log.warn("No driver supports %s %s" % (driver,address))
+
+    def probe(self):
+        log.info("Starting Scan Threads")
+        for t in self.threads:
+            t.start()
+
+    def quit(self):
+        log.info("Stopping Scan Threads")
+        for t in self.threads:
+            t.cancel()
+
+class ObexFileDataProvider(FileDataProvider.FolderTwoWay):
+
+    _name_ = "Pictures"
+
+    #FIXME: Does gnomevfs-obexftp support obexpush also?
+    SUPPORTED_BLUETOOTH_CLASSES = (
+        bluetooth.OBEX_FILETRANS_CLASS,
+    )
+
+    def __init__(self, address, *args):
+        FileDataProvider.FolderTwoWay.__init__(
+                            self,
+                            folder= "obex://[%s]" % address,
+                            folderGroupName="Test",
+                            includeHidden=False,
+                            compareIgnoreMtime=False
+                            )
+        self.address = address
+        #FIXME: In the land of GIO, I think I need to gio-mount this
+        #location before I can do anything with it...
+
+    def get_UID(self):
+        return self.address
+
+class Test(DataProvider.DataSource):
+    _name_ = "Test Phone"
+    _description_ = "Test Phone"
+    _module_type_ = "source"
+    def __init__(self, address, *args):
+        DataProvider.DataSource.__init__(self)
+
+    def get_UID(self):
+        return ""
+

Added: trunk/conduit/modules/PhoneModule/ScanThreads.py
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/PhoneModule/ScanThreads.py	Fri Mar 28 12:28:30 2008
@@ -0,0 +1,63 @@
+import logging
+log = logging.getLogger("modules.Phone")
+
+import thread
+
+import threading
+import bluetooth
+import time
+
+import conduit
+
+UNKNOWN_VALUE = "Unknown"
+
+class ScanThread(threading.Thread):
+    SLEEP_TIME = 20
+    SLEEP = 0.1
+    def __init__(self):
+        threading.Thread.__init__(self)
+        self._found = {}
+        self._cancelled = False
+        self._foundLock = threading.Lock()
+
+    def found_phone(self, address, name):
+        #locked because, AIUI, the DeviceDiscover callsback anytime,
+        #from another thread
+        self._foundLock.acquire()
+        if address not in self._found:
+            log.info("Thread %s Found Phone: %s" % (thread.get_ident(),address))
+            self._found[address] = {"name":name}
+        self._foundLock.release()
+
+    def pause_scanning(self):
+        i = 0
+        while ( i < (self.SLEEP_TIME/self.SLEEP) ) and ( self.is_cancelled() == False ):
+            time.sleep(self.SLEEP)
+            i += 1
+
+    def is_cancelled(self):
+        return conduit.GLOBALS.cancelled or self._cancelled
+
+    def cancel(self):
+        self._cancelled = True
+
+class DeviceDiscovererFilter(bluetooth.DeviceDiscoverer):
+    def __init__(self, parent):
+        bluetooth.DeviceDiscoverer.__init__(self)
+        self.parent = parent
+
+    def device_discovered(self, address, device_class, name):
+        '''
+        Called when device is iscovered, checks device is phone
+        '''
+        log.info("Bluetooth Device Discovered: %s %s" % (name,address))
+        major_class = ( device_class & 0xf00 ) >> 8
+        # We want only devices with phone class
+        # See https://www.bluetooth.org/apps/content/?doc_id=49706
+        if major_class == 2:
+            self.parent.found_phone(address, name)
+
+    def inquiry_complete(self):
+        log.debug("Bluetooth Search Complete")
+
+

Added: trunk/conduit/modules/PhoneModule/__init__.py
==============================================================================
--- (empty file)
+++ trunk/conduit/modules/PhoneModule/__init__.py	Fri Mar 28 12:28:30 2008
@@ -0,0 +1 @@
+

Added: trunk/scripts/ListPhones.py
==============================================================================
--- (empty file)
+++ trunk/scripts/ListPhones.py	Fri Mar 28 12:28:30 2008
@@ -0,0 +1,54 @@
+import sys
+import os.path
+import bluetooth
+import gammu
+
+# make sure we have conduit folder in path!
+my_path = os.path.dirname(__file__)
+base_path = os.path.abspath(os.path.join(my_path, '..'))
+sys.path.insert(0, base_path)
+
+import conduit.Logging as Logging
+import conduit.modules.PhoneModule.Data as Data
+import conduit.modules.PhoneModule.ScanThreads as ScanThreads
+import conduit.modules.PhoneModule.Gammu as Gammu
+
+import logging
+log = logging.getLogger("ListPhones")
+
+class FoundPhones:
+    def __init__(self):
+        self._found = {}
+
+    def found_phone(self, address, name):
+        log.info("Phone Manufacturer: %s" % Data.get_vendor_from_bluetooth_address(address))
+        if address not in self._found:
+            self._found[address] = name
+
+    def lookup_model_information(self):
+        for address,name in self._found.items():
+            if address in os.environ.get('PHONE_BLACKLIST',"").split(","):
+                log.info("Skipping %s, blacklisted" % address)
+                continue
+            #try all bluetooth connections
+            for connection in Data.get_connections_from_bluetooth_address(address):
+                log.info("Connecting to %s Using: %s" % (name,connection))
+                try:
+                    phone = Gammu.GammuPhone(address, connection)
+                    phone.sm.Terminate()
+                    log.info("\t ---> OK (%s)" % ', '.join(phone.info.values()))
+                    break
+                except gammu.GSMError, val:
+                    log.info("\t ---> Failed")    
+    
+#discover all the phones
+log.info("Searching for Phones")
+found = FoundPhones()
+discoverer = ScanThreads.DeviceDiscovererFilter(found)
+discoverer.find_devices()
+discoverer.process_inquiry()
+
+#use gammu to get their manufacturer
+found.lookup_model_information()
+
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]