diff --git a/packages/addons/service/bluetooth-audio/changelog.txt b/packages/addons/service/bluetooth-audio/changelog.txt new file mode 100644 index 0000000000..572ffaee81 --- /dev/null +++ b/packages/addons/service/bluetooth-audio/changelog.txt @@ -0,0 +1,5 @@ +101 +- Fix log errors + +100 +- Initial release diff --git a/packages/addons/service/bluetooth-audio/icon/icon.png b/packages/addons/service/bluetooth-audio/icon/icon.png new file mode 100644 index 0000000000..5d57247f6e Binary files /dev/null and b/packages/addons/service/bluetooth-audio/icon/icon.png differ diff --git a/packages/addons/service/bluetooth-audio/package.mk b/packages/addons/service/bluetooth-audio/package.mk new file mode 100644 index 0000000000..eb370d110a --- /dev/null +++ b/packages/addons/service/bluetooth-audio/package.mk @@ -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 . +################################################################################ + +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() { + : +} diff --git a/packages/addons/service/bluetooth-audio/source/default.py b/packages/addons/service/bluetooth-audio/source/default.py new file mode 100644 index 0000000000..22f0e39bfa --- /dev/null +++ b/packages/addons/service/bluetooth-audio/source/default.py @@ -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 . +################################################################################ + +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 diff --git a/packages/addons/service/bluetooth-audio/source/resources/language/English/strings.po b/packages/addons/service/bluetooth-audio/source/resources/language/English/strings.po new file mode 100644 index 0000000000..f9c9babc09 --- /dev/null +++ b/packages/addons/service/bluetooth-audio/source/resources/language/English/strings.po @@ -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 "" diff --git a/packages/addons/service/bluetooth-audio/source/resources/settings.xml b/packages/addons/service/bluetooth-audio/source/resources/settings.xml new file mode 100644 index 0000000000..90a80bfae8 --- /dev/null +++ b/packages/addons/service/bluetooth-audio/source/resources/settings.xml @@ -0,0 +1,7 @@ + + + + + + +