bluetooth-audio: use dbussy/ravel

This commit is contained in:
mglae 2020-10-03 15:47:47 +02:00
parent 6bcdacb108
commit 5f281c01da
2 changed files with 45 additions and 75 deletions

View File

@ -3,12 +3,10 @@
# Copyright (C) 2016-present Team LibreELEC (https://libreelec.tv) # Copyright (C) 2016-present Team LibreELEC (https://libreelec.tv)
import sys import sys
import dbus
import dbus.mainloop.glib
import gobject
import time import time
gobject.threads_init() import asyncio
import ravel
class BluetoothAudioClient(object): class BluetoothAudioClient(object):
@ -19,87 +17,54 @@ class BluetoothAudioClient(object):
self.signal_removed = None self.signal_removed = None
self._setup_loop() self._setup_loop()
self._setup_bus()
self._setup_signals()
def quit(self):
self.signal_added.remove()
self.signal_removed.remove()
self._loop.quit()
def _setup_loop(self): def _setup_loop(self):
self._loop = gobject.MainLoop() self._loop = asyncio.new_event_loop()
self.bus = ravel.system_bus()
self.bus.attach_asyncio(self._loop)
self.bus.listen_objects_added(self.object_added)
self.bus.listen_objects_removed(self.object_removed)
def run(self): def run(self):
self._loop.run() self._loop.run_forever()
def _setup_bus(self): @ravel.signal(name = "InterfacesAdded", in_signature = "oa{sa{sv}}",
arg_keys = ("device_path", "args"))
def object_added(self, device_path, args) :
if 'org.bluez.MediaTransport1' in args:
self.devices[device_path] = {
'Connected': '',
'Device': str(args['org.bluez.MediaTransport1']['Device'][1]),
'Class': '',
}
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) audio_device_iface = self.bus['org.bluez'][self.devices[device_path]['Device']].get_interface('org.bluez.Device1')
self._bus = dbus.SystemBus() self.devices[device_path]['Class'] = audio_device_iface.Class
self.devices[device_path]['Connected'] = audio_device_iface.Connected
def _setup_signals(self): if self.devices[device_path]['Class'] & (1 << 21):
print('bluetooth')
sys.stdout.flush()
self.signal_added = self._bus.add_signal_receiver(handler_function=self.switch_audio, @ravel.signal(name = "InterfacesRemoved", in_signature = "oas",
signal_name='InterfacesAdded', arg_keys = ("device_path", "args"))
dbus_interface='org.freedesktop.DBus.ObjectManager', def object_removed(self, device_path, args) :
bus_name='org.bluez', if device_path in self.devices and self.devices[device_path]['Class'] & (1 << 21):
member_keyword='signal') audio_device_iface = self.bus['org.bluez'][self.devices[device_path]['Device']].get_interface('org.bluez.Device1')
self.devices[device_path]['Connected'] = audio_device_iface.Connected
self.signal_removed = self._bus.add_signal_receiver(handler_function=self.switch_audio, while self.devices[device_path]['Connected']:
signal_name='InterfacesRemoved', self.devices[device_path]['Connected'] = audio_device_iface.Connected
dbus_interface='org.freedesktop.DBus.ObjectManager', time.sleep(0.1)
bus_name='org.bluez',
member_keyword='signal')
def switch_audio(self, *args, **kwargs): for path in self.devices:
if self.devices[path]['Connected'] and self.devices[path]['Class'] & (1 << 21):
return
device_path = args[0] print('default')
sys.stdout.flush()
try:
if kwargs['signal'] == 'InterfacesAdded':
self.devices[device_path] = {
'Connected': '',
'Device': '',
'Class': '',
}
device = self._bus.get_object('org.bluez', device_path)
device_iface = dbus.Interface(device, dbus.PROPERTIES_IFACE)
self.devices[device_path]['Device'] = device_iface.Get('org.bluez.MediaTransport1', 'Device')
audio_device_path = self._bus.get_object('org.bluez', self.devices[device_path]['Device'])
audio_device_iface = dbus.Interface(audio_device_path, dbus.PROPERTIES_IFACE)
self.devices[device_path]['Class'] = audio_device_iface.Get('org.bluez.Device1', 'Class')
self.devices[device_path]['Connected'] = audio_device_iface.Get('org.bluez.Device1', 'Connected')
if self.devices[device_path]['Class'] & (1 << 21):
print('bluetooth')
sys.stdout.flush()
elif kwargs['signal'] == 'InterfacesRemoved':
if self.devices[device_path]['Device'] is not None and self.devices[device_path]['Class'] & (1 << 21):
audio_device_path = self._bus.get_object('org.bluez', self.devices[device_path]['Device'])
audio_device_iface = dbus.Interface(audio_device_path, dbus.PROPERTIES_IFACE)
self.devices[device_path]['Connected'] = audio_device_iface.Get('org.bluez.Device1', 'Connected')
while self.devices[device_path]['Connected']:
self.devices[device_path]['Connected'] = audio_device_iface.Get('org.bluez.Device1', 'Connected')
time.sleep(0.1)
for path in self.devices:
if self.devices[path]['Connected'] and self.devices[path]['Class'] & (1 << 21):
return
print('default')
sys.stdout.flush()
except (TypeError, KeyError, dbus.exceptions.DBusException) as e:
print('%s: ' % str(e), file=sys.stderr)
client = BluetoothAudioClient() client = BluetoothAudioClient()

View File

@ -5,11 +5,12 @@ import json
import subprocess import subprocess
import threading import threading
import xbmc import xbmc
import xbmcvfs
import xbmcaddon import xbmcaddon
__addon__ = xbmcaddon.Addon() __addon__ = xbmcaddon.Addon()
__addonid__ = __addon__.getAddonInfo('id') __addonid__ = __addon__.getAddonInfo('id')
__addonpath__ = xbmc.translatePath(xbmcaddon.Addon().getAddonInfo('path')) __addonpath__ = xbmcvfs.translatePath(xbmcaddon.Addon().getAddonInfo('path'))
class KodiFunctions(object): class KodiFunctions(object):
@ -41,6 +42,7 @@ class KodiFunctions(object):
self.audiodevice = __addon__.getSetting('audiodevice') self.audiodevice = __addon__.getSetting('audiodevice')
self.pulsedevice = 'PULSE:Default' self.pulsedevice = 'PULSE:Default'
xbmc.log('%s: setting default audio device "%s" on start' % (__addonid__, self.audiodevice), xbmc.LOGINFO)
self.select_default() self.select_default()
def select_default(self): def select_default(self):
@ -60,7 +62,7 @@ class BluetoothAudioClient(object):
xbmc.log('%s: starting add-on' % __addonid__, xbmc.LOGINFO) xbmc.log('%s: starting add-on' % __addonid__, xbmc.LOGINFO)
self.kodi = KodiFunctions() self.kodi = KodiFunctions()
self.path = __addonpath__ + '/bin/dbusservice.py' self.path = __addonpath__ + 'bin/dbusservice.py'
self.service = subprocess.Popen([self.path], stdout=subprocess.PIPE) self.service = subprocess.Popen([self.path], stdout=subprocess.PIPE)
@ -75,9 +77,11 @@ class BluetoothAudioClient(object):
if line == b'': if line == b'':
break break
if line == b'bluetooth\n': if line == b'bluetooth\n':
xbmc.log('%s: switching to bluetooth audio device' % __addonid__, xbmc.LOGINFO)
self.kodi.select_pulse() self.kodi.select_pulse()
continue continue
if line == b'default\n': if line == b'default\n':
xbmc.log('%s: switching to default audio device' % __addonid__, xbmc.LOGINFO)
self.kodi.select_default() self.kodi.select_default()
continue continue
xbmc.log('%s: unexpected input: %s' % (__addonid__, line), xbmc.LOGERROR) xbmc.log('%s: unexpected input: %s' % (__addonid__, line), xbmc.LOGERROR)
@ -89,6 +93,7 @@ class BluetoothAudioClient(object):
self.service.terminate() self.service.terminate()
self._thread.join() self._thread.join()
del self.service
self.kodi.select_default() self.kodi.select_default()