Advanced bluetooth code example¶
Proxy objects D-Bus, Qt adaptor¶
By using adaptors you can access an interface via an object instead of calling methods via QDbusInterace class. You create a QDBusAbstractAdaptor object which is used for relaying calls into the real object and the signals from it.
Using a Q_CLASSINFO macro you define the D-BUS interface it is implementing.
Some times you can get the interface description from introspecting the D-BUS. You can also write it yourself or if it is a known interface you can usually find an XML description of it.
Look at the interface description of org.bluez.Agent1
How to implement custom UI for displaying pairing information.¶
Suppose you would like to show a UI with a pincode to verify when you activate pairing on a unit.
The steps involved would be.
Look at the D-Bus api for your version of Bluez. The agent-api is what we are looking for. Agent-api
Try to get hold of the XML describing the org.bluez.Agent1 interface org.bluez.Agent1
Use the tool that comes with Qt called qdbusxml2cpp. In this example having the xml file in the same folder as the tool. Run the command
qdbusxml2cpp org.bluez.Agent1.xml -a bluezagent1adaptor
Import the generated class bluezagent1adaptor to your Qt project.
Do your own implementation in another class (Below named BluezPairingAgent
Register your implemented object with a call to the org.bluez.AgentManager1 interface
After completing the above steps you will automatically get the proper callbacks to your object that implements the interface.
Registering your implementation with org.bluez.AgentManager1¶
From your setup code you will need something very similar to this code below.
// Setup and register bluetooth pairing agent
m_agent = new BluezPairingAgent();
// Give your agent an object path
QString objectPath("/pairing/agent");
m_agentAdaptor = new Agent1Adaptor(m_agent);
// For your object to be visible on d-bus, call the registerObject method
if (QDBusConnection::systemBus().registerObject(objectPath, m_agent)) {
// Get the interface reference for the org.bluez.AgentManager1
QDBusInterface agentManager("org.bluez", "/org/bluez", "org.bluez.AgentManager1", QDBusConnection::systemBus());
if (!agentManager.isValid())
qWarning() << Q_FUNC_INFO << agentManager.lastError().message();
else {
QVariant agentPath{ QVariant::fromValue(QDBusObjectPath(objectPath)) };
// Call the RegisterAgent method with the correct arguments, since we don't have a keyboard connected we submit DisplayOnly
QDBusMessage msg{ agentManager.call("RegisterAgent", agentPath, "DisplayOnly") };
if (msg.type() == QDBusMessage::ErrorMessage)
qWarning() << Q_FUNC_INFO << msg.errorMessage();
else {
msg = agentManager.call("RequestDefaultAgent", agentPath);
if (msg.type() == QDBusMessage::ErrorMessage)
qWarning() << Q_FUNC_INFO << msg.errorMessage();
}
}
}
Now when someone initiates pairing bluez will call our agent object!
It is now up to you to write a good looking UI for it. Below you can see the generated class and a simple implementation.
Agent1Adaptor.h (Generated via qdbusxml2cpp)¶
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp org.bluez.Agent1.xml -a bluezagent1adaptor
*
* qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#ifndef BLUEZAGENT1ADAPTOR_H
#define BLUEZAGENT1ADAPTOR_H
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
QT_BEGIN_NAMESPACE
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
QT_END_NAMESPACE
/*
* Adaptor class for interface org.bluez.Agent1
*/
class Agent1Adaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.bluez.Agent1")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"org.bluez.Agent1\">\n"
" <method name=\"Release\"/>\n"
" <method name=\"RequestPinCode\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" <arg direction=\"out\" type=\"s\"/>\n"
" </method>\n"
" <method name=\"DisplayPinCode\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" <arg direction=\"in\" type=\"s\"/>\n"
" </method>\n"
" <method name=\"RequestPasskey\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" <arg direction=\"out\" type=\"u\"/>\n"
" </method>\n"
" <method name=\"DisplayPasskey\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" <arg direction=\"in\" type=\"u\"/>\n"
" <arg direction=\"in\" type=\"q\"/>\n"
" </method>\n"
" <method name=\"RequestConfirmation\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" <arg direction=\"in\" type=\"u\"/>\n"
" </method>\n"
" <method name=\"RequestAuthorization\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" </method>\n"
" <method name=\"AuthorizeService\">\n"
" <arg direction=\"in\" type=\"o\"/>\n"
" <arg direction=\"in\" type=\"s\"/>\n"
" </method>\n"
" <method name=\"Cancel\"/>\n"
" </interface>\n"
"")
public:
Agent1Adaptor(QObject *parent);
virtual ~Agent1Adaptor();
public: // PROPERTIES
public Q_SLOTS: // METHODS
void AuthorizeService(const QDBusObjectPath &in0, const QString &in1);
void Cancel();
void DisplayPasskey(const QDBusObjectPath &in0, uint in1, ushort in2);
void DisplayPinCode(const QDBusObjectPath &in0, const QString &in1);
void Release();
void RequestAuthorization(const QDBusObjectPath &in0);
void RequestConfirmation(const QDBusObjectPath &in0, uint in1);
uint RequestPasskey(const QDBusObjectPath &in0);
QString RequestPinCode(const QDBusObjectPath &in0);
Q_SIGNALS: // SIGNALS
};
#endif
Agent1Adaptor.cpp (Generated via qdbusxml2cpp)¶
/*
* This file was generated by qdbusxml2cpp version 0.8
* Command line was: qdbusxml2cpp org.bluez.Agent1.xml -a bluezagent1adaptor
*
* qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#include "bluezagent1adaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class Agent1Adaptor
*/
Agent1Adaptor::Agent1Adaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
Agent1Adaptor::~Agent1Adaptor()
{
// destructor
}
void Agent1Adaptor::AuthorizeService(const QDBusObjectPath &in0, const QString &in1)
{
// handle method call org.bluez.Agent1.AuthorizeService
QMetaObject::invokeMethod(parent(), "AuthorizeService", Q_ARG(QDBusObjectPath, in0), Q_ARG(QString, in1));
}
void Agent1Adaptor::Cancel()
{
// handle method call org.bluez.Agent1.Cancel
QMetaObject::invokeMethod(parent(), "Cancel");
}
void Agent1Adaptor::DisplayPasskey(const QDBusObjectPath &in0, uint in1, ushort in2)
{
// handle method call org.bluez.Agent1.DisplayPasskey
QMetaObject::invokeMethod(parent(), "DisplayPasskey", Q_ARG(QDBusObjectPath, in0), Q_ARG(uint, in1), Q_ARG(ushort, in2));
}
void Agent1Adaptor::DisplayPinCode(const QDBusObjectPath &in0, const QString &in1)
{
// handle method call org.bluez.Agent1.DisplayPinCode
QMetaObject::invokeMethod(parent(), "DisplayPinCode", Q_ARG(QDBusObjectPath, in0), Q_ARG(QString, in1));
}
void Agent1Adaptor::Release()
{
// handle method call org.bluez.Agent1.Release
QMetaObject::invokeMethod(parent(), "Release");
}
void Agent1Adaptor::RequestAuthorization(const QDBusObjectPath &in0)
{
// handle method call org.bluez.Agent1.RequestAuthorization
QMetaObject::invokeMethod(parent(), "RequestAuthorization", Q_ARG(QDBusObjectPath, in0));
}
void Agent1Adaptor::RequestConfirmation(const QDBusObjectPath &in0, uint in1)
{
// handle method call org.bluez.Agent1.RequestConfirmation
QMetaObject::invokeMethod(parent(), "RequestConfirmation", Q_ARG(QDBusObjectPath, in0), Q_ARG(uint, in1));
}
uint Agent1Adaptor::RequestPasskey(const QDBusObjectPath &in0)
{
// handle method call org.bluez.Agent1.RequestPasskey
uint out0;
QMetaObject::invokeMethod(parent(), "RequestPasskey", Q_RETURN_ARG(uint, out0), Q_ARG(QDBusObjectPath, in0));
return out0;
}
QString Agent1Adaptor::RequestPinCode(const QDBusObjectPath &in0)
{
// handle method call org.bluez.Agent1.RequestPinCode
QString out0;
QMetaObject::invokeMethod(parent(), "RequestPinCode", Q_RETURN_ARG(QString, out0), Q_ARG(QDBusObjectPath, in0));
return out0;
}
BluezPairingAgent.h (Our implementation)¶
#ifndef BLUEZPAIRINGAGENT_H
#define BLUEZPAIRINGAGENT_H
#include <QObject>
#include <QtDBus>
/*
* This class implements the interfaces for the generated D-Bus
* interface
*
* The interface was generated from org.bluez.Agent1.xml
*/
class BluezPairingAgent : public QObject
{
Q_OBJECT
public:
explicit BluezPairingAgent(QObject *parent = nullptr);
public slots:
void Release() const;
QString RequestPinCode(const QDBusObjectPath &device) const;
void DisplayPinCode(const QDBusObjectPath &device, const QString &pincode);
uint RequestPasskey(const QDBusObjectPath &device) const;
void DisplayPasskey(const QDBusObjectPath &device, uint passkey, ushort entered);
void RequestConfirmation(const QDBusObjectPath &device, uint passkey);
void RequestAuthorization(const QDBusObjectPath &device) const;
void AuthorizeService(const QDBusObjectPath &device, const QString &uuid) const;
void Cancel() const;
signals:
void showCode(const uint &code, const ushort &entered);
void showVerificationCode(const uint &code);
void showPinCode(const QString &code);
};
#endif // BLUEZPAIRINGAGENT_H
BluezPairingAgent.cpp (Our implementation)¶
#include "bluezpairingagent.h"
#include <QDebug>
#include <iostream>
/*
* This class implements the interfaces for the generated D-Bus
* interface
*
* The interface was generated from org.bluez.Agent1.xml
*/
BluezPairingAgent::BluezPairingAgent(QObject *parent)
: QObject(parent) {
}
void BluezPairingAgent::Release() const {
//NOTE: Not needed yet, but must be here as it is expected by bluez.
// This call is for unregistering agents, which we never do.
}
QString BluezPairingAgent::RequestPinCode(const QDBusObjectPath &device) const {
Q_UNUSED(device)
return "1234";
}
void BluezPairingAgent::DisplayPinCode(const QDBusObjectPath &device, const QString &pincode) {
Q_UNUSED(device)
emit showPinCode(pincode);
}
uint BluezPairingAgent::RequestPasskey(const QDBusObjectPath &device) const {
Q_UNUSED(device)
return 1234;
}
void BluezPairingAgent::DisplayPasskey(const QDBusObjectPath &device, uint passkey, ushort entered) {
Q_UNUSED(device)
emit showCode(passkey, entered);
}
void BluezPairingAgent::RequestConfirmation(const QDBusObjectPath &device, uint passkey) {
Q_UNUSED(device)
emit showVerificationCode(passkey);
}
void BluezPairingAgent::RequestAuthorization(const QDBusObjectPath &device) const {
Q_UNUSED(device)
//NOTE: Not needed yet, but must be here as it is expected by bluez
}
void BluezPairingAgent::AuthorizeService(const QDBusObjectPath &device, const QString &uuid) const {
Q_UNUSED(device)
Q_UNUSED(uuid)
//NOTE: Not needed yet, but must be here as it is expected by bluez
}
void BluezPairingAgent::Cancel() const {
//NOTE: Not needed, but must be here as it is expected by bluez
}
Interface description of org.bluez.Agent1¶
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.bluez.Agent1">
<method name="Release" />
<method name="RequestPinCode">
<arg direction="in" type="o" />
<arg direction="out" type="s" />
</method>
<method name="DisplayPinCode">
<arg direction="in" type="o" />
<arg direction="in" type="s" />
</method>
<method name="RequestPasskey">
<arg direction="in" type="o" />
<arg direction="out" type="u" />
</method>
<method name="DisplayPasskey">
<arg direction="in" type="o" />
<arg direction="in" type="u" />
<arg direction="in" type="q" />
</method>
<method name="RequestConfirmation">
<arg direction="in" type="o" />
<arg direction="in" type="u" />
</method>
<method name="RequestAuthorization">
<arg direction="in" type="o" />
</method>
<method name="AuthorizeService">
<arg direction="in" type="o" />
<arg direction="in" type="s" />
</method>
<method name="Cancel" />
</interface>
</node>