D-Bus is a message bus that Linux systems use in order to make programs communicate with each other or with the system itself. It allows applications to integrate amongst themselves using well-defined interfaces. This allows each application to provide services that can be used by others, sort of like adding API’s to your programs.

In this article, we’re going to write a small D-Bus service and a client to consume it. Perhaps some people see D-Bus as an ancient and scary relic of the past, but hopefully after reading this you will find it an approachable topic.

D-Bus Service for Getting the Current Time

First of all, let’s import dbus and time. This shouldn’t be a big surprise since we’re writing a D-Bus service about time.

import dbus
import dbus.service
import time

Next, we need to create a D-Bus service class. This is done by inheriting from dbus.service.Object. The functions we put into this class will be automatically exported as D-Bus methods.

class Time(dbus.service.Object):
    def __init__(self):
        self.bus = dbus.SessionBus()
        name = dbus.service.BusName('com.gkbrk.Time', bus=self.bus)
        super().__init__(name, '/Time')

This part just gets a connection to the Session Bus and sets the name of our service. Let’s write our function that returns the current time.

    @dbus.service.method('com.gkbrk.Time', out_signature='s')
    def CurrentTime(self):
        """Use strftime to return a formatted timestamp
        that looks like 23-02-2018 06:57:04."""
        
        formatter = '%d-%m-%Y %H:%M:%S'
        return time.strftime(formatter)

This method is a small wrapper around the strftime function from the standard library, which formats the current time into a human readable string.

I want to talk about the decorator for a bit. The out_signature = ‘s’ part basically tells D-Bus that the function returns a string. If we had made it out_signature = ‘ss’, that would mean we are returning two strings from our function, which is a tuple of strings in Python. Similarly, putting in_signature there would let us denote the function arguments.

Running the Service

In order to run the service, we’re going to need a bit more boilerplate. The code below creates the GLib main loop, registers our service and runs the loop. The main loop will stop the program from terminating, so we can respond to calls to our service.

if __name__ == '__main__':
    import dbus.mainloop.glib
    from gi.repository import GLib
    
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    
    loop = GLib.MainLoop()
    object = Time()
    loop.run()

Writing a Client to Access Our Service

Writing a client is a lot easier than writing the service. Again, we need to import dbus and connect to the Session Bus for this.

import dbus

bus = dbus.SessionBus()

After this, we will create our Time object again, using the com.gkbrk.Time address we created before.

time = bus.get_object('com.gkbrk.Time', '/Time')

Using this object, we can call any method we defined on the service and use the result. Let’s print the current time that we get from our D-Bus service.

curr = time.CurrentTime()
print('The current time is', curr)

If there are no problems; if you run the service we wrote in a terminal and run the client from another terminal, you should get the time printed on your console.

Now, you should be able to create simple services that are useful to you. If you come up with any interesting services, I would love to hear about them via email or Twitter.


Thank you for reading my article. You can find related information in these sources.