Skip to content

Commit

Permalink
Add a disconnect callback for BlueZ
Browse files Browse the repository at this point in the history
The callback will be called only on unsolicited disconnect event from
peer. An initiated disconnect from the host will not call it.

Other implementations (Core Bluetooth and .NET) will fail with a
NotImplementedError exception.
  • Loading branch information
arthur-proglove authored and Johannes Hutter committed Jul 17, 2019
1 parent b767f9d commit 235088a
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
28 changes: 27 additions & 1 deletion bleak/backends/bluezdbus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def __init__(self, address, loop=None, **kwargs):
self._bus = None
self._rules = {}

self._disconnected_callback = None

self._char_path_to_uuid = {}

# We need to know BlueZ version since battery level characteristic
Expand All @@ -51,6 +53,20 @@ def __init__(self, address, loop=None, **kwargs):

# Connectivity methods

def set_disconnected_callback(
self, callback: Callable[[BaseBleakClient], None], **kwargs
) -> None:
"""Set the disconnected callback.
The callback will be called on DBus PropChanged event with
the 'Connected' key set to False.
Args:
callback: callback to be called on disconnection.
"""

self._disconnected_callback = callback

async def connect(self, **kwargs) -> bool:
"""Connect to the specified GATT server.
Expand Down Expand Up @@ -531,7 +547,17 @@ def _properties_changed_callback(self, message):
self._notification_callbacks[message.path](
message.path, message.body[1]
)

elif message.body[0] == defs.DEVICE_INTERFACE:
device_path = '/org/bluez/%s/dev_%s' % (self.device,
self.address.replace(':', '_'))
if message.path == device_path:
message_body_map = message.body[1]
if 'Connected' in message_body_map and \
not message_body_map['Connected']:
logger.debug("Device {} disconnected."
.format(self.address))
if self._disconnected_callback is not None:
self._disconnected_callback(self)

def _data_notification_wrapper(func, char_map):
@wraps(func)
Expand Down
24 changes: 24 additions & 0 deletions bleak/backends/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,30 @@ async def __aexit__(self, exc_type, exc_val, exc_tb):

# Connectivity methods

@abc.abstractmethod
async def set_disconnected_callback(
self, callback: Callable[['BaseBleakClient'], None], **kwargs
) -> None:
"""Set the disconnect callback.
The callback will only be called on unsolicited disconnect event.
Callbacks must accept one input which is the client object itself.
.. code-block:: python
def callback(client):
print("Client with address {} got disconnected!".format(client.address))
client.set_disconnected_callback(callback)
client.connect()
Args:
callback: callback to be called on disconnection.
"""

raise NotImplementedError()

@abc.abstractmethod
async def connect(self, **kwargs) -> bool:
"""Connect to the specified GATT server.
Expand Down

0 comments on commit 235088a

Please sign in to comment.