Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I get my custom service object registered on the system bus? #53

Open
dbaba opened this issue May 27, 2020 · 7 comments
Open

How can I get my custom service object registered on the system bus? #53

dbaba opened this issue May 27, 2020 · 7 comments

Comments

@dbaba
Copy link

dbaba commented May 27, 2020

I'm looking for the way to register my custom service on D-Bus with dbus-next API. But I'm not getting it working.

What I did is:

  • Create a couple of classes inheriting dbus-next Interface
  • Invoke dbus.systemBus().export('/service/path', aboveObject) for each dbus-next Interface object to be exported

Then when I got a ProxyObject of the service path, I couldn't find anything in interfaces property of the ProxyObject. What am I missing?

# dbus.systemBus().getProxyObject(':1.2', '/service/path').then(x => console.log(x))
=>
ProxyObject {
  bus: MessageBus {
    _events: [Object: null prototype] {},
    _eventsCount: 0,
    _maxListeners: undefined,
    _builder: Builder { options: [Object] },
    _connection: EventEmitter {
      (snip)
    },
    _serial: 8,
    _methodReturnHandlers: {},
    _signals: EventEmitter {
      (snip)
    },
    _nameOwners: {
      'org.freedesktop.DBus': 'org.freedesktop.DBus',
      'org.bluez': ':1.428',
      'org.freedesktop.DBus.ObjectManager': 'org.freedesktop.DBus',
      ':1.2': ':1.2'
    },
    _methodHandlers: [ [Function] ],
    _serviceObjects: {  ======================> Exported paths are described here
      '/service/path': [ServiceObject],
      '/service/path/subpath': [ServiceObject],
      (snip) 
    }, 
  name: ':1.420',
    [Symbol(kCapture)]: false
  },
  name: ':1.2',
  path: '/service/path',
  interfaces: {},  ==========================> but no exported interfaces appear
  (snip) 
}

With node-dbus, it has dbus.registerService() to register a Service Object (not Interface). I'd say the Service Object registration function is required to register a set of D-Bus interfaces but I don't have any idea of the equivalent function in dbus-next.

@acrisci
Copy link
Member

acrisci commented May 27, 2020

I wonder if you are connecting to the right connection. The bus you've shown that has the exported objects has name :1.420 and the bus you've connected to has name :1.2.

@EricBorland
Copy link

I'm not sure if it's related but I'm trying to implement a Bluetooth GATT server using Bluez over DBus and after exporting the interface I still receive DBus Error: No object received when trying to RegisterApplication. Using the DBus python library it works by exporting the object to the bus like this dbus.service.Object.__init__(self, dbus.SystemBus(), path) but is there any equivalent function in dbus-next?

@EricBorland
Copy link

EricBorland commented May 27, 2021

Maybe it's related to this #17?

@ErwanHesry
Copy link

ErwanHesry commented Jan 31, 2023

@EricBorland Any news about this problem? I'm also trying to get a BLE Gatt server over DBus, but got the same error when trying to RegisterApplication. My interface looks good, but got the error too. Here it is:

class GattServer extends dbus.interface.Interface {
    _serialNumber: string

    constructor() {
        super('org.bluez.coregatt.manager0')
        this._serialNumber = 'MY_SERIAL_NUMBER'
    }

    get SerialNumber () {
        return this._serialNumber
    }

    set SerialNumber (value) {
        this._serialNumber = value
    }

    GetManagedObjects () {
        console.log('GetManagedObjects')
        return {}
    }

    Echo (what) {
        return what;
    }
}

GattServer.configureMembers({
    properties: {
        SerialNumber: {
            signature: 's'
        }
    },
    methods: {
        GetManagedObjects: {
            inSignature: '',
            outSignature: 'a{oa{sa{sv}}}'
        },
        Echo: {
            inSignature: 'v',
            outSignature: 'v'
        },
    }
})

@EricBorland
Copy link

EricBorland commented Feb 1, 2023

Hey, it's been a while but I ended up getting it working. I don't remember what exactly I was doing wrong or what I did to fix it, but the final code looked like something similar to this:

import dbus, { Message, Variant } from 'dbus-next';

const {
  ACCESS_READ,
  INTERFACES,
  Interface,
  method,
  property,
} = dbus.interface;

export default class GattApplication extends Interface {
  @property({ signature: 's', access: ACCESS_READ })
  path = '/';

  services = [];

  constructor(bus, services) {
    super(INTERFACES.GATT_APPLICATION);
    // Properties definition
    this._bus = bus;

    // Exporting interface to bus
    this._bus.exportInterface(this.path, this);

    // Initializing services
    this.addServices(services);

    // Exporting GetManagedObjects to ObjectManager
    this._bus.addMethod('GetManagedObjects', INTERFACES.OBJECT_MANAGER, this.path, 'a{oa{sa{sv}}}', this.GetManagedObjects.bind(this));
  }

  addService(service) {
    this.services.push(service);
    this._bus.exportInterface(service.getPath(), service);
  }

  addServices(services) {
    services.forEach(service => this.addService(service));
  }

  GetManagedObjects() {
    const response = {};
    this.services.forEach(service => {
      response[service.getPath()] = service.getProperties();
      service.getCharacteristics().forEach(characteristic => {
        response[characteristic.getPath()] = characteristic.getProperties();
        characteristic.getDescriptors().forEach(descriptor => {
          response[descriptor.getPath()] = descriptor.getProperties();
        });
      });
    });

    return response;
  }
}

And used like this:

const { agentManager, gattManager, advertisingManager } = await this.bus.initManagers(['agent', 'gatt', 'advertising']);
const app = new Gatt.Application(this.bus, services);
await gattManager.RegisterApplication(app.path, {});
console.log('Application successfully registered in GATT');

Where this.bus was an abstraction on top of dbus-next adding some extra methods like the initManagers that only loops over the array and initializes each manager using the initManager method from dbus-next...
Let me know if that helps.

Cheers

@ErwanHesry
Copy link

Thanks a lot @EricBorland, it helped me a lot.
I do have your abstraction on top of dbus-next, but I managed to get something that works. Well, for the first part: creating the application and send it through dbus.
The only thing I had to change is the addMethod you have in your abstraction. Here is my GattApplication class:

class GattApplication extends Interface {
  _bus:dbus.MessageBus
  path = Constants.Gatt.GATT_APPLICATION_PATH;
  name = Constants.Gatt.GATT_APPLICATION_NAME;

  services = [];

  constructor(bus, services) {
    super(Constants.Gatt.GATT_SERVICE_IFACE);
    // Properties definition
    this._bus = bus;

    // Exporting interface to bus
    this._bus.export(this.path, this);

    // Initializing services
    this.addServices(services);

    // Exporting GetManagedObjects to ObjectManager
    this.addMethod('GetManagedObjects', Constants.Gatt.DBUS_OM_IFACE, 'a{oa{sa{sv}}}', this.GetManagedObjects.bind(this));
  }

  addMethod = (name, iface, signature, cb) => {
    this._bus.addMethodHandler((msg) => {
        console.log(`CALL ${msg.member} on ${msg.path}`)
        if (msg.interface === iface && msg.path === this.path && msg.member === name) {
            this._bus.send(new dbus.Message({
                type: dbus.MessageType.METHOD_RETURN,
                replySerial: msg.serial,
                destination: msg.sender,
                signature,
                body: [cb()]
            }))
            return true
        }
        return false
    })
  }

  addService(service) {
    this.services.push(service);
    this._bus.export(service.getPath(), service);
  }

  addServices(services) {
    services.forEach(service => this.addService(service));
  }

  GetManagedObjects() {
    const response = {};
    this.services.forEach(service => {
      response[service.getPath()] = service.getProperties();
      service.getCharacteristics().forEach(characteristic => {
        response[characteristic.getPath()] = characteristic.getProperties();
        characteristic.getDescriptors().forEach(descriptor => {
          response[descriptor.getPath()] = descriptor.getProperties();
        });
      });
    });

    return response;
  }

With that GattApplication, I can registerApplication with my gattManager. However, as I don't have any service yet, Bluez returns an error No object received (which is normal I think). But now, the GetManagedObjects works (previously, I was seeing an error message that the method does not exists on my application).

Now I have to create services. Any advice on that part? I have found some example in the Python example. I'll try to convert that Python code into JS.

@ErwanHesry
Copy link

Ok, so that was definitely the lack of service. I created a dummy service emulating a battery service (uuid 180f) containing a characteristic emulating battery level (uuid 2a19). All based on the format of the application above.
Result: it works!!!

I then added an advertisement service which is sent through DBus to the Bluez LEAdvertisingManager. And voilà, my device is now discoverable.

Again, thanks a lot for your help 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants