Search code examples
pythonbluetoothbluezobex

Bluez-obex and python opp server, how to change where files are stored?


I am creating a python-based opp obex server using bluez-obex, but I'm having trouble changing the directory. I based my code off of this and turned it into a class with a file path as it's input. I keep getting KeyError: 'object does not export any interfaces; you might need to pass object path as the 2nd argument for get()' with my current code. Am I setting the path wrong? My class code and calling function are below.

from gi.repository import GLib
import pydbus
class opp_server():
    def __init__(self, path):
        self.BUS_NAME = 'org.bluez.obex'
        self.PATH = '/org/bluez/obex'
        self.AGENT_MANAGER_INTERFACE = 'org.bluez.obex.AgentManager1'
        self.AGENT_INTERFACE = 'org.bluez.obex.Agent1'
        self.SESSION_INTERFACE = 'org.bluez.obex.Session1'
        self.TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'

        self.ses_bus = pydbus.SessionBus()
        self.path = path

    def transfer_status_handler(self, iface, props_changed, props_removed):
        if iface == self.TRANSFER_INTERFACE:
            status = props_changed.get('Status')
            if status == 'complete':
                print('Transfer complete')
            elif status == 'queued':
                print('Still queued')
            elif status == 'active':
                print('transferring')
            elif status == 'suspended':
                print('Suspended')
            elif status == 'error':
                print('error')

    def iface_added_handler(self, dbus_path, interfaces):
        if self.SESSION_INTERFACE in interfaces and 'server' in dbus_path:
            print('Server session added')
        elif self.TRANSFER_INTERFACE in interfaces and 'server' in dbus_path:
            print('Transfer started')
            transfer = self.ses_bus.get(self.BUS_NAME, dbus_path)
            transfer.onPropertiesChanged = self.transfer_status_handler


    def AuthorizePush(self):
        print('Authorize Push', self.path)
        transfer = self.ses_bus.get(self.BUS_NAME, self.path)
        props = transfer.GetAll(self.TRANSFER_INTERFACE)
        print(props)
        return props.get('Name')

    def Cancel(self):
        print('Authorization Cancelled')

    def Release(self):
        pass


    def server(self):
        obex_mngr = self.ses_bus.get('org.bluez.obex', '/')
        obex_mngr.onInterfacesAdded = self.iface_added_handler
        mainloop = GLib.MainLoop()
        self.ses_bus.register_object('/test/agent', self.AuthorizePush(), None)
        print('Agent created')
        agnt_mngr = ses_bus.get(self.BUS_NAME, self.PATH)
        agnt_mngr.RegisterAgent('/test/agent')
        print('Agent registered')
        try:
            mainloop.run()
        except KeyboardInterrupt:
            mainloop.quit()

Calling the class

server = servertest.opp_server('/home/pi/GAMMA/ImageData')
server.server()

Solution

  • It looks like you have mixed up DBus object paths and filesystem paths. For AuthorizePush the input is the DBus object path for the org.bluez.obex.Transfer1 interfaces but you are trying to use the filesystem path of where you want the file to be put.

    You need to have the three methods with the same parameters when you publish/export your org.bluez.obex.Agent1 interface. It might worth looking at the pydbus tutorial for Exporting own objects

    The obexd is started with the information in: /usr/share/dbus-1/services/org.bluez.obex.service which for me is:

    [D-BUS Service]
    Name=org.bluez.obex
    Exec=/usr/lib/bluetooth/obexd
    SystemdService=obex.service
    

    obexd can be started with other options as can be seen from the help:

    $ /usr/lib/bluetooth/obexd -h
    Usage:
      obexd [OPTION?]
    
    Help Options:
      -h, --help                  Show help options
    
    Application Options:
      -d, --debug=DEBUG           Enable debug information output
      -p, --plugin=NAME,...       Specify plugins to load
      -P, --noplugin=NAME,...     Specify plugins not to load
      -n, --nodetach              Run with logging in foreground
      -r, --root=PATH             Specify root folder location. Both absolute and relative can be used, but relative paths are assumed to be relative to user $HOME folder. Default $XDG_CACHE_HOME
      -S, --root-setup=SCRIPT     Root folder setup script
      -l, --symlinks              Allow symlinks leading outside of the root folder
      -c, --capability=FILE       Specify capability file, use '!' mark for scripts
      -a, --auto-accept           Automatically accept push requests
    
    

    So you could use the --root=PATH option to change the default location.

    If you did want to change the path in your code, then you need to make the value returned from AuthorizePush to have an absolute path. e.g:

        def AuthorizePush(self, path):
            transfer = ses_bus.get(BUS_NAME, path)
            props = transfer.GetAll(TRANSFER_INTERFACE)
            return '/tmp/' + props.get('Name')
    

    This will put the file in the /tmp directory.