mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-28 13:16:41 +00:00
Merge pull request #1942 from chewitt/btaddon
bluetooth-audio: initial add-on
This commit is contained in:
commit
510401aa2a
5
packages/addons/service/bluetooth-audio/changelog.txt
Normal file
5
packages/addons/service/bluetooth-audio/changelog.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
101
|
||||||
|
- Fix log errors
|
||||||
|
|
||||||
|
100
|
||||||
|
- Initial release
|
BIN
packages/addons/service/bluetooth-audio/icon/icon.png
Normal file
BIN
packages/addons/service/bluetooth-audio/icon/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
46
packages/addons/service/bluetooth-audio/package.mk
Normal file
46
packages/addons/service/bluetooth-audio/package.mk
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
################################################################################
|
||||||
|
# This file is part of LibreELEC - https://libreelec.tv
|
||||||
|
# Copyright (C) 2016-present Team LibreELEC
|
||||||
|
#
|
||||||
|
# LibreELEC is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# LibreELEC is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
PKG_NAME="bluetooth-audio"
|
||||||
|
PKG_VERSION="0"
|
||||||
|
PKG_REV="101"
|
||||||
|
PKG_ARCH="any"
|
||||||
|
PKG_LICENSE="GPL"
|
||||||
|
PKG_SITE=""
|
||||||
|
PKG_URL=""
|
||||||
|
PKG_DEPENDS_TARGET="toolchain"
|
||||||
|
PKG_SECTION="service"
|
||||||
|
PKG_SHORTDESC="Provides the ability to change the kodi audio device if a bluetooth audio device is connected"
|
||||||
|
PKG_LONGDESC="Provides the ability to change the kodi audio device if a bluetooth audio device is connected"
|
||||||
|
PKG_AUTORECONF="no"
|
||||||
|
|
||||||
|
PKG_IS_ADDON="yes"
|
||||||
|
PKG_ADDON_NAME="Bluetooth Audio Device Changer"
|
||||||
|
PKG_ADDON_TYPE="xbmc.service"
|
||||||
|
|
||||||
|
make_target() {
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
makeinstall_target() {
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
addon() {
|
||||||
|
:
|
||||||
|
}
|
194
packages/addons/service/bluetooth-audio/source/default.py
Normal file
194
packages/addons/service/bluetooth-audio/source/default.py
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
################################################################################
|
||||||
|
# This file is part of LibreELEC - https://libreelec.tv
|
||||||
|
# Copyright (C) 2016-present Team LibreELEC
|
||||||
|
#
|
||||||
|
# LibreELEC is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# LibreELEC is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with LibreELEC. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
import dbus
|
||||||
|
import dbus.mainloop.glib
|
||||||
|
import gobject
|
||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import xbmc
|
||||||
|
import xbmcaddon
|
||||||
|
|
||||||
|
__addon__ = xbmcaddon.Addon()
|
||||||
|
__addonid__ = __addon__.getAddonInfo('id')
|
||||||
|
|
||||||
|
gobject.threads_init()
|
||||||
|
|
||||||
|
class KodiFunctions(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.getjson = {
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "Settings.GetSettingValue",
|
||||||
|
"params": {
|
||||||
|
"setting": "audiooutput.audiodevice",
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setjson = {
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "Settings.SetSettingValue",
|
||||||
|
"params": {
|
||||||
|
"setting": "audiooutput.audiodevice",
|
||||||
|
"value": "",
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if __addon__.getSetting('audiodevice') == '':
|
||||||
|
self.audiodevice = json.loads(xbmc.executeJSONRPC(json.dumps(self.getjson)))['result']['value']
|
||||||
|
__addon__.setSetting('audiodevice', self.audiodevice)
|
||||||
|
else:
|
||||||
|
self.audiodevice = __addon__.getSetting('audiodevice')
|
||||||
|
self.pulsedevice = 'PULSE:Default'
|
||||||
|
|
||||||
|
self.select_default()
|
||||||
|
|
||||||
|
def select_default(self):
|
||||||
|
|
||||||
|
self.setjson['params']['value'] = self.audiodevice
|
||||||
|
xbmc.executeJSONRPC(json.dumps(self.setjson))
|
||||||
|
|
||||||
|
def select_pulse(self):
|
||||||
|
|
||||||
|
self.setjson['params']['value'] = self.pulsedevice
|
||||||
|
xbmc.executeJSONRPC(json.dumps(self.setjson))
|
||||||
|
|
||||||
|
class BluetoothAudioClient(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
xbmc.log('%s: starting add-on' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
|
||||||
|
self.devices = {}
|
||||||
|
self.signal_added = None
|
||||||
|
self.signal_removed = None
|
||||||
|
|
||||||
|
self.kodi = KodiFunctions()
|
||||||
|
|
||||||
|
self._setup_loop()
|
||||||
|
self._setup_bus()
|
||||||
|
self._setup_signals()
|
||||||
|
|
||||||
|
def quit(self):
|
||||||
|
|
||||||
|
xbmc.log('%s: stopping add-on' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
|
||||||
|
self.kodi.select_default()
|
||||||
|
|
||||||
|
self.signal_added.remove()
|
||||||
|
self.signal_removed.remove()
|
||||||
|
|
||||||
|
self._loop.quit()
|
||||||
|
|
||||||
|
def _setup_loop(self):
|
||||||
|
|
||||||
|
self._loop = gobject.MainLoop()
|
||||||
|
|
||||||
|
self._thread = threading.Thread(target=self._loop.run)
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
def _setup_bus(self):
|
||||||
|
|
||||||
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
|
self._bus = dbus.SystemBus()
|
||||||
|
|
||||||
|
def _setup_signals(self):
|
||||||
|
|
||||||
|
self.signal_added = self._bus.add_signal_receiver(handler_function=self.switch_audio,
|
||||||
|
signal_name='InterfacesAdded',
|
||||||
|
dbus_interface='org.freedesktop.DBus.ObjectManager',
|
||||||
|
bus_name='org.bluez',
|
||||||
|
member_keyword='signal')
|
||||||
|
|
||||||
|
self.signal_removed = self._bus.add_signal_receiver(handler_function=self.switch_audio,
|
||||||
|
signal_name='InterfacesRemoved',
|
||||||
|
dbus_interface='org.freedesktop.DBus.ObjectManager',
|
||||||
|
bus_name='org.bluez',
|
||||||
|
member_keyword='signal')
|
||||||
|
|
||||||
|
def switch_audio(self, *args, **kwargs):
|
||||||
|
|
||||||
|
device_path = args[0]
|
||||||
|
|
||||||
|
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):
|
||||||
|
xbmc.log('%s: bluetooth audio device connected' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
xbmc.log('%s: switching to bluetooth audio device' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
self.kodi.select_pulse()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
xbmc.log('%s: bluetooth audio device disconnected' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
xbmc.log('%s: checking for other connected devices' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
|
||||||
|
for path in self.devices:
|
||||||
|
if self.devices[path]['Connected'] and self.devices[path]['Class'] & (1 << 21):
|
||||||
|
xbmc.log('%s: found connected bluetooth audio device' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
return
|
||||||
|
|
||||||
|
xbmc.log('%s: switching to default audio device' % __addonid__, xbmc.LOGNOTICE)
|
||||||
|
self.kodi.select_default()
|
||||||
|
|
||||||
|
except (TypeError, KeyError, dbus.exceptions.DBusException) as e:
|
||||||
|
xbmc.log('%s: ' % __addonid__ + unicode(e), xbmc.LOGERROR)
|
||||||
|
|
||||||
|
class BluetoothMonitor(xbmc.Monitor):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
|
xbmc.Monitor.__init__(self)
|
||||||
|
|
||||||
|
if (__name__ == "__main__"):
|
||||||
|
monitor = BluetoothMonitor()
|
||||||
|
client = BluetoothAudioClient()
|
||||||
|
|
||||||
|
monitor.waitForAbort()
|
||||||
|
|
||||||
|
client.quit()
|
||||||
|
|
||||||
|
del BluetoothAudioClient
|
||||||
|
del BluetoothMonitor
|
@ -0,0 +1,11 @@
|
|||||||
|
# Kodi Media Center language file
|
||||||
|
# Addon Name: bluetooth-audio
|
||||||
|
# Addon id: service.bluetooth-audio
|
||||||
|
|
||||||
|
msgctxt "#30000"
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "#30001"
|
||||||
|
msgid "Audio Device"
|
||||||
|
msgstr ""
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<settings>
|
||||||
|
<category label="30000">
|
||||||
|
<setting label="30001" type="text" id="audiodevice" default=""/>
|
||||||
|
</category>
|
||||||
|
</settings>
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user