#! /usr/bin/python3 from __future__ import print_function from __future__ import division from __future__ import absolute_import from __future__ import unicode_literals import dbus from dbus.mainloop.glib import DBusGMainLoop from gi.repository import Pango import os.path import sys import signal #support running uninstalled _dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) if os.path.exists(os.path.join(_dirname, "CHANGELOG.md")): sys.path.insert(0, _dirname) import blueman.bluez as Bluez from blueman.Constants import * from blueman.Functions import * from blueman.main.SignalTracker import SignalTracker enable_rgba_colormap() import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk DBusGMainLoop(set_as_default=True) # Workaround introspection bug, gnome bug 622084 signal.signal(signal.SIGINT, signal.SIG_DFL) class BluemanAdapters: def __init__(self, selected_hci_dev): setup_icon_path() self.bus = dbus.SystemBus() self.bus.watch_name_owner('org.bluez', self.on_dbus_name_owner_change) builder = Gtk.Builder() builder.set_translation_domain("blueman") builder.add_from_file(UI_PATH + "/adapters.ui") self.dialog = builder.get_object("dialog") check_single_instance("blueman-adapters", lambda time: self.dialog.present_with_time(time)) check_bluetooth_status(_("Bluetooth needs to be turned on for the adapter manager to work"), lambda: exit()) self.dialog.props.icon_name = "blueman-device" self.dialog.connect("response", self.on_dialog_response) self.notebook = builder.get_object("notebook") self.tabs = {} try: self.signals = SignalTracker() self.manager = Bluez.Manager() self.signals.Handle("bluez", self.manager, self.on_adapter_added, 'AdapterAdded') adapters = self.manager.list_adapters() for adapter in adapters: self.add_to_notebook(adapter) except Exception as e: print(e) self.manager = None #fixme: show error dialog and exit #activate a particular tab according to command line option if selected_hci_dev is not None: if selected_hci_dev in self.tabs: hci_dev_num = int(selected_hci_dev[3:]) self.notebook.set_current_page(hci_dev_num) else: print('Error: the selected adapter does not exist') self.dialog.show() Gtk.main() def on_dialog_response(self, dialog, response_id): for hci, settings in self.tabs.items(): if settings['changed']: settings['adapter'].set_name(settings['name']) Gtk.main_quit() def on_adapter_added(self, adapter_path): def on_property_changed(name, value): if name == 'Powered' and value: adapter.unhandle_signal(on_property_changed, "PropertyChanged") self.add_to_notebook(adapter) adapter = Bluez.Adapter(adapter_path) adapter.handle_signal(on_property_changed, "PropertyChanged") def on_dbus_name_owner_change(self, owner): print('org.bluez owner changed to '+owner) if owner == '': self.manager = None #fixme: show error dialog and exit def build_adapter_tab(self, adapter): adapter_settings = {} def on_property_changed(name, value): if name == 'Powered': if not value: self.remove_from_notebook(adapter) else: self.add_to_notebook(adapter) elif name == "Discoverable": if value == 0: hidden_radio.set_active(True) def on_hidden_toggle(radio): if not radio.props.active: return adapter_settings['discoverable'] = False adapter.set('Discoverable', False) hscale.set_sensitive(False) #we need to triger "format-value" signal hscale.set_value(1) hscale.set_value(0) def on_always_toggle(radio): if not radio.props.active: return adapter_settings['discoverable'] = True adapter.set('Discoverable', True) hscale.set_sensitive(False) #we need to triger "format-value" signal hscale.set_value(1) hscale.set_value(0) def on_temporary_toggle(radio): if not radio.props.active: return adapter_settings['discoverable'] = True adapter.set('Discoverable', True) hscale.set_sensitive(True) hscale.set_value(3) def on_scale_format_value(scale, value): if value == 0: if adapter_settings['discoverable']: return _("Always") else: return _("Hidden") else: return gettext.ngettext("%d Minute", "%d Minutes", value) % (value) def on_scale_value_changed(scale): val = scale.get_value() print('value: '+str(val)) if val == 0 and adapter_settings['discoverable']: always_radio.props.active = True timeout = int(val * 60) adapter.set('DiscoverableTimeout', timeout) def on_name_changed(entry): adapter_settings['name'] = entry.get_text() adapter_settings['changed'] = True props = adapter.get_properties() adapter_settings['adapter'] = adapter adapter_settings['signals'] = SignalTracker() adapter_settings['signals'].Handle(adapter, on_property_changed, "PropertyChanged") adapter_settings['address'] = props['Address'] adapter_settings['name'] = adapter.get_name() adapter_settings['discoverable'] = props['Discoverable'] #we use count timeout in minutes adapter_settings['discoverable_timeout'] = props['DiscoverableTimeout'] / 60 adapter_settings['changed'] = False builder = Gtk.Builder() builder.set_translation_domain("blueman") builder.add_from_file(UI_PATH + "/adapters-tab.ui") adapter_settings['vbox'] = builder.get_object("vbox1") hscale = builder.get_object("hscale") hscale.set_range(0, 30) hscale.set_increments(1, 1) if adapter_settings['discoverable']: hscale.set_value(adapter_settings['discoverable_timeout']) hscale.set_sensitive(adapter_settings['discoverable_timeout'] > 0) adapter_settings['signals'].Handle(hscale, "format-value", on_scale_format_value) adapter_settings['signals'].Handle(hscale, "value-changed", on_scale_value_changed) hidden_radio = builder.get_object("hidden1") if not adapter_settings['discoverable']: hidden_radio.set_active(True) adapter_settings['signals'].Handle(hidden_radio, "toggled", on_hidden_toggle) always_radio = builder.get_object("always") if adapter_settings['discoverable'] and adapter_settings['discoverable_timeout'] == 0: always_radio.set_active(True) adapter_settings['signals'].Handle(always_radio, "toggled", on_always_toggle) temporary_radio = builder.get_object("temporary") if adapter_settings['discoverable'] and adapter_settings['discoverable_timeout'] > 0: temporary_radio.set_active(True) adapter_settings['signals'].Handle(temporary_radio, "toggled", on_temporary_toggle) name_entry = builder.get_object("name_entry") name_entry.set_text(adapter_settings['name']) adapter_settings['signals'].Handle(name_entry, "changed", on_name_changed) return adapter_settings def add_to_notebook(self, adapter): hci_dev = os.path.basename(adapter.get_object_path()) hci_dev_num = int(hci_dev[3:]) if not hci_dev in self.tabs: self.tabs[hci_dev] = self.build_adapter_tab(adapter) else: if self.tabs[hci_dev]['visible']: return #might need to update settings at this point settings = self.tabs[hci_dev] settings['visible'] = True name = settings['name'] if name == '': name = _('Adapter') + ' %d' % (hci_dev_num + 1) label = Gtk.Label(label=name) label.set_max_width_chars(20) label.props.hexpand = True label.set_ellipsize(Pango.EllipsizeMode.END) self.notebook.insert_page(settings['vbox'], label, hci_dev_num) def remove_from_notebook(self, adapter): hci_dev = os.path.basename(adapter.get_object_path()) hci_dev_num = int(hci_dev[3:]) self.tabs[hci_dev]['visible'] = False self.notebook.remove_page(hci_dev_num) #leave actual tab contents intact in case adapter becomes present once again if __name__ == '__main__': set_proc_title() adapter_name = None if len(sys.argv) > 1: adapter_name = sys.argv[1] BluemanAdapters(adapter_name)