PageRenderTime 57ms CodeModel.GetById 18ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/bluetooth/bluez.py

http://pybluez.googlecode.com/
Python | 633 lines | 580 code | 11 blank | 42 comment | 18 complexity | 4336ed256b8c7c3b30fbb05bc2d29af1 MD5 | raw file
  1import sys
  2import struct
  3import binascii
  4
  5if sys.version < '3':
  6    from .btcommon import *
  7    import _bluetooth as _bt
  8else:
  9    from bluetooth.btcommon import *
 10    import bluetooth._bluetooth as _bt
 11import array
 12import fcntl
 13_constants = [ 'HCI', 'RFCOMM', 'L2CAP', 'SCO', 'SOL_L2CAP', 'SOL_RFCOMM',\
 14    'L2CAP_OPTIONS' ]
 15for _c in _constants:
 16    command_ = "{C} = _bt.{C1}".format(C=_c, C1=_c)
 17    exec(command_)
 18del _constants
 19
 20# ============== SDP service registration and unregistration ============
 21
 22def discover_devices (duration=8, flush_cache=True, lookup_names=False, lookup_class=False):
 23    sock = _gethcisock ()
 24    try:
 25        results = _bt.hci_inquiry (sock, duration=duration, flush_cache=True, lookup_class=lookup_class)
 26    except _bt.error:
 27        sock.close ()
 28        raise BluetoothError ("error communicating with local "
 29        "bluetooth adapter")
 30
 31    if lookup_names:
 32        pairs = []
 33        for item in results:
 34            if lookup_class:
 35               addr, dev_class = item
 36            else:
 37               addr = item
 38            timeoutms = int (10 * 1000)
 39            try: 
 40                name = _bt.hci_read_remote_name (sock, addr, timeoutms)
 41            except _bt.error as e:
 42                # name lookup failed.  either a timeout, or I/O error
 43                continue
 44            pairs.append ((addr, name, dev_class) if lookup_class else (addr, name))
 45        sock.close ()
 46        return pairs
 47    else:
 48        sock.close ()
 49        return results
 50
 51def lookup_name (address, timeout=10):
 52    if not is_valid_address (address): 
 53        raise BluetoothError ("%s is not a valid Bluetooth address" % address)
 54
 55    sock = _gethcisock ()
 56    timeoutms = int (timeout * 1000)
 57    try: 
 58        name = _bt.hci_read_remote_name (sock, address, timeoutms)
 59    except _bt.error as e:
 60        # name lookup failed.  either a timeout, or I/O error
 61        name = None
 62    sock.close ()
 63    return name
 64
 65def set_packet_timeout (address, timeout):
 66    """
 67    Adjusts the ACL flush timeout for the ACL connection to the specified
 68    device.  This means that all L2CAP and RFCOMM data being sent to that
 69    device will be dropped if not acknowledged in timeout milliseconds (maximum
 70    1280).  A timeout of 0 means to never drop packets.
 71
 72    Since this affects all Bluetooth connections to that device, and not just
 73    those initiated by this process or PyBluez, a call to this method requires
 74    superuser privileges.
 75
 76    You must have an active connection to the specified device before invoking
 77    this method
 78    """
 79    n = round (timeout / 0.625)
 80    _write_flush_timeout (address, n)
 81
 82def get_l2cap_options (sock):
 83    """get_l2cap_options (sock, mtu)
 84
 85    Gets L2CAP options for the specified L2CAP socket.
 86    Options are: omtu, imtu, flush_to, mode, fcs, max_tx, txwin_size.
 87    """
 88    # TODO this should be in the C module, because it depends
 89    # directly on struct l2cap_options layout.
 90    s = sock.getsockopt (SOL_L2CAP, L2CAP_OPTIONS, 12)
 91    options = list( struct.unpack ("HHHBBBH", s))
 92    return options
 93
 94def set_l2cap_options (sock, options):
 95    """set_l2cap_options (sock, options)
 96
 97    Sets L2CAP options for the specified L2CAP socket.
 98    The option list must be in the same format supplied by
 99    get_l2cap_options().
100    """
101    # TODO this should be in the C module, because it depends
102    # directly on struct l2cap_options layout.
103    s = struct.pack ("HHHBBBH", *options)
104    sock.setsockopt (SOL_L2CAP, L2CAP_OPTIONS, s)
105
106def set_l2cap_mtu (sock, mtu):
107    """set_l2cap_mtu (sock, mtu)
108
109    Adjusts the MTU for the specified L2CAP socket.  This method needs to be
110    invoked on both sides of the connection for it to work!  The default mtu
111    that all L2CAP connections start with is 672 bytes.
112
113    mtu must be between 48 and 65535, inclusive.
114    """
115    options = get_l2cap_options (sock)
116    options[0] = options[1] = mtu
117    set_l2cap_options (sock, options)
118
119def _get_available_port (protocol):
120    """
121    deprecated.  bind to PORT_ANY instead.
122    """
123    if protocol == RFCOMM:
124        for channel in range (1,31):
125            s = BluetoothSocket (RFCOMM)
126            try:
127                s.bind ( ("", channel))
128                s.close ()
129                return channel
130            except:
131                s.close ()
132    elif protocol == L2CAP:
133        for psm in range (0x1001,0x8000,2):
134            s = BluetoothSocket (L2CAP)
135            try:
136                s.bind ( ("", psm))
137                s.close ()
138                return psm
139            except:
140                s.close ()
141    else:
142        raise ValueError ("protocol must either RFCOMM or L2CAP")
143
144class BluetoothSocket:
145    __doc__ = _bt.btsocket.__doc__
146
147    def __init__ (self, proto = RFCOMM, _sock=None):
148        if _sock is None:
149            _sock = _bt.btsocket (proto)
150        self._sock = _sock
151        self._proto = proto
152
153    def dup (self):
154        """dup () -> socket object
155
156        Return a new socket object connected to the same system resource."""
157        return BluetoothSocket (proto=self._proto, _sock=self._sock)
158
159    def accept (self):
160        try:
161            client, addr = self._sock.accept ()
162        except _bt.error as e:
163            raise BluetoothError (str (e))
164        newsock = BluetoothSocket (self._proto, client)
165        return (newsock, addr)
166    accept.__doc__ = _bt.btsocket.accept.__doc__
167
168    def bind (self, addrport):
169        if self._proto == RFCOMM or self._proto == L2CAP:
170            addr, port = addrport
171            if port == 0: addrport = (addr, _get_available_port (self._proto))
172        return self._sock.bind (addrport)
173
174    def get_l2cap_options(self):
175        """get_l2cap_options (sock, mtu)
176
177        Gets L2CAP options for the specified L2CAP socket.
178        Options are: omtu, imtu, flush_to, mode, fcs, max_tx, txwin_size.
179        """
180        return get_l2cap_options(self)
181
182    def set_l2cap_options(self, options):
183        """set_l2cap_options (sock, options)
184
185        Sets L2CAP options for the specified L2CAP socket.
186        The option list must be in the same format supplied by
187        get_l2cap_options().
188        """
189        return set_l2cap_options(self, options)
190
191    def set_l2cap_mtu(self, mtu):
192        """set_l2cap_mtu (sock, mtu)
193
194        Adjusts the MTU for the specified L2CAP socket.  This method needs to be
195        invoked on both sides of the connection for it to work!  The default mtu
196        that all L2CAP connections start with is 672 bytes.
197
198        mtu must be between 48 and 65535, inclusive.
199        """
200        return set_l2cap_mtu(self, mtu)
201
202    # import methods from the wraapped socket object
203    _s = ("""def %s (self, *args, **kwargs): 
204    try: 
205        return self._sock.%s (*args, **kwargs)
206    except _bt.error as e:
207        raise BluetoothError (str (e))
208    %s.__doc__ = _bt.btsocket.%s.__doc__\n""")
209    for _m in ( 'connect', 'connect_ex', 'close',
210        'fileno', 'getpeername', 'getsockname', 'gettimeout',
211        'getsockopt', 'listen', 'makefile', 'recv', 'recvfrom', 'sendall',
212        'send', 'sendto', 'setblocking', 'setsockopt', 'settimeout', 
213        'shutdown', 'setl2capsecurity'):
214        exec( _s % (_m, _m, _m, _m))
215    del _m, _s
216
217def advertise_service (sock, name, service_id = "", service_classes = [], \
218        profiles = [], provider = "", description = "", protocols = []):
219    if service_id != "" and not is_valid_uuid (service_id):
220        raise ValueError ("invalid UUID specified for service_id")
221    for uuid in service_classes:
222        if not is_valid_uuid (uuid):
223            raise ValueError ("invalid UUID specified in service_classes")
224    for uuid, version in profiles:
225        if not is_valid_uuid (uuid) or  version < 0 or  version > 0xFFFF:
226            raise ValueError ("Invalid Profile Descriptor")
227    for uuid in protocols:
228        if not is_valid_uuid (uuid):
229            raise ValueError ("invalid UUID specified in protocols")        
230
231    try:
232        _bt.sdp_advertise_service (sock._sock, name, service_id, \
233                service_classes, profiles, provider, description, \
234                protocols)
235    except _bt.error as e:
236        raise BluetoothError (str (e))
237
238def stop_advertising (sock):
239    try:
240        _bt.sdp_stop_advertising (sock._sock)
241    except _bt.error as e:
242        raise BluetoothError (str (e))
243
244def find_service (name = None, uuid = None, address = None):
245    if not address:
246        devices = discover_devices ()
247    else:
248        devices = [ address ]
249
250    results = []
251
252    if uuid is not None and not is_valid_uuid (uuid):
253        raise ValueError ("invalid UUID")
254
255    try:
256        for addr in devices:
257            try:
258                s = _bt.SDPSession ()
259                s.connect (addr)
260                matches = []
261                if uuid is not None:
262                    matches = s.search (uuid)
263                else:
264                    matches = s.browse ()
265            except _bt.error:
266                continue
267
268            if name is not None:
269                matches = [s for s in matches if s.get ("name", "") == name]
270
271            for m in matches:
272                m["host"] = addr
273
274            results.extend (matches)
275    except _bt.error as e:
276        raise BluetoothError (str (e))
277
278    return results
279
280# ================ BlueZ internal methods ================
281def _gethcisock (device_id = -1):
282    try:
283        sock = _bt.hci_open_dev (device_id)
284    except:
285        raise BluetoothError ("error accessing bluetooth device")
286    return sock
287
288def _get_acl_conn_handle (hci_sock, addr):
289    hci_fd = hci_sock.fileno ()
290    reqstr = struct.pack ("6sB17s", _bt.str2ba (addr), 
291            _bt.ACL_LINK, "\0" * 17)
292    request = array.array ("c", reqstr)
293    try:
294        fcntl.ioctl (hci_fd, _bt.HCIGETCONNINFO, request, 1)
295    except IOError as e:
296        raise BluetoothError ("There is no ACL connection to %s" % addr)
297
298    # XXX should this be "<8xH14x"?
299    handle = struct.unpack ("8xH14x", request.tostring ())[0]
300    return handle
301
302def _write_flush_timeout (addr, timeout):
303    hci_sock = _bt.hci_open_dev ()
304    # get the ACL connection handle to the remote device
305    handle = _get_acl_conn_handle (hci_sock, addr)
306    # XXX should this be "<HH"
307    pkt = struct.pack ("HH", handle, _bt.htobs (timeout))
308    response = _bt.hci_send_req (hci_sock, _bt.OGF_HOST_CTL, 
309        0x0028, _bt.EVT_CMD_COMPLETE, 3, pkt)
310    status = struct.unpack ("B", response[0])[0]
311    rhandle = struct.unpack ("H", response[1:3])[0]
312    assert rhandle == handle 
313    assert status == 0
314
315def _read_flush_timeout (addr):
316    hci_sock = _bt.hci_open_dev ()
317    # get the ACL connection handle to the remote device
318    handle = _get_acl_conn_handle (hci_sock, addr)
319    # XXX should this be "<H"?
320    pkt = struct.pack ("H", handle)
321    response = _bt.hci_send_req (hci_sock, _bt.OGF_HOST_CTL, 
322        0x0027, _bt.EVT_CMD_COMPLETE, 5, pkt)
323    status = struct.unpack ("B", response[0])[0]
324    rhandle = struct.unpack ("H", response[1:3])[0]
325    assert rhandle == handle
326    assert status == 0
327    fto = struct.unpack ("H", response[3:5])[0]
328    return fto
329
330# =============== DeviceDiscoverer ==================
331class DeviceDiscoverer:
332    """
333    Skeleton class for finer control of the device discovery process.
334
335    To implement asynchronous device discovery (e.g. if you want to do
336    something *as soon as* a device is discovered), subclass
337    DeviceDiscoverer and override device_discovered () and
338    inquiry_complete ()
339    """
340    def __init__ (self, device_id=-1):
341        """
342        __init__ (device_id=-1)
343
344        device_id - The ID of the Bluetooth adapter that will be used
345                    for discovery.
346        """
347        self.sock = None
348        self.is_inquiring = False
349        self.lookup_names = False
350        self.device_id = device_id
351
352        self.names_to_find = {}
353        self.names_found = {}
354
355    def find_devices (self, lookup_names=True, 
356            duration=8, 
357            flush_cache=True):
358        """
359        find_devices (lookup_names=True, service_name=None, 
360                       duration=8, flush_cache=True)
361
362        Call this method to initiate the device discovery process
363
364        lookup_names - set to True if you want to lookup the user-friendly 
365                       names for each device found.
366
367        service_name - set to the name of a service you're looking for.
368                       only devices with a service of this name will be 
369                       returned in device_discovered () NOT YET IMPLEMENTED
370
371
372        ADVANCED PARAMETERS:  (don't change these unless you know what 
373                            you're doing)
374
375        duration - the number of 1.2 second units to spend searching for
376                   bluetooth devices.  If lookup_names is True, then the 
377                   inquiry process can take a lot longer.
378
379        flush_cache - return devices discovered in previous inquiries
380        """
381        if self.is_inquiring:
382            raise BluetoothError ("Already inquiring!")
383
384        self.lookup_names = lookup_names
385
386        self.sock = _gethcisock (self.device_id)
387        flt = _bt.hci_filter_new ()
388        _bt.hci_filter_all_events (flt)
389        _bt.hci_filter_set_ptype (flt, _bt.HCI_EVENT_PKT)
390
391        try:
392            self.sock.setsockopt (_bt.SOL_HCI, _bt.HCI_FILTER, flt)
393        except:
394            raise BluetoothError ("problem with local bluetooth device.")
395
396        # send the inquiry command
397        max_responses = 255
398        cmd_pkt = struct.pack ("BBBBB", 0x33, 0x8b, 0x9e, \
399                duration, max_responses)
400
401        self.pre_inquiry ()
402        
403        try:
404            _bt.hci_send_cmd (self.sock, _bt.OGF_LINK_CTL, \
405                    _bt.OCF_INQUIRY, cmd_pkt)
406        except:
407            raise BluetoothError ("problem with local bluetooth device.")
408
409        self.is_inquiring = True
410
411        self.names_to_find = {}
412        self.names_found = {}
413
414    def cancel_inquiry (self):
415        """
416        Call this method to cancel an inquiry in process.  inquiry_complete 
417        will still be called.
418        """
419        self.names_to_find = {}
420
421        if self.is_inquiring:
422            try:
423                _bt.hci_send_cmd (self.sock, _bt.OGF_LINK_CTL, \
424                        _bt.OCF_INQUIRY_CANCEL)
425                self.sock.close ()
426                self.sock = None
427            except:
428                raise BluetoothError ("error canceling inquiry")
429            self.is_inquiring = False
430
431    def process_inquiry (self):
432        """
433        Repeatedly calls process_event () until the device inquiry has 
434        completed.
435        """
436        while self.is_inquiring or len (self.names_to_find) > 0:
437            self.process_event ()
438
439    def process_event (self):
440        """
441        Waits for one event to happen, and proceses it.  The event will be
442        either a device discovery, or an inquiry completion.
443        """
444        self._process_hci_event ()
445
446    def _process_hci_event (self):
447        import socket
448
449        if self.sock is None: return
450        # voodoo magic!!!
451        pkt = self.sock.recv (258)
452        ptype, event, plen = struct.unpack ("BBB", pkt[:3])
453        pkt = pkt[3:]
454        if event == _bt.EVT_INQUIRY_RESULT:
455            nrsp = struct.unpack ("B", pkt[0])[0]
456            for i in range (nrsp):
457                addr = _bt.ba2str (pkt[1+6*i:1+6*i+6])
458                psrm = pkt[ 1+6*nrsp+i ]
459                pspm = pkt[ 1+7*nrsp+i ]
460                devclass_raw = struct.unpack ("BBB", 
461                        pkt[1+9*nrsp+3*i:1+9*nrsp+3*i+3])
462                devclass = (devclass_raw[2] << 16) | \
463                        (devclass_raw[1] << 8) | \
464                        devclass_raw[0]
465                clockoff = pkt[1+12*nrsp+2*i:1+12*nrsp+2*i+2]
466
467                self._device_discovered (addr, devclass, 
468                        psrm, pspm, clockoff, None)
469        elif event == _bt.EVT_INQUIRY_RESULT_WITH_RSSI:
470            nrsp = struct.unpack ("B", pkt[0])[0]
471            for i in range (nrsp):
472                addr = _bt.ba2str (pkt[1+6*i:1+6*i+6])
473                psrm = pkt[ 1+6*nrsp+i ]
474                pspm = pkt[ 1+7*nrsp+i ]
475#                devclass_raw = pkt[1+8*nrsp+3*i:1+8*nrsp+3*i+3]
476#                devclass = struct.unpack ("I", "%s\0" % devclass_raw)[0]
477                devclass_raw = struct.unpack ("BBB", 
478                        pkt[1+8*nrsp+3*i:1+8*nrsp+3*i+3])
479                devclass = (devclass_raw[2] << 16) | \
480                        (devclass_raw[1] << 8) | \
481                        devclass_raw[0]
482                clockoff = pkt[1+11*nrsp+2*i:1+11*nrsp+2*i+2]
483                rssi = struct.unpack ("b", pkt[1+13*nrsp+i])[0]
484
485                self._device_discovered (addr, devclass, 
486                        psrm, pspm, clockoff, None)
487        elif _bt.HAVE_EVT_EXTENDED_INQUIRY_RESULT and event == _bt.EVT_EXTENDED_INQUIRY_RESULT:
488            nrsp = struct.unpack ("B", pkt[0])[0]
489            for i in range (nrsp):
490                addr = _bt.ba2str (pkt[1+6*i:1+6*i+6])
491                psrm = pkt[ 1+6*nrsp+i ]
492                pspm = pkt[ 1+7*nrsp+i ]
493                devclass_raw = struct.unpack ("BBB",
494                        pkt[1+8*nrsp+3*i:1+8*nrsp+3*i+3])
495                devclass = (devclass_raw[2] << 16) | \
496                        (devclass_raw[1] << 8) | \
497                        devclass_raw[0]
498                clockoff = pkt[1+11*nrsp+2*i:1+11*nrsp+2*i+2]
499                rssi = struct.unpack ("b", pkt[1+13*nrsp+i])[0]
500
501                data_len = _bt.EXTENDED_INQUIRY_INFO_SIZE - _bt.INQUIRY_INFO_WITH_RSSI_SIZE
502                data = pkt[1+14*nrsp+i:1+14*nrsp+i+data_len]
503                name = None
504                pos = 0
505                while(pos <= len(data)):
506                    struct_len = ord(data[pos])
507                    if struct_len == 0:
508                        break
509                    eir_type = ord(data[pos+1])
510                    if eir_type == 0x09: # Complete local name
511                        name = data[pos+2:pos+struct_len+1]
512                    pos += struct_len + 2
513
514                self._device_discovered (addr, devclass,
515                        psrm, pspm, clockoff, name)
516        elif event == _bt.EVT_INQUIRY_COMPLETE:
517            self.is_inquiring = False
518            if len (self.names_to_find) == 0:
519#                print "inquiry complete (evt_inquiry_complete)"
520                self.sock.close ()
521                self.inquiry_complete ()
522            else:
523                self._send_next_name_req ()
524
525        elif event == _bt.EVT_CMD_STATUS:
526            # XXX shold this be "<BBH"
527            status, ncmd, opcode = struct.unpack ("BBH", pkt[:4])
528            if status != 0:
529                self.is_inquiring = False
530                self.sock.close ()
531                
532#                print "inquiry complete (bad status 0x%X 0x%X 0x%X)" % \
533#                        (status, ncmd, opcode)
534                self.names_to_find = {}
535                self.inquiry_complete ()
536        elif event == _bt.EVT_REMOTE_NAME_REQ_COMPLETE:
537            status = struct.unpack ("B", pkt[0])[0]
538            addr = _bt.ba2str (pkt[1:7])
539            if status == 0:
540                try:
541                    name = pkt[7:].split ('\0')[0]
542                except IndexError:
543                    name = '' 
544                if addr in self.names_to_find:
545                    device_class = self.names_to_find[addr][0]
546                    self.device_discovered (addr, device_class, name)
547                    del self.names_to_find[addr]
548                    self.names_found[addr] = ( device_class, name)
549                else:
550                    pass
551            else:
552                if addr in self.names_to_find: del self.names_to_find[addr]
553                # XXX should we do something when a name lookup fails?
554#                print "name req unsuccessful %s - %s" % (addr, status)
555
556            if len (self.names_to_find) == 0:
557                self.is_inquiring = False
558                self.sock.close ()
559                self.inquiry_complete ()
560#                print "inquiry complete (name req complete)"
561            else:
562                self._send_next_name_req ()
563        else:
564            pass
565#            print "unrecognized packet type 0x%02x" % ptype
566
567    def _device_discovered (self, address, device_class, 
568            psrm, pspm, clockoff, name):
569        if self.lookup_names:
570            if name is not None:
571                self.device_discovered (address, device_class, name)
572            elif address not in self.names_found and \
573                address not in self.names_to_find:
574            
575                self.names_to_find[address] = \
576                    (device_class, psrm, pspm, clockoff)
577        else:
578            self.device_discovered (address, device_class, None)
579
580    def _send_next_name_req (self):
581        assert len (self.names_to_find) > 0
582        address = list(self.names_to_find.keys ())[0]
583        device_class, psrm, pspm, clockoff = self.names_to_find[address]
584        bdaddr = _bt.str2ba (address)
585        
586        cmd_pkt = "%s%s\0%s" % (bdaddr, psrm, clockoff)
587
588        try:
589            _bt.hci_send_cmd (self.sock, _bt.OGF_LINK_CTL, \
590                    _bt.OCF_REMOTE_NAME_REQ, cmd_pkt)
591        except Exception as e:
592            raise BluetoothError ("error request name of %s - %s" % 
593                    (address, str (e)))
594
595    def fileno (self):
596        if not self.sock: return None
597        return self.sock.fileno ()
598
599    def pre_inquiry (self):
600        """
601        Called just after find_devices is invoked, but just before the
602        inquiry is started.
603
604        This method exists to be overriden
605        """
606
607    def device_discovered (self, address, device_class, name):
608        """
609        Called when a bluetooth device is discovered.
610
611        address is the bluetooth address of the device
612
613        device_class is the Class of Device, as specified in [1]
614                     passed in as a 3-byte string
615
616        name is the user-friendly name of the device if lookup_names was
617        set when the inquiry was started.  otherwise None
618        
619        This method exists to be overriden.
620
621        [1] https://www.bluetooth.org/foundry/assignnumb/document/baseband
622        """
623        if name:
624            print(("found: %s - %s (class 0x%X)" % \
625                    (address, name, device_class)))
626        else:
627            print(("found: %s (class 0x%X)" % (address, device_class)))
628
629    def inquiry_complete (self):
630        """
631        Called when an inquiry started by find_devices has completed.
632        """
633        print("inquiry complete")