def set_op(self, device: Device, message: str) -> None: ManagerDeviceMenu.__ops__[device.get_object_path()] = message for inst in ManagerDeviceMenu.__instances__: logging.info(f"op: regenerating instance {inst}") if inst.SelectedDevice == self.SelectedDevice and not ( inst.is_popup and not inst.props.visible): inst.generate()
def disconnect_service(self, device: Device, uuid: str = GENERIC_CONNECT, port: int = 0) -> None: def ok(_obj: AppletService, _result: None, _user_date: None) -> None: logging.info("disconnect success") self.generate() def err(_obj: Optional[AppletService], result: GLib.Error, _user_date: None) -> None: logging.warning(f"disconnect failed {result}") msg, tb = e_(result.message) MessageArea.show_message(_("Disconnection Failed: ") + msg, tb) self.generate() if self._appl is None: err(None, GLib.Error('Applet DBus Service not available'), None) return self._appl.DisconnectService('(osd)', device.get_object_path(), uuid, port, result_handler=ok, error_handler=err, timeout=GLib.MAXINT)
def generic_connect(self, _item: Optional[Gtk.MenuItem], device: Device, connect: bool) -> None: def fail(_obj: AppletService, result: GLib.Error, _user_data: None) -> None: logging.info(f"fail: {result}") prog.message(_("Failed")) self.unset_op(device) self._handle_error_message(result.message) def success(_obj: AppletService, _result: None, _user_data: None) -> None: logging.info("success") prog.message(_("Success!")) MessageArea.close() self.unset_op(device) assert self._appl if connect: self.set_op(self.SelectedDevice, _("Connecting…")) self._appl.ConnectService("(os)", device.get_object_path(), '00000000-0000-0000-0000-000000000000', result_handler=success, error_handler=fail, timeout=GLib.MAXINT) else: self.set_op(self.SelectedDevice, _("Disconnecting…")) self._appl.DisconnectService( "(osd)", device.get_object_path(), '00000000-0000-0000-0000-000000000000', 0, result_handler=success, error_handler=fail, timeout=GLib.MAXINT) prog = ManagerProgressbar(self.Blueman) def abort() -> None: assert self._appl is not None # https://github.com/python/mypy/issues/2608 self._appl.DisconnectService( "(osd)", device.get_object_path(), '00000000-0000-0000-0000-000000000000', 0) prog.connect("cancelled", lambda x: abort()) prog.start()
def device_remove_event(self, device: Device) -> None: tree_iter = self.find_device(device) row_fader = self.get(tree_iter, "row_fader")["row_fader"] super().device_remove_event(device) self._faderhandlers.update({ device.get_object_path(): row_fader.connect("animation-finished", self.__on_fader_finished, device) }) row_fader.thaw() self.emit("device-selected", None, None) row_fader.animate(start=row_fader.get_state(), end=0.0, duration=400)
def generic_connect(self, _item: Gtk.MenuItem, device: Device, connect: bool) -> None: def fail(_obj: AppletService, result: GLib.Error, _user_data: None) -> None: logging.info(f"fail: {result}") prog.message(_("Failed")) self.unset_op(device) msg, tb = e_(result.message) MessageArea.show_message(_("Connection Failed: ") + msg) def success(_obj: AppletService, _result: None, _user_data: None) -> None: logging.info("success") prog.message(_("Success!")) MessageArea.close() self.unset_op(device) assert self._appl if connect: self.set_op(self.SelectedDevice, _("Connecting…")) self._appl.ConnectService("(os)", device.get_object_path(), '00000000-0000-0000-0000-000000000000', result_handler=success, error_handler=fail, timeout=GLib.MAXINT) else: self.set_op(self.SelectedDevice, _("Disconnecting…")) self._appl.DisconnectService( "(osd)", device.get_object_path(), '00000000-0000-0000-0000-000000000000', 0, result_handler=success, error_handler=fail, timeout=GLib.MAXINT) prog = ManagerProgressbar(self.Blueman, False) prog.start()
def find_device(self, device: Device) -> Optional[Gtk.TreeIter]: object_path = device.get_object_path() try: row = self.path_to_row[object_path] if row.valid(): path = row.get_path() tree_iter = self.liststore.get_iter(path) return tree_iter else: del self.path_to_row[object_path] return None except KeyError: return None
def add_device(self, device: Device) -> None: # device belongs to another adapter if not self.Adapter or not device[ 'Adapter'] == self.Adapter.get_object_path(): return logging.info("adding new device") tree_iter = self.liststore.append() self.set(tree_iter, device=device) self.row_setup_event(tree_iter, device) object_path = device.get_object_path() timestamp = datetime.strftime(datetime.now(), '%Y%m%d%H%M%S%f') self.set(tree_iter, dbus_path=object_path, timestamp=float(timestamp))
def bind_to_menuitem(self, item: Gtk.CheckMenuItem, device: Device, uuid: str) -> None: data = device.get_object_path(), uuid def switch(active: bool) -> None: services = set(self["services"]) if active: self["services"] = set(services).union({data}) else: self["services"] = set(self["services"]).difference({data}) def on_change(config: AutoConnectConfig, key: str) -> None: if key == "services": item.props.active = data in set(config[key]) item.props.active = data in set(self["services"]) item.connect("toggled", lambda i: switch(i.props.active)) self.connect("changed", on_change)
def _update_power_levels(self, tree_iter: Gtk.TreeIter, device: Device, cinfo: conn_info) -> None: row = self.get(tree_iter, "cell_fader", "battery", "rssi", "lq", "tpl") bars = {} if device["ServicesResolved"] and any(ServiceUUID(uuid).short_uuid == BATTERY_SERVICE_SVCLASS_ID for uuid in device["UUIDs"]): bars["battery"] = Battery(obj_path=device.get_object_path())["Percentage"] # cinfo init may fail for bluetooth devices version 4 and up # FIXME Workaround is horrible and we should show something better if cinfo.failed: if not bars: bars = {"rssi": 100.0, "tpl": 100.0, "lq": 100.0} else: try: bars["rssi"] = max(50 + float(cinfo.get_rssi()) / 127 * 50, 10) except ConnInfoReadError: bars["rssi"] = 50 try: bars["lq"] = max(50 + float(cinfo.get_lq()) / 127 * 50, 10) except ConnInfoReadError: bars["lq"] = 50 try: bars["tpl"] = max(float(cinfo.get_tpl()) / 255 * 100, 10) except ConnInfoReadError: bars["tpl"] = 0 if row["battery"] == row["rssi"] == row["tpl"] == row["lq"] == 0: self._prepare_fader(row["cell_fader"]).animate(start=0.0, end=1.0, duration=400) w = 14 * self.get_scale_factor() h = 48 * self.get_scale_factor() for (name, perc) in bars.items(): if round(row[name], -1) != round(perc, -1): icon_name = f"blueman-{name}-{int(round(perc, -1))}.png" icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(os.path.join(PIXMAP_PATH, icon_name), w, h, True) self.set(tree_iter, **{name: perc, f"{name}_pb": icon})
def __on_fader_finished(self, fader: TreeRowFade, device: Device) -> None: fader.disconnect(self._faderhandlers.pop(device.get_object_path())) fader.freeze()
class Device(GObject.GObject): __gsignals__ = { 'invalidated': (GObject.SignalFlags.NO_HOOKS, None, ()), 'property-changed': (GObject.SignalFlags.NO_HOOKS, None, (GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT,)), } def __init__(self, instance): GObject.GObject.__init__(self) self.Properties = {} self.Fake = True self.Temp = False if isinstance(instance, str) or isinstance(instance, unicode): self.Device = BluezDevice(instance) else: self.Device = instance #set fallback icon, fixes lp:#327718 self.Device.Icon = "blueman" self.Device.Class = "unknown" self.__services = {} self.Valid = True self.Signals = SignalTracker() dprint("caching initial properties") self.Properties = self.Device.get_properties() if not "Fake" in self.Properties: self.Fake = False w = weakref.ref(self) if not self.Fake: self._obj_path = self.Device.get_object_path() self.Signals.Handle("bluez", self.Device, lambda key, value: w() and w().property_changed(key, value), "PropertyChanged") object_path = self.Device.get_object_path() adapter = Adapter(object_path.replace("/" + os.path.basename(object_path), "")) self.Signals.Handle("bluez", adapter, lambda path: w() and w().on_device_removed(path), "DeviceRemoved") @property def Services(self): if len(self.__services) == 0: self.init_services() return self.__services def __del__(self): dprint("deleting device", self.get_object_path()) self.Destroy() def get_object_path(self): if not self.Fake: return self._obj_path def on_device_removed(self, path): if path == self._obj_path: self.emit("invalidated") self.Destroy() def init_services(self): dprint("Loading services") if not self.Fake: services = self.Device.list_services() self.__services = {} for service in services: name = service.get_interface_name().split(".") if name[0] == 'org' and name[1] == 'bluez': name = name[2].lower() if name.endswith('1'): name = name[:-1] self.__services[name] = service def Copy(self): if not self.Valid: raise Exception("Attempted to copy an invalidated device") return Device(self.Device) def property_changed(self, key, value): self.emit("property-changed", key, value) self.Properties[key] = value if key == "UUIDs": self.init_services() def Destroy(self): dprint("invalidating device", self.get_object_path()) self.Valid = False #self.Device = None self.Signals.DisconnectAll() #def __del__(self): # dprint("DEBUG: deleting Device instance") def get_properties(self): #print "Properties requested" if not self.Valid: raise Exception("Attempted to get properties for an invalidated device") return self.Properties def __getattr__(self, name): if name in self.__dict__["Properties"]: if not self.Valid: #traceback.print_stack() dprint("Warning: Attempted to get %s property for an invalidated device" % name) return self.__dict__["Properties"][name] else: return getattr(self.Device, name) def __setattr__(self, key, value): if not key in self.__dict__ and "Properties" in self.__dict__ and key in self.__dict__["Properties"]: if not self.Valid: raise Exception("Attempted to set properties for an invalidated device") dprint("Setting property", key, value) self.__dict__["Device"].set(key, value) else: self.__dict__[key] = value
def on_request_menu_items(self, manager_menu: ManagerDeviceMenu, device: Device) -> List[DeviceMenuItem]: items: List[DeviceMenuItem] = [] appl = AppletService() services = get_services(device) connectable_services = [ service for service in services if service.connectable ] for service in connectable_services: item: Gtk.MenuItem = create_menuitem(service.name, service.icon) if service.description: item.props.tooltip_text = service.description item.connect( "activate", lambda _item: manager_menu.connect_service( service.device, service.uuid)) items.append( DeviceMenuItem(item, DeviceMenuItem.Group.CONNECT, service.priority)) item.props.sensitive = service.available item.show() connected_services = [ service for service in services if service.connected_instances ] for service in connected_services: for instance in service.connected_instances: surface = self._make_x_icon(service.icon, 16) item = create_menuitem(instance.name, surface=surface) item.connect( "activate", lambda _item: manager_menu.disconnect_service( service.device, service.uuid, instance.port)) items.append( DeviceMenuItem(item, DeviceMenuItem.Group.DISCONNECT, service.priority + 100)) item.show() if services: config = AutoConnectConfig() autoconnect_services = set(config["services"]) for service in services: if service.connected_instances or ( device.get_object_path(), service.uuid) in autoconnect_services: item = Gtk.CheckMenuItem(label=service.name) config.bind_to_menuitem(item, device, service.uuid) item.show() items.append( DeviceMenuItem(item, DeviceMenuItem.Group.AUTOCONNECT, service.priority)) for action, priority in set((action, service.priority) for service in services for action in service.common_actions if any(plugin in appl.QueryPlugins() for plugin in action.plugins)): item = create_menuitem(action.title, action.icon) items.append( DeviceMenuItem(item, DeviceMenuItem.Group.ACTIONS, priority + 200)) item.show() item.connect("activate", self._get_activation_handler(action.callback)) return items
def get_op(self, device: Device) -> Optional[str]: try: return ManagerDeviceMenu.__ops__[device.get_object_path()] except KeyError: return None
def __init__(self, device: Device, uuid: str): super().__init__(device, uuid) self._service = Network(obj_path=device.get_object_path())
def remove_device(self, device: Device) -> None: param = GLib.Variant('(o)', (device.get_object_path(), )) self._call('RemoveDevice', param)
class Device(GObject.GObject): __gsignals__ = { str('invalidated'): (GObject.SignalFlags.NO_HOOKS, None, ()), str('property-changed'): (GObject.SignalFlags.NO_HOOKS, None, (GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT,)), } def __init__(self, instance): GObject.GObject.__init__(self) self.Properties = {} self.Temp = False if hasattr(instance, "format") and hasattr(instance, "upper"): self.Device = BluezDevice(instance) else: self.Device = instance #set fallback icon, fixes lp:#327718 self.Device.Icon = "blueman" self.Device.Class = "unknown" self.Valid = True dprint("caching initial properties") self.Properties = self.Device.get_properties() w = weakref.ref(self) self._obj_path = self.Device.get_object_path() self.Device.connect_signal('property-changed', lambda _device, key, value: w() and w().property_changed(key, value)) self._any_adapter = Adapter() self._any_adapter.connect_signal('device-removed', lambda _adapter, path: w() and w().on_device_removed(path)) def get_service(self, uuid): for name, cls in inspect.getmembers(blueman.services, inspect.isclass): if uuid128_to_uuid16(uuid) == cls.__svclass_id__: return cls(self, uuid) def get_services(self): services = (self.get_service(uuid) for uuid in self.UUIDs) return [service for service in services if service] def __del__(self): dprint("deleting device", self.get_object_path()) self.Destroy() def get_object_path(self): return self._obj_path def on_device_removed(self, path): if path == self._obj_path: self.emit("invalidated") self.Destroy() def Copy(self): if not self.Valid: raise Exception("Attempted to copy an invalidated device") return Device(self.Device) def property_changed(self, key, value): self.emit("property-changed", key, value) self.Properties[key] = value def Destroy(self): dprint("invalidating device", self.get_object_path()) self.Valid = False #self.Device = None #def __del__(self): # dprint("DEBUG: deleting Device instance") def get_properties(self): #print "Properties requested" if not self.Valid: raise Exception("Attempted to get properties for an invalidated device") return self.Properties def __getattr__(self, name): if name in self.__dict__["Properties"]: if not self.Valid: #traceback.print_stack() dprint("Warning: Attempted to get %s property for an invalidated device" % name) return self.__dict__["Properties"][name] else: return getattr(self.Device, name) def __setattr__(self, key, value): if not key in self.__dict__ and "Properties" in self.__dict__ and key in self.__dict__["Properties"]: if not self.Valid: raise Exception("Attempted to set properties for an invalidated device") dprint("Setting property", key, value) self.__dict__["Device"].set(key, value) else: self.__dict__[key] = value
class Device(GObject.GObject): __gsignals__ = { 'invalidated': (GObject.SignalFlags.NO_HOOKS, None, ()), 'property-changed': (GObject.SignalFlags.NO_HOOKS, None, ( GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT, )), } def __init__(self, instance): GObject.GObject.__init__(self) self.Properties = {} self.Fake = True self.Temp = False if isinstance(instance, str) or isinstance(instance, unicode): self.Device = BluezDevice(instance) else: self.Device = instance #set fallback icon, fixes lp:#327718 self.Device.Icon = "blueman" self.Device.Class = "unknown" self.__services = {} self.Valid = True self.Signals = SignalTracker() dprint("caching initial properties") self.Properties = self.Device.get_properties() if not "Fake" in self.Properties: self.Fake = False w = weakref.ref(self) if not self.Fake: self._obj_path = self.Device.get_object_path() self.Signals.Handle( "bluez", self.Device, lambda key, value: w() and w().property_changed(key, value), "PropertyChanged") object_path = self.Device.get_object_path() adapter = Adapter( object_path.replace("/" + os.path.basename(object_path), "")) self.Signals.Handle( "bluez", adapter, lambda path: w() and w().on_device_removed(path), "DeviceRemoved") @property def Services(self): if len(self.__services) == 0: self.init_services() return self.__services def __del__(self): dprint("deleting device", self.get_object_path()) self.Destroy() def get_object_path(self): if not self.Fake: return self._obj_path def on_device_removed(self, path): if path == self._obj_path: self.emit("invalidated") self.Destroy() def init_services(self): dprint("Loading services") if not self.Fake: services = self.Device.list_services() self.__services = {} for service in services: name = service.get_interface_name().split(".") if name[0] == 'org' and name[1] == 'bluez': name = name[2].lower() if name.endswith('1'): name = name[:-1] self.__services[name] = service def Copy(self): if not self.Valid: raise Exception("Attempted to copy an invalidated device") return Device(self.Device) def property_changed(self, key, value): self.emit("property-changed", key, value) self.Properties[key] = value if key == "UUIDs": self.init_services() def Destroy(self): dprint("invalidating device", self.get_object_path()) self.Valid = False #self.Device = None self.Signals.DisconnectAll() #def __del__(self): # dprint("DEBUG: deleting Device instance") def get_properties(self): #print "Properties requested" if not self.Valid: raise Exception( "Attempted to get properties for an invalidated device") return self.Properties def __getattr__(self, name): if name in self.__dict__["Properties"]: if not self.Valid: #traceback.print_stack() dprint( "Warning: Attempted to get %s property for an invalidated device" % name) return self.__dict__["Properties"][name] else: return getattr(self.Device, name) def __setattr__(self, key, value): if not key in self.__dict__ and "Properties" in self.__dict__ and key in self.__dict__[ "Properties"]: if not self.Valid: raise Exception( "Attempted to set properties for an invalidated device") dprint("Setting property", key, value) self.__dict__["Device"].set(key, value) else: self.__dict__[key] = value