Skip to content

Commit

Permalink
Replace hcitool and hciconfig with bluez dbus
Browse files Browse the repository at this point in the history
hcitool and hciconfig are both deprecated in upstream bluez-utils. In
order to maintain the same functionality. The new implementation still
shells out to rfkill in order to unblock the hci devices in the event
they become blocked.
  • Loading branch information
asonix committed Oct 20, 2017
1 parent fd0a555 commit 628dd92
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 36 deletions.
3 changes: 3 additions & 0 deletions CONTRIBUTORS.md
Expand Up @@ -19,4 +19,7 @@
[jouva](https://github.com/jouva)
* cleaned up the readme

[asonix](https://github.com/asonix)
* changed pair to use DBus instead of poking at /var/lib/bluetooth and hci{config,tool} as root

Thanks for all the help in making Joustmania an awesome game!!
5 changes: 2 additions & 3 deletions README.md
Expand Up @@ -78,9 +78,8 @@ Pairing controllers

If pairing is not working for some reason, or you would like to resync all controllers run the following
```
sudo -i
cd /home/pi/JoustMania/
./reset_bluetooth_connections.sh
cd ~/JoustMania/
python clear_devices.py
```

How to select a game mode
Expand Down
10 changes: 10 additions & 0 deletions clear_devices.py
@@ -0,0 +1,10 @@
import jm_dbus

if __name__ == '__main__':
hcis = jm_dbus.get_hci_dict().keys()
for hci in hcis:
hci_proxy = jm_dbus.get_adapter_proxy(hci)
devices = jm_dbus.get_node_child_names(hci_proxy)

for dev in devices:
jm_dbus.remove_device(hci, dev)
159 changes: 159 additions & 0 deletions jm_dbus.py
@@ -0,0 +1,159 @@
"""
This module handles interacting with Bluez over DBus for JoustMania
"""
import os
import dbus
from xml.etree import ElementTree

BUS = dbus.SystemBus()
ORG_BLUEZ = 'org.bluez'
ORG_BLUEZ_PATH = '/org/bluez'

def get_hci_dict():
"""Get dictionary mapping hci number to address"""
proxy = get_root_proxy()
hcis = get_node_child_names(proxy)
hci_dict = {}

for hci in hcis:
proxy2 = get_adapter_proxy(hci)
addr = get_adapter_attrib(proxy2, 'Address')
hci_dict[hci] = str(addr)

return hci_dict

def get_attached_addresses(hci):
"""Get the addresses of devices known by hci"""
proxy = get_adapter_proxy(hci)
devices = get_node_child_names(proxy)

known_devices = []
for dev in devices:
proxy2 = get_device_proxy(hci, dev)

dev_addr = str(get_device_attrib(proxy2, 'Address'))
known_devices.append(dev_addr)

return known_devices

def get_bus():
"""Get DBus hook"""
return BUS

def get_root_proxy():
"""Get root Bluez DBus node"""
return BUS.get_object(ORG_BLUEZ, ORG_BLUEZ_PATH)

def enable_pairable(hci):
"""Allow devices to pair with the HCI"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
if not iface.Get('org.bluez.Adapter1', 'Pairable'):
iface.Set('org.bluez.Adapter1', 'Pairable', True)

def disable_pairable(hci):
"""Prevent devices from pairing with the HCI"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
if iface.Get('org.bluez.Adapter1', 'Pairable'):
iface.Set('org.bluez.Adapter1', 'Pairable', False)

def get_discovery_filters(hci):
"""Get information about discovery options"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.bluez.Adapter1')
return iface.GetDiscoveryFilters()

def start_discovery(hci):
"""Start scanning for devices"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.bluez.Adapter1')
if not get_adapter_attrib(proxy, 'Discovering').real:
try:
return iface.StartDiscovery()
except dbus.exceptions.DBusException as e:
if "InProgress" in str(e) or "NotReady" in str(e):
pass
else:
raise e

def stop_discovery(hci):
"""Stop scanning for devices"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.bluez.Adapter1')
if get_adapter_attrib(proxy, 'Discovering').real:
try:
return iface.StopDiscovery()
except dbus.exceptions.DBusException as e:
if "InProgress" in str(e) or "NotReady" in str(e):
pass
else:
raise e

def remove_device(hci, dev):
hci_proxy = get_adapter_proxy(hci)
dev_proxy = get_device_proxy(hci, dev)
iface = dbus.Interface(hci_proxy, 'org.bluez.Adapter1')
return iface.RemoveDevice(dev_proxy)

def enable_adapter(hci):
"""Set the HCI's Powered attribute to true"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
if iface.Get('org.bluez.Adapter1', 'Powered').real:
return False
else:
try:
print('Enabling adapter')
iface.Set('org.bluez.Adapter1', 'Powered', True)
return True
except dbus.exceptions.DBusException as e:
if "rfkill" in str(e):
rfkill_unblock(hci)
# Recurse after unblocking the bluetooth adapter
return enable_adapter(hci)
else:
raise e

def rfkill_unblock(hci):
hci_id = os.popen('rfkill list | grep {0} | cut -d ":" -f 1'.format(hci)).read().split('\n')[0]
os.popen('rfkill unblock {0}'.format(hci_id)).read()

def disable_adapter(hci):
"""Set the HCI's Powered attribute to false"""
proxy = get_adapter_proxy(hci)
iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
if iface.Get('org.bluez.Adapter1', 'Powered').real:
iface.Set('org.bluez.Adapter1', 'Powered', False)
return True
else:
return False

def get_adapter_proxy(hci):
"""Abstract getting Bluez DBus adapter nodes"""
hci_path = os.path.join(ORG_BLUEZ_PATH, hci)
return BUS.get_object(ORG_BLUEZ, hci_path)

def get_device_proxy(hci, dev):
"""Abstract getting Bluez DBus device nodes"""
device_path = os.path.join(ORG_BLUEZ_PATH, hci, dev)
return BUS.get_object(ORG_BLUEZ, device_path)

def get_bluez_attrib(proxy, kind, attrib):
"""Abstract getting attributes from Bluez DBus Interfaces"""
iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
return iface.Get('org.bluez.{0}'.format(kind), attrib)

def get_adapter_attrib(proxy, attrib):
"""Abstract getting attributes from Bluez Adapter1 Interfaces"""
return get_bluez_attrib(proxy, 'Adapter1', attrib)

def get_device_attrib(proxy, attrib):
"""Abstract getting attributes from Bluez Device1 Interfaces"""
return get_bluez_attrib(proxy, 'Device1', attrib)

def get_node_child_names(proxy):
"""Abstract finding child nodes of a DBus Node"""
iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Introspectable')
tree = ElementTree.fromstring(iface.Introspect())
return [child.attrib['name'] for child in tree if child.tag == 'node']
45 changes: 34 additions & 11 deletions pair.py
@@ -1,33 +1,56 @@
import psmove
import os

BT_DIR = '/var/lib/bluetooth/'
import jm_dbus

class Pair():
"""
Manage paring move controllers to the server
"""
def __init__(self):
"""Use DBus to find bluetooth controllers"""
self.hci_dict = jm_dbus.get_hci_dict()

devices = self.hci_dict.values()
self.bt_devices = {}
devices = os.listdir(BT_DIR)
for device in devices:
self.bt_devices[device] = []

self.pre_existing_devices()

def pre_existing_devices(self):
for device in self.bt_devices.keys():
device_path = os.path.join(BT_DIR, device)
print ('trust file is ' + str(device_path))
if os.path.exists(device_path):
self.bt_devices[device] = [bt for bt in os.listdir(device_path) if ':' in bt]
else:
print('the path doesnt exist! ' )
"""
Enumerate known devices
For each device on each adapter, add the device's address to it's adapter's
list of known devices
"""
for hci, addr in self.hci_dict.items():
proxy = jm_dbus.get_adapter_proxy(hci)
devices = jm_dbus.get_node_child_names(proxy)

self.bt_devices[addr] = jm_dbus.get_attached_addresses(hci)

def update_adapters(self):
"""
Rescan for bluetooth adapters that may not have existed on program launch
"""
self.hci_dict = jm_dbus.get_hci_dict()

for addr in self.hci_dict.values():
if addr not in self.bt_devices.keys():
self.bt_devices[addr] = []

self.pre_existing_devices()

def check_if_not_paired(self, addr):
for devs in self.bt_devices.keys():
if addr in self.bt_devices[devs]:
return False
return True
return True

def get_lowest_bt_device(self):
num = 9999999
print(self.bt_devices)
for dev in self.bt_devices.keys():
if len(self.bt_devices[dev]) < num:
num = len(self.bt_devices[dev])
Expand Down
25 changes: 11 additions & 14 deletions piparty.py
Expand Up @@ -7,6 +7,7 @@
from enum import Enum
from multiprocessing import Process, Value, Array, Queue, Manager
from games import ffa
import jm_dbus


TEAM_NUM = len(colors.team_color_list)
Expand Down Expand Up @@ -291,20 +292,16 @@ def check_for_new_moves(self):
#self.alive_count = len([move.get_serial() for move in self.moves if self.move_opts[move.get_serial()][Opts.alive.value] == Alive.on.value])


@staticmethod
def enable_bt_scanning(on=True):
scan_cmd = "hciconfig {0} {1}"
if on:
scan = "pscan"
else:
scan = "noscan"
bt_hcis = os.popen("hcitool dev | grep hci | awk '{print $1}'").read().split('\n')
bt_hcis = [bt for bt in bt_hcis if bt]
def enable_bt_scanning(self, on=True):
bt_hcis = list(jm_dbus.get_hci_dict().keys())

for hci in bt_hcis:
scan_enabled = os.popen(scan_cmd.format(hci, scan)).read()
if not bt_hcis:
for i in range(8):
os.popen("sudo hciconfig hci{} up".format(i))
if jm_dbus.enable_adapter(hci):
self.pair.update_adapters()
if on:
jm_dbus.enable_pairable(hci)
else:
jm_dbus.disable_pairable(hci)

def pair_usb_move(self, move):
move_serial = move.get_serial()
Expand Down Expand Up @@ -726,4 +723,4 @@ def start_game(self, random_mode=False):
if __name__ == "__main__":
InitAudio()
piparty = Menu()
piparty.game_loop()
piparty.game_loop()
5 changes: 0 additions & 5 deletions reset_bluetooth_connections.sh

This file was deleted.

7 changes: 4 additions & 3 deletions setup.sh
Expand Up @@ -21,9 +21,10 @@ setup() {
libportmidi-dev portaudio19-dev \
libsdl-image1.2-dev libsdl-ttf2.0-dev \
libblas-dev liblapack-dev \
bluez supervisor cmake ffmpeg \
bluez bluez-tools rfkill supervisor cmake ffmpeg \
libudev-dev swig libbluetooth-dev \
alsa-utils alsa-tools libasound2-dev || exit -1
alsa-utils alsa-tools libasound2-dev \
python-dbus-dev libdbus-glib-1-dev || exit -1

#install components for psmoveapi
sudo apt-get install -y \
Expand All @@ -44,7 +45,7 @@ setup() {
rm -rf $VENV
/usr/bin/python3.6 -m virtualenv --system-site-packages $VENV || exit -1
PYTHON=$VENV/bin/python3.6
$PYTHON -m pip install --ignore-installed psutil flask Flask-WTF pyalsaaudio pydub pygame pyaudio pyyaml || exit -1
$PYTHON -m pip install --ignore-installed psutil flask Flask-WTF pyalsaaudio pydub pygame pyaudio pyyaml dbus-python || exit -1

#install psmoveapi
git clone --recursive git://github.com/thp/psmoveapi.git
Expand Down

0 comments on commit 628dd92

Please sign in to comment.