Skip to content

Commit

Permalink
Allow multiple connections at once via new BluefangConnection class
Browse files Browse the repository at this point in the history
  • Loading branch information
tmcneal committed Sep 5, 2017
1 parent dcc8b02 commit 5f52ccb
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 62 deletions.
Empty file.
54 changes: 10 additions & 44 deletions bluefang/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,18 @@
import dbus.mainloop.glib # type: ignore
from gi.repository import GObject as gobject # type: ignore
import logging
from queue import *
import sys
import time # type: ignore
import collections
from typing import Any, List
from typing import Any, List, Optional

from bluefang import agents
from bluefang.connection import BluefangConnection
from bluefang import servicerecords
from bluefang import profile
from bluefang import l2cap

BLUEZ_SERVICE = "org.bluez" # type: str
BLUEZ_ADAPTER = BLUEZ_SERVICE + ".Adapter1" # type: str
BLUEZ_AGENT_MANAGER = BLUEZ_SERVICE + ".AgentManager1" # type: str
BLUEZ_DEVICE = BLUEZ_SERVICE + ".Device1" # type: str
BLUEZ_PROFILE_MANAGER = BLUEZ_SERVICE + ".ProfileManager1" # type: str

HID_UUID = "00001124-0000-1000-8000-00805f9b34fb" # type: str
HID_CONTROL_PSM = 17 # type: int
HID_INTERRUPT_PSM = 19 # type: int
from bluefang.constants import *

BluetoothDevice = collections.namedtuple('BluetoothDevice', 'name alias address bluetooth_class is_connected is_paired path')

Expand All @@ -48,7 +41,7 @@ def info(self) -> Any:

return adapter

def connect(self, deviceAddress: str) -> None:
def connect(self, device_address: str) -> Optional[BluefangConnection]:
"""
Attempt to connect to the given Device.
"""
Expand All @@ -66,26 +59,10 @@ def connect(self, deviceAddress: str) -> None:
arg0="org.bluez.Device1"
)

manager = dbus.Interface(dbus.SystemBus().get_object(BLUEZ_SERVICE, "/"), "org.freedesktop.DBus.ObjectManager")
#device = manager.GetManagedObjects()['/org/bluez/hci0'] # Requires correct permissions in /etc/dbus-1/system-local.conf
theDevice = next((x for x in self.devices if x.address == deviceAddress), None)
if theDevice is None:
raise Exception("Unable to find device %s. Try scanning first." % deviceAddress)

device = dbus.Interface(dbus.SystemBus().get_object(BLUEZ_SERVICE, theDevice.path), BLUEZ_DEVICE)
control_socket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
control_socket.connect((deviceAddress, HID_CONTROL_PSM))
logging.info("Connected! Spawning control thread")
control_connection = l2cap.L2CAPServerThread(control_socket, deviceAddress)
control_connection.daemon = True
control_connection.start()

interrupt_socket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
interrupt_socket.connect((deviceAddress, HID_INTERRUPT_PSM))
logging.info("Connected! Spawning interrupt thread")
interrupt_connection = l2cap.L2CAPClientThread(interrupt_socket, deviceAddress)
interrupt_connection.daemon = True
interrupt_connection.start()
connection = BluefangConnection(device_address, self.devices)
connection.connect()

return connection

def _connection_established(self, changed: Any, invalidated: Any, path: Any) -> None:
logging.info("Connection has been established")
Expand Down Expand Up @@ -120,21 +97,10 @@ def start_server(self) -> None:
control.daemon = True
control.start()

interrupt = l2cap.L2CAPClientThread(interrupt_socket, interrupt_address)
interrupt = l2cap.L2CAPClientThread(interrupt_socket, interrupt_address, Queue())
interrupt.daemon = True
interrupt.start()

def poll_commands(self) -> None:
while 1:
command = input("Enter a command: ")
self.send_command(command)

def send_command(self, command: Any) -> None:
l2cap.q.put(command)

def disconnect(self, blah: Any) -> None:
logging.info("Disconnected %s" % blah) #TODO implement this later

def discoverable(self, state: str) -> None:
deviceManager = dbus.Interface(dbus.SystemBus().get_object(BLUEZ_SERVICE, "/org/bluez/hci0"), 'org.freedesktop.DBus.Properties')
if state == "on":
Expand Down
18 changes: 9 additions & 9 deletions bluefang/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,23 @@ def stop(self) -> None:
self._connection._unregister_object_path(AGENT_PATH)

@dbus.service.method(BLUEZ_AGENT, in_signature="os", out_signature="")
def DisplayPinCode(self, device: Any, pincode: str) -> None:
def DisplayPinCode(self, device, pincode):
logging.info("DisplayPinCode invoked")

@dbus.service.method(BLUEZ_AGENT, in_signature="o", out_signature="s")
def RequestPinCode(self, device: Any) -> str:
def RequestPinCode(self, device):
logging.info("Pairing with device [{}]".format(device))
self.pin_code = input("Please enter the pin code: ")
logging.info("Trying with pin code: [{}]".format(self.pin_code))
self.trust_device(device)
return self.pin_code

@dbus.service.method("org.bluez.Agent", in_signature="ou", out_signature="")
def DisplayPasskey(self, device: Any, passkey: int) -> None:
def DisplayPasskey(self, device, passkey):
logging.info("Passkey ({}, {:06d})".format(device, passkey))

@dbus.service.method(BLUEZ_AGENT, in_signature="ou", out_signature="")
def RequestConfirmation(self, device: Any, passkey: int) -> None:
def RequestConfirmation(self, device, passkey):
"""Always confirm"""
logging.info("RequestConfirmation (%s, %06d)" % (device, passkey))
time.sleep(2)
Expand All @@ -89,30 +89,30 @@ def RequestConfirmation(self, device: Any, passkey: int) -> None:
return

@dbus.service.method(BLUEZ_AGENT, in_signature="os", out_signature="")
def AuthorizeService(self, device: Any, uuid: str) -> None:
def AuthorizeService(self, device, uuid):
"""Always authorize"""
logging.info("AuthorizeService method invoked")
return

@dbus.service.method(BLUEZ_AGENT, in_signature="o", out_signature="u")
def RequestPasskey(self, device: Any) -> Any:
def RequestPasskey(self, device):
logging.info("RequestPasskey")
passkey = input("Please enter pass key: ")
return dbus.UInt32(passkey)

@dbus.service.method(BLUEZ_AGENT, in_signature="o", out_signature="")
def RequestPairingConsent(self, device: Any) -> None:
def RequestPairingConsent(self, device):
logging.info("RequestPairingConsent")
return

@dbus.service.method(BLUEZ_AGENT, in_signature="o", out_signature="")
def RequestAuthorization(self, device: Any) -> None:
def RequestAuthorization(self, device):
"""Always authorize"""
logging.info("Authorizing device [{}]".format(self.device))
return

@dbus.service.method(BLUEZ_AGENT, in_signature="", out_signature="")
def Cancel(self) -> None:
def Cancel(self):
logging.info("Pairing request canceled from device [{}]".format(self.device))

def trust_device(self, path: str) -> None:
Expand Down
59 changes: 59 additions & 0 deletions bluefang/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-

import dbus # type: ignore
import bluetooth # type: ignore
from queue import *
from bluefang.constants import *
from typing import Any, List
import logging
from bluefang import l2cap


class BluefangConnection:
def __init__(self, device_address: str, devices: List[Any]) -> None: # TODO don't pass devices in here
self.device_address = device_address # type: str
self.q = Queue() # type: Queue
self.devices = devices
self.is_connected = False

def connect(self) -> None:

manager = dbus.Interface(dbus.SystemBus().get_object(BLUEZ_SERVICE, "/"), "org.freedesktop.DBus.ObjectManager")
#device = manager.GetManagedObjects()['/org/bluez/hci0'] # Requires correct permissions in /etc/dbus-1/system-local.conf
the_device = next((x for x in self.devices if x.address == self.device_address), None)
if the_device is None:
raise Exception("Unable to find device %s. Try scanning first." % self.device_address)

device = dbus.Interface(dbus.SystemBus().get_object(BLUEZ_SERVICE, the_device.path), BLUEZ_DEVICE)
control_socket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
control_socket.connect((self.device_address, HID_CONTROL_PSM))
logging.info("Connected! Spawning control thread")
control_connection = l2cap.L2CAPServerThread(control_socket, self.device_address)
control_connection.daemon = True
control_connection.start()

interrupt_socket = bluetooth.BluetoothSocket(bluetooth.L2CAP)
interrupt_socket.connect((self.device_address, HID_INTERRUPT_PSM))
logging.info("Connected! Spawning interrupt thread")
interrupt_connection = l2cap.L2CAPClientThread(interrupt_socket, self.device_address, self.q)
interrupt_connection.daemon = True
interrupt_connection.start()

self.is_connected = True

def poll_commands(self) -> None:
if not self.is_connected:
raise Exception("Connection must first be established")

while 1:
command = input("Enter a command: ")
self.send_command(command)

def send_command(self, command: Any) -> None:
if not self.is_connected:
raise Exception("Connection must first be established")

self.q.put(command)

def disconnect(self, blah: Any) -> None:
raise Exception("TODO Implement this")
12 changes: 12 additions & 0 deletions bluefang/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

# -*- coding: utf-8 -*-

BLUEZ_SERVICE = "org.bluez" # type: str
BLUEZ_ADAPTER = BLUEZ_SERVICE + ".Adapter1" # type: str
BLUEZ_AGENT_MANAGER = BLUEZ_SERVICE + ".AgentManager1" # type: str
BLUEZ_DEVICE = BLUEZ_SERVICE + ".Device1" # type: str
BLUEZ_PROFILE_MANAGER = BLUEZ_SERVICE + ".ProfileManager1" # type: str

HID_UUID = "00001124-0000-1000-8000-00805f9b34fb" # type: str
HID_CONTROL_PSM = 17 # type: int
HID_INTERRUPT_PSM = 19 # type: int
9 changes: 4 additions & 5 deletions bluefang/l2cap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@
from bluefang import commands
from typing import Any

q = Queue() # type: Queue[str]

class L2CAPClientThread(Thread):
def __init__(self, socket: Any, address: str) -> None:
def __init__(self, socket: Any, address: str, q: Queue) -> None:
Thread.__init__(self)
self.socket = socket
self.address = address
self.q = q

def run(self) -> None:
logging.info("Sending on address: {0}".format(self.address))
while True:
command = q.get()
command = self.q.get()
self.process_command(command)
q.task_done()
self.q.task_done()

def process_command(self, command: str) -> None:
logging.info("Received command: " + command)
Expand Down
9 changes: 5 additions & 4 deletions bluefang/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@
class Profile(dbus.service.Object):
fd = -1

# dbus decorated methods can't have type annotations. See https://github.com/smurfix/flask-script/issues/169
@dbus.service.method("org.bluez.Profile1", in_signature="", out_signature="")
def Release(self) -> None:
def Release(self):
logging.info("Release")
#mainloop.quit()

@dbus.service.method("org.bluez.Profile1", in_signature="", out_signature="")
def Cancel(self) -> None:
def Cancel(self):
logging.info("Cancel")

@dbus.service.method("org.bluez.Profile1", in_signature="oha{sv}", out_signature="")
def NewConnection(self, path: Any, fd: Any, properties: Any) -> None:
def NewConnection(self, path, fd, properties):
logging.info("in new connection")
self.fd = fd.take()
logging.info("NewConnection(%s, %d)" % (path, self.fd))
Expand All @@ -31,7 +32,7 @@ def NewConnection(self, path: Any, fd: Any, properties: Any) -> None:
logging.info(" %s = %s" % (key, properties[key]))

@dbus.service.method("org.bluez.Profile1", in_signature="o", out_signature="")
def RequestDisconnect(self, path: Any) -> None:
def RequestDisconnect(self, path):
logging.info("RequestDisconnection(%s)" % (path))

if(self.fd > 0):
Expand Down

0 comments on commit 5f52ccb

Please sign in to comment.