DBus overview
04 Jan 2015Dbus
Links:
- Python DBus tutorial
- Nemo qml plugin dbus :: Read the docs
- Nemo qml plugin dbus :: github
- dbus-python/examples
- Tinkering with DBus on Jola
Dbus server in python
Depencies:
dbus-python
:pkcon install dbus-python
pygobject2
:pkcon install pygobject2
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:
- Through an explicit request to the object representing the bus itself.
- By invoking a method on an object in the context of the client’s well-known bus name. The latter can be inhibited through an option in the method invocation message. Some bindings may try to activate an appropriate client when you create a proxy on a well-known bus name that is not currently in use; others may defer this until you use the proxy to invoke the method. The difference can matter if you listen for a signal coming from an object: if the client that should provide the object is not actually running, you could wait in vain!
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.
s
: represents a Dbus Stringb
: booleani
: signed 32 integer- etc
In addition, there are container types:
- Arrays: signature is
ax
wherex
represent some other type. (e.g.:as
is a array of strings). (It is possible to send arrays of structs:a(xy)
, wherex
andy
are types, in python, it will be represented as a tuple) - Dicts: signature is
a{xy}
wherex
represents the signature of the keys andy
of the values.
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:
service
: (string) DBus service nameiface
: (string) DBus interfacepath
: (string) DBus object pathsignalEnabled
: (bool) ifsignalsEnabled
is set to true, signals of the destination object will be connected to functions on the object that have the same name. Example:MySignal
will call themySignal
method.
Methods:
void call(string method, var arguments)
: Call a D-Bus method with the namemethod
on the object with arguments as argument list. For a function with no arguments, pass in[]
(empty array).void typedCall(string method, var arguments, var callback=undefined)
: Call a D-Bus method with the namemethod
on the object witharguments
as argument list. When the function returns, callcallback
with a single argument that is the return value. Thecallback
argument is optional, if set toundefined
(the default), the return value will be discarded. The argument list is anArray
ofObject
s with two properties:value
andtype
. Thetype
must be a type signature as explained before. Check this for the allowed types.
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
}
}
}
}
}
}