DBus overview

Dbus

Links:

Dbus server in python

Depencies:

Most probably they are already installed, the base SailfishOS SDK already has them.

DBus activation .service file

Note: If you are planning to ship your app to harbour, probably you shouldn’t be reading this. Harbour doesn’t allow custom DBus activations.

Source: Introduction To Dbus - Activation - Freedesktop.org

A way of offering services on the bus is instructing the bus daemon to start (or activate) clients automatically when needed. Activation of a client can be triggered in two ways, both keyed by a well-known bus name that the activated client must obtain:

To create a client that can be activated, describe it in a service file. A service file looks like a human-readable .ini file, line-based and encoded in UTF-8. Its name must always end in .service.

These .service files are located in /usr/share/dbus-1/services/. Names of the files are arbitraty, so long as it ends with .service.

Example:

[D-BUS Service]
Name=harbour.buatsap.Service
Exec=/usr/bin/invoker --type=generic -s /usr/bin/example-service.py
User=nemo

If many services are provided by the same client, they have to be separated by semicolons ;. e.g: Name=harbour.buatsap.Service;harbour.buatsap.Service2.

Example

Download example-service.zip for sources.

The example is writen in python2

import gi.repository.GObject as gobject

import dbus
import dbus.service
import dbus.mainloop.glib

class Buatsap(dbus.service.Object):
    # Interface, in_signature and out_signature are i/o params, they must be valid DBus types
    @dbus.service.method("harbour.buatsap.Server",
                         in_signature='ss', out_signature='b')
    # Method name is the Method name of the interface
    def Login(self, user, password):
        print ("Trying to login in, user: %s pass: %s" % (user, password))
        return True;

    @dbus.service.method("harbour.buatsap.Server",
                         in_signature='', out_signature='')
    def Exit(self):
        mainloop.quit()

class Chats(dbus.service.Object):

    @dbus.service.method("harbour.buatsap.Chats",
                         in_signature='', out_signature='as')
    def GetChats(self):
        return ["chat1", "chat2", "chat3"];

    @dbus.service.method("harbour.buatsap.Chats",
                         in_signature='', out_signature='')
    def EmitNewChat(self):
        self.NewChat('1', 'last message');

    @dbus.service.signal("harbour.buatsap.Chats")
    def NewChat(self, chatId, lastMessage):
        pass

if __name__ == '__main__':
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    session_bus = dbus.SessionBus()
    # Bus name
    name = dbus.service.BusName("harbour.buatsap.Service", session_bus)
    # Object name or path
    buatsap = Buatsap(session_bus, '/')
    chats = Chats(session_bus, '/chats')

    mainloop = gobject.MainLoop()
    print "Running example service."
    print usage
    mainloop.run()

Each method has a certain signature ( in_signature for input and out_signature for return values) representing the types of its params and output.

In addition, there are container types:

To register signals, the @dbus.service.signal("interface") decorator is used. The method name will be the signal name, and parameters receibed are the parameters send with the signal. The method doesn’t need a body. In order to send the signal, one must call the method. (TODO: Check where type definition of params has to be done)

For information read Data types from the python dbus tutorial.

DBus client in QML

In order to use the Nemo DBus plugin, we’ll have to install the package nemo-qml-plugin-dbus-qt5 (in the sdk it is allready installed).

Note: Remeber that now-a-days (jan/2015) no nemo plugin are allowed in Harbour.

To make DBus calls the DBusInterface object can be used. This QML Object has the followin properties:

Methods:

Qmlplugindump of org.nemomobile.dbus: Better SailfishOS QML types, Coderus’s repo

Example

Download dbus-client-qml.zip for sources.

import QtQuick 2.0
import Sailfish.Silica 1.0
import org.nemomobile.dbus 2.0

ApplicationWindow {
    initialPage: Component {
        Page {
            DBusInterface {
                id: dbusServer
                service: 'harbour.buatsap.Service'
                iface: 'harbour.buatsap.Server'
                path: '/'
            }

            DBusInterface {
                id: dbusChats
                service: 'harbour.buatsap.Service'
                iface: 'harbour.buatsap.Chats'
                path: '/chats'
                signalsEnabled: true

                function newChat(chatId, lastMessage){
                    console.log("chatId: "+chatId+" lastMessage: "+lastMessage)
                    messageModel.append({
                      id: chatId,
                      message: lastMessage
                    })
                }
            }

            SilicaFlickable {
                anchors.fill: parent
                contentHeight: column.height

                Column {
                    id: column
                    width: parent.width
                    height: childRect.height

                    PageHeader { title: "Qml DBus Client" }

                    TextField {
                        id: username
                        width: parent.width
                        label: "Username"
                        placeholderText: "Username"
                        focus: true
                        EnterKey.onClicked: {
                            password.focus = true;
                        }
                    }

                    TextField {
                        id: password
                        width: parent.width
                        inputMethodHints: Qt.ImhNoPredictiveText
                        echoMode: TextInput.Password
                        label: "Password"
                        placeholderText: "Enter password"
                        EnterKey.onClicked: {
                            dbusServer.typedCall('Login', [
                                                  { type: "s", value: username.text},
                                                  { type: "s", value: password.text}
                                               ], function(r){
                                console.log(r)
                                if(r)
                                    result.text = "Login succesfull!"
                                else
                                    result.text = "Bad login"
                            })
                        }
                    }

                    TextField {
                        id: result
                        width: parent.width
                        readOnly: true
                        label: ""
                        text: "Please login"
                    }

                    Repeater {
                        id: listView
                        width: parent.width
                        model: ListModel { id: messageModel }
                        height: childrenRect.height
                        delegate: BackgroundItem {
                            width: listView.width
                            Label {
                                text: "#"+ model.id +" "+model.message
                                color: highlighted ? Theme.highlightColor : Theme.primaryColor
                                anchors.verticalCenter: parent.verticalCenter
                                x: Theme.paddingLarge
                            }
                        }
                    }

                    Button {
                        text: "Test Signal"
                        onClicked: {
                            dbusChats.call("EmitNewChat", []);
                        }
                        anchors.horizontalCenter: parent.horizontalCenter
                    }
                }
            }
        }
    }
}