Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
language: python

python:
- "2.7_with_system_site_packages"
- 2.7
- 3.4

virtualenv:
system_site_packages: true

before_install:
- "sudo apt-get -qq update"

install:
- "sudo apt-get install python-dbus python-gobject"
- "sudo pip install coveralls flake8 cffi"
- "sudo apt-get install libffi-dev"
- "sudo pip install --upgrade coverage flake8 cffi pydbus psutil"
- "sudo make -C codecs install"

script:
Expand Down
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Installing the RTP/SBC codec (this must be done first):
Note: the default platform is x86 (mmx). To build for a different platform e.g., armv6
then run ``sudo make -C codecs install PLATFORM=armv6`` instead.

Prerequisites

sudo apt-get install python-pip libffi-dev

Install the python library by running:

pip install BT-Manager
Expand All @@ -52,6 +56,11 @@ Project resources
Changelog
=========

v0.4.0
------

BlueZ 5.x support

v0.3.0
------

Expand Down
10 changes: 5 additions & 5 deletions bt_manager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import cffi
import os

__version__ = '0.3.1'
__version__ = '0.4.0'

if StrictVersion(cffi.__version__) < StrictVersion('0.7'):
raise RuntimeError(
Expand All @@ -18,11 +18,7 @@
fh.close()

from bt_manager.adapter import BTAdapter # noqa
from bt_manager.agent import BTAgent # noqa
from bt_manager.attributes import ATTRIBUTES # noqa
from bt_manager.audio import BTAudio, BTAudioSource # noqa
from bt_manager.audio import BTAudioSink, SBCAudioCodec # noqa
from bt_manager.audio import SBCAudioSource, SBCAudioSink # noqa
from bt_manager.cod import BTCoD # noqa
from bt_manager.codecs import * # noqa
from bt_manager.control import BTControl # noqa
Expand All @@ -40,3 +36,7 @@
from bt_manager.uuid import BTUUID, BTUUID16, BTUUID32 # noqa
from bt_manager.uuid import BASE_UUID # noqa
from bt_manager.vendors import VENDORS # noqa
from bt_manager.audio import BTAudio, BTAudioSource, BTAudioSink # noqa
from bt_manager.audio import SBCAudioSource, SBCAudioSink, SBCAudioCodec # noqa
from bt_manager.audio5 import SBCAudioSource5, SBCAudioSink5, SBCAudioCodec # noqa
from bt_manager.agent import BTAgent # noqa
164 changes: 145 additions & 19 deletions bt_manager/adapter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import unicode_literals

from interface import BTInterface
from manager import BTManager
import dbus
from dbus import DBusException
from interface import BTInterface, translate_to_dbus_type


class BTAdapter(BTInterface):
Expand Down Expand Up @@ -108,18 +109,109 @@ class BTAdapter(BTInterface):
Signal notifying when a device is now out-of-range
"""

def __init__(self, adapter_path=None, adapter_id=None):
manager = BTManager()
if (adapter_path is None):
if (adapter_id is None):
adapter_path = manager.default_adapter()
ADAPTER_INTERFACE_BLUEZ4 = 'org.bluez.Adapter'
ADAPTER_INTERFACE_BLUEZ5 = 'org.bluez.Adapter1'
DEVICE_INTERFACE_BLUEZ5 = 'org.bluez.Device1'
AGENT_INTERFACE = 'org.bluez.AgentManager1'

def __init__(self, adapter_path):
self.adapter_path = adapter_path
if (self.get_version() <= self.BLUEZ4_VERSION):
BTInterface.__init__(self, adapter_path, BTAdapter.ADAPTER_INTERFACE_BLUEZ4)
self._properties = self._interface.GetProperties().keys()
self._register_signal_name(BTAdapter.SIGNAL_PROPERTY_CHANGED)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_FOUND)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_REMOVED)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_CREATED)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_DISAPPEARED)

else:
BTInterface.__init__(self, adapter_path, self.ADAPTER_INTERFACE_BLUEZ5)
self._init_agent()
self._init_object_manager()
self._init_properties()

def _init_properties(self):
self._props_interface = dbus.Interface(self._object, self.DBUS_PROPERTIES)
self._properties = list(self._props_interface.GetAll(self.ADAPTER_INTERFACE_BLUEZ5).keys())
self._register_signal_name(self.SIGNAL_PROPERTIES_CHANGED)

def _init_agent(self):
bluez_path = self._bus.get_object(self.BLUEZ_DBUS_OBJECT, '/org/bluez')
self._agent_interface = dbus.Interface(bluez_path, self.AGENT_INTERFACE)

def _init_object_manager(self):
root_path = self._bus.get_object(self.BLUEZ_DBUS_OBJECT, '/')
self._ojects_interface = dbus.Interface(root_path, self.DBUS_OBJ_MANAGER)
self._register_signal_name(self.SIGNAL_INTERFACES_ADDED)
self._register_signal_name(self.SIGNAL_INTERFACES_REMOVED)

def get_path(self):
return self.adapter_path

def get_property(self, name=None):
"""
Helper to get a property value by name or all
properties as a dictionary.

See also :py:meth:`set_property`

:param str name: defaults to None which means all properties
in the object's dictionary are returned as a dict.
Otherwise, the property name key is used and its value
is returned.
:return: Property value by property key, or a dictionary of
all properties
:raises KeyError: if the property key is not found in the
object's dictionary
:raises dbus.Exception: org.bluez.Error.DoesNotExist
:raises dbus.Exception: org.bluez.Error.InvalidArguments
"""
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
if (name):
return self._interface.GetProperties()[name]
else:
adapter_path = manager.find_adapter(adapter_id)
BTInterface.__init__(self, adapter_path, 'org.bluez.Adapter')
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_FOUND)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_REMOVED)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_CREATED)
self._register_signal_name(BTAdapter.SIGNAL_DEVICE_DISAPPEARED)
return self._interface.GetProperties()

# BlueZ 5
else:
if (name):
return self._props_interface.Get(self.ADAPTER_INTERFACE_BLUEZ5, name)
else:
return self._props_interface.GetAll(self.ADAPTER_INTERFACE_BLUEZ5)

def set_property(self, name, value):
"""
Helper to set a property value by name, translating to correct
dbus type

See also :py:meth:`get_property`

:param str name: The property name in the object's dictionary
whose value shall be set.
:param value: Properties new value to be assigned.
:return:
:raises KeyError: if the property key is not found in the
object's dictionary
:raises dbus.Exception: org.bluez.Error.DoesNotExist
:raises dbus.Exception: org.bluez.Error.InvalidArguments
"""
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
typeof = type(self.get_property(name))
self._interface.SetProperty(
name,
translate_to_dbus_type(typeof, value)
)

# BlueZ 5
else:
typeof = type(self.get_property(name))
self._props_interface.Set(
BTAdapter.ADAPTER_INTERFACE_BLUEZ5, name,
translate_to_dbus_type(typeof, value)
)

def start_discovery(self):
"""
Expand Down Expand Up @@ -153,7 +245,7 @@ def stop_discovery(self):
"""
return self._interface.StopDiscovery()

def find_device(self, dev_id):
def find_device(self, dev_id, property='Address'):
"""
Returns the object path of device for given address.
The device object needs to be first created via
Expand All @@ -168,7 +260,16 @@ def find_device(self, dev_id):
:raises dbus.Exception: org.bluez.Error.DoesNotExist
:raises dbus.Exception: org.bluez.Error.InvalidArguments
"""
return self._interface.FindDevice(dev_id)
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
return self._interface.FindDevice(dev_id)
# BlueZ 5
else:
for (key, object) in self._ojects_interface.GetManagedObjects().items():
if self.DEVICE_INTERFACE_BLUEZ5 in object:
if object[self.DEVICE_INTERFACE_BLUEZ5][property] == dev_id:
return key
raise DBusException('org.bluez.Error.DoesNotExist')

def list_devices(self):
"""
Expand All @@ -180,7 +281,17 @@ def list_devices(self):
:raises dbus.Exception: org.bluez.Error.Failed
:raises dbus.Exception: org.bluez.Error.OutOfMemory
"""
return self._interface.ListDevices()
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
return self._interface.ListDevices()
# BlueZ 5
else:
objects = self._ojects_interface.GetManagedObjects().items()
devices = []
for (key, object) in objects:
if self.DEVICE_INTERFACE_BLUEZ5 in object:
devices.append(key)
return devices

def create_paired_device(self, dev_id, agent_path,
capability, cb_notify_device, cb_notify_error):
Expand Down Expand Up @@ -219,11 +330,16 @@ def create_paired_device(self, dev_id, agent_path,
:raises dbus.Exception: org.bluez.Error.InvalidArguments
:raises dbus.Exception: org.bluez.Error.Failed
"""
return self._interface.CreatePairedDevice(dev_id,
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
return self._interface.CreatePairedDevice(dev_id,
agent_path,
capability,
reply_handler=cb_notify_device, # noqa
error_handler=cb_notify_error) # noqa
# BlueZ 5 TODO !?
else:
pass

def remove_device(self, dev_path):
"""
Expand Down Expand Up @@ -257,7 +373,12 @@ def register_agent(self, path, capability):
:raises dbus.Exception: org.bluez.Error.InvalidArguments
:raises dbus.Exception: org.bluez.Error.AlreadyExists
"""
return self._interface.RegisterAgent(path, capability)
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
return self._interface.RegisterAgent(path, capability)
# BlueZ 5
else:
return self._agent_interface.RegisterAgent(path, capability)

def unregister_agent(self, path):
"""
Expand All @@ -270,4 +391,9 @@ def unregister_agent(self, path):
:return:
:raises dbus.Exception: org.bluez.Error.DoesNotExist
"""
return self._interface.UnregisterAgent(path)
# BlueZ 4
if (self.get_version() <= self.BLUEZ4_VERSION):
return self._interface.UnregisterAgent(path)
# BlueZ 5
else:
return self._agent_interface.UnregisterAgent(path)
Loading