summaryrefslogtreecommitdiffstats
path: root/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp')
-rw-r--r--tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp1799
1 files changed, 1799 insertions, 0 deletions
diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
new file mode 100644
index 00000000..cb5bbb07
--- /dev/null
+++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
@@ -0,0 +1,1799 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QBluetoothLocalDevice>
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QBluetoothUuid>
+#include <QLowEnergyController>
+#include <QLowEnergyCharacteristic>
+
+#include <QDebug>
+
+/*!
+ This test requires a TI sensor tag with Firmware version: 1.5 (Oct 23 2013).
+ Since revision updates change user strings and even shift handles around
+ other versions than the above are unlikely to succeed. Please update the
+ sensor tag before continuing.
+
+ The TI sensor can be updated using the related iOS app. The Android version
+ doesn't seem to update at this point in time.
+ */
+
+QT_USE_NAMESPACE
+
+class tst_QLowEnergyController : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QLowEnergyController();
+ ~tst_QLowEnergyController();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void tst_connect();
+ void tst_concurrentDiscovery();
+ void tst_defaultBehavior();
+ void tst_writeCharacteristic();
+ void tst_writeDescriptor();
+
+private:
+ void verifyServiceProperties(const QLowEnergyService *info);
+
+ QBluetoothDeviceDiscoveryAgent *devAgent;
+ QBluetoothAddress remoteDevice;
+ QList<QBluetoothUuid> foundServices;
+};
+
+Q_DECLARE_METATYPE(QLowEnergyCharacteristic)
+Q_DECLARE_METATYPE(QLowEnergyDescriptor)
+Q_DECLARE_METATYPE(QLowEnergyService::ServiceError)
+
+tst_QLowEnergyController::tst_QLowEnergyController()
+{
+ qRegisterMetaType<QLowEnergyCharacteristic>();
+ qRegisterMetaType<QLowEnergyDescriptor>();
+
+ //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ const QString remote = qgetenv("BT_TEST_DEVICE");
+ if (!remote.isEmpty()) {
+ remoteDevice = QBluetoothAddress(remote);
+ qWarning() << "Using remote device " << remote << " for testing. Ensure that the device is discoverable for pairing requests";
+ } else {
+ qWarning() << "Not using any remote device for testing. Set BT_TEST_DEVICE env to run manual tests involving a remote device";
+ }
+}
+
+tst_QLowEnergyController::~tst_QLowEnergyController()
+{
+
+}
+
+void tst_QLowEnergyController::initTestCase()
+{
+ if (remoteDevice.isNull()
+ || QBluetoothLocalDevice::allDevices().isEmpty()) {
+ qWarning("No remote device or local adapter found.");
+ return;
+ }
+
+ devAgent = new QBluetoothDeviceDiscoveryAgent(this);
+
+ QSignalSpy finishedSpy(devAgent, SIGNAL(finished()));
+ // there should be no changes yet
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(finishedSpy.isEmpty());
+
+ bool deviceFound = false;
+ devAgent->start();
+ QTRY_VERIFY_WITH_TIMEOUT(finishedSpy.count() > 0, 30000);
+ foreach (const QBluetoothDeviceInfo &info, devAgent->discoveredDevices()) {
+ if (info.address() == remoteDevice) {
+ deviceFound = true;
+ break;
+ }
+ }
+
+ QVERIFY2(deviceFound, "Cannot find remote device.");
+
+ // These are the services exported by the TI SensorTag
+ foundServices << QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000ccc0-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000"));
+}
+
+void tst_QLowEnergyController::cleanupTestCase()
+{
+
+}
+
+void tst_QLowEnergyController::tst_connect()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ const QBluetoothAddress localAdapter = localAdapters.at(0).address();
+ QLowEnergyController control(remoteDevice);
+ QSignalSpy connectedSpy(&control, SIGNAL(connected()));
+ QSignalSpy disconnectedSpy(&control, SIGNAL(disconnected()));
+
+ QCOMPARE(control.localAddress(), localAdapter);
+ QVERIFY(!control.localAddress().isNull());
+ QCOMPARE(control.remoteAddress(), remoteDevice);
+ QCOMPARE(control.state(), QLowEnergyController::UnconnectedState);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+ QVERIFY(control.errorString().isEmpty());
+ QCOMPARE(disconnectedSpy.count(), 0);
+ QCOMPARE(connectedSpy.count(), 0);
+ QVERIFY(control.services().isEmpty());
+
+ bool wasError = false;
+ control.connectToDevice();
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 10000);
+
+ QCOMPARE(disconnectedSpy.count(), 0);
+ if (control.error() != QLowEnergyController::NoError) {
+ //error during connect
+ QCOMPARE(connectedSpy.count(), 0);
+ QCOMPARE(control.state(), QLowEnergyController::UnconnectedState);
+ wasError = true;
+ } else if (control.state() == QLowEnergyController::ConnectingState) {
+ //timeout
+ QCOMPARE(connectedSpy.count(), 0);
+ QVERIFY(control.errorString().isEmpty());
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+ QVERIFY(control.services().isEmpty());
+ QSKIP("Connection to LE device cannot be established. Skipping test.");
+ return;
+ } else {
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ QCOMPARE(connectedSpy.count(), 1);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+ QVERIFY(control.errorString().isEmpty());
+ }
+
+ QVERIFY(control.services().isEmpty());
+
+ QList<QLowEnergyService *> savedReferences;
+
+ if (!wasError) {
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ QSignalSpy serviceFoundSpy(&control, SIGNAL(serviceDiscovered(QBluetoothUuid)));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ QVERIFY(!serviceFoundSpy.isEmpty());
+ QVERIFY(serviceFoundSpy.count() >= foundServices.count());
+ QVERIFY(!serviceFoundSpy.isEmpty());
+ QList<QBluetoothUuid> listing;
+ for (int i = 0; i < serviceFoundSpy.count(); i++) {
+ const QVariant v = serviceFoundSpy[i].at(0);
+ listing.append(v.value<QBluetoothUuid>());
+ }
+
+ foreach (const QBluetoothUuid &uuid, foundServices) {
+ QVERIFY2(listing.contains(uuid),
+ uuid.toString().toLatin1());
+
+ QLowEnergyService *service = control.createServiceObject(uuid);
+ QVERIFY2(service, uuid.toString().toLatin1());
+ savedReferences.append(service);
+ QCOMPARE(service->type(), QLowEnergyService::PrimaryService);
+ QCOMPARE(service->state(), QLowEnergyService::DiscoveryRequired);
+ }
+
+ // unrelated uuids don't return valid service object
+ // invalid service uuid
+ QVERIFY(!control.createServiceObject(QBluetoothUuid()));
+ // some random uuid
+ QVERIFY(!control.createServiceObject(QBluetoothUuid(QBluetoothUuid::DeviceName)));
+
+ // initiate characteristic discovery
+ foreach (QLowEnergyService *service, savedReferences) {
+ qDebug() << "Discoverying" << service->serviceUuid();
+ QSignalSpy stateSpy(service,
+ SIGNAL(stateChanged(QLowEnergyService::ServiceState)));
+ QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError)));
+ service->discoverDetails();
+
+ QTRY_VERIFY_WITH_TIMEOUT(
+ service->state() == QLowEnergyService::ServiceDiscovered, 10000);
+
+ QCOMPARE(errorSpy.count(), 0); //no error
+ QCOMPARE(stateSpy.count(), 2); //
+
+ verifyServiceProperties(service);
+ }
+
+ // ensure that related service objects share same state
+ foreach (QLowEnergyService* originalService, savedReferences) {
+ QLowEnergyService *newService = control.createServiceObject(
+ originalService->serviceUuid());
+ QVERIFY(newService);
+ QCOMPARE(newService->state(), QLowEnergyService::ServiceDiscovered);
+ delete newService;
+ }
+ }
+
+ // Finish off
+ control.disconnectFromDevice();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ control.state() == QLowEnergyController::UnconnectedState,
+ 10000);
+
+ if (wasError) {
+ QCOMPARE(disconnectedSpy.count(), 0);
+ } else {
+ QCOMPARE(disconnectedSpy.count(), 1);
+ // after disconnect all service references must be invalid
+ foreach (const QLowEnergyService *entry, savedReferences) {
+ const QBluetoothUuid &uuid = entry->serviceUuid();
+ QVERIFY2(entry->state() == QLowEnergyService::InvalidService,
+ uuid.toString().toLatin1());
+
+ //after disconnect all related characteristics and descriptors are invalid
+ QList<QLowEnergyCharacteristic> chars = entry->characteristics();
+ for (int i = 0; i < chars.count(); i++) {
+ QCOMPARE(chars.at(i).isValid(), false);
+ QList<QLowEnergyDescriptor> descriptors = chars[i].descriptors();
+ for (int j = 0; j < descriptors.count(); j++)
+ QCOMPARE(descriptors[j].isValid(), false);
+ }
+ }
+ }
+
+ qDeleteAll(savedReferences);
+ savedReferences.clear();
+}
+
+void tst_QLowEnergyController::tst_concurrentDiscovery()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ // quick setup - more elaborate test is done by connectNew()
+ QLowEnergyController control(remoteDevice);
+ QCOMPARE(control.state(), QLowEnergyController::UnconnectedState);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ if (control.state() == QLowEnergyController::ConnectingState
+ || control.error() != QLowEnergyController::NoError) {
+ // default BTLE backend forever hangs in ConnectingState
+ QSKIP("Cannot connect to remote device");
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+
+ // 2. new controller to same device fails
+ {
+ QLowEnergyController control2(remoteDevice);
+ control2.connectToDevice();
+ {
+ QTRY_IMPL(control2.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ QVERIFY(control2.error() != QLowEnergyController::NoError);
+ }
+
+ /* We are testing that we can run service discovery on the same device
+ * for multiple services at the same time.
+ * */
+
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ // pick MAX_SERVICES_SAME_TIME_ACCESS services
+ // and discover them at the same time
+#define MAX_SERVICES_SAME_TIME_ACCESS 3
+ QLowEnergyService *services[MAX_SERVICES_SAME_TIME_ACCESS];
+
+ QVERIFY(control.services().count() >= MAX_SERVICES_SAME_TIME_ACCESS);
+
+ QList<QBluetoothUuid> uuids = control.services();
+
+ // initialize services
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ services[i] = control.createServiceObject(uuids.at(i), this);
+ QVERIFY(services[i]);
+ }
+
+ // start complete discovery
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++)
+ services[i]->discoverDetails();
+
+ // wait until discovery done
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ qWarning() << "Waiting for" << i << services[i]->serviceUuid();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ services[i]->state() == QLowEnergyService::ServiceDiscovered,
+ 30000);
+ }
+
+ // verify discovered services
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ verifyServiceProperties(services[i]);
+
+ QVERIFY(!services[i]->contains(QLowEnergyCharacteristic()));
+ QVERIFY(!services[i]->contains(QLowEnergyDescriptor()));
+ }
+
+ control.disconnectFromDevice();
+ QTRY_VERIFY_WITH_TIMEOUT(control.state() == QLowEnergyController::UnconnectedState,
+ 30000);
+ discoveryFinishedSpy.clear();
+
+ // redo the discovery with same controller
+ QLowEnergyService *services_second[MAX_SERVICES_SAME_TIME_ACCESS];
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ // get all details
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ services_second[i] = control.createServiceObject(uuids.at(i), this);
+ QVERIFY(services_second[i]->parent() == this);
+ QVERIFY(services[i]);
+ QVERIFY(services_second[i]->state() == QLowEnergyService::DiscoveryRequired);
+ services_second[i]->discoverDetails();
+ }
+
+ // wait until discovery done
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ qWarning() << "Waiting for" << i << services_second[i]->serviceUuid();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ services_second[i]->state() == QLowEnergyService::ServiceDiscovered,
+ 30000);
+ QCOMPARE(services_second[i]->serviceName(), services[i]->serviceName());
+ QCOMPARE(services_second[i]->serviceUuid(), services[i]->serviceUuid());
+ }
+
+ // verify discovered services (1st and 2nd round)
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ verifyServiceProperties(services_second[i]);
+ //after disconnect all related characteristics and descriptors are invalid
+ const QList<QLowEnergyCharacteristic> chars = services[i]->characteristics();
+ for (int j = 0; j < chars.count(); j++) {
+ QCOMPARE(chars.at(j).isValid(), false);
+ QVERIFY(services[i]->contains(chars[j]));
+ QVERIFY(!services_second[i]->contains(chars[j]));
+ const QList<QLowEnergyDescriptor> descriptors = chars[j].descriptors();
+ for (int k = 0; k < descriptors.count(); k++) {
+ QCOMPARE(descriptors[k].isValid(), false);
+ services[i]->contains(descriptors[k]);
+ QVERIFY(!services_second[i]->contains(chars[j]));
+ }
+ }
+
+ QCOMPARE(services[i]->serviceUuid(), services_second[i]->serviceUuid());
+ QCOMPARE(services[i]->serviceName(), services_second[i]->serviceName());
+ QCOMPARE(services[i]->type(), services_second[i]->type());
+ QVERIFY(services[i]->state() == QLowEnergyService::InvalidService);
+ QVERIFY(services_second[i]->state() == QLowEnergyService::ServiceDiscovered);
+ }
+
+ // cleanup
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ delete services[i];
+ delete services_second[i];
+ }
+
+ control.disconnectFromDevice();
+}
+
+void tst_QLowEnergyController::verifyServiceProperties(
+ const QLowEnergyService *info)
+{
+ if (info->serviceUuid() ==
+ QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying GAP Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 5);
+
+ // Device Name
+ QString temp("00002a00-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x3));
+ QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("544920424c452053656e736f7220546167"));
+ QVERIFY(chars[0].isValid());
+ QCOMPARE(chars[0].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[0]));
+
+ // Appearance
+ temp = QString("00002a01-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x5));
+ QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("0000"));
+ QVERIFY(chars[1].isValid());
+ QCOMPARE(chars[1].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[1]));
+
+ // Peripheral Privacy Flag
+ temp = QString("00002a02-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x7));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[2].isValid());
+ QCOMPARE(chars[2].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[2]));
+
+ // Reconnection Address
+ temp = QString("00002a03-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[3].handle(), QLowEnergyHandle(0x9));
+ //Early firmware version had this characteristic as Read|Write and may fail
+ QCOMPARE(chars[3].properties(), QLowEnergyCharacteristic::Write);
+ if (chars[3].properties() & QLowEnergyCharacteristic::Read)
+ QCOMPARE(chars[3].value(), QByteArray::fromHex("000000000000"));
+ else
+ QCOMPARE(chars[3].value(), QByteArray());
+ QVERIFY(chars[3].isValid());
+ QCOMPARE(chars[3].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[3]));
+
+ // Peripheral Preferred Connection Parameters
+ temp = QString("00002a04-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[4].handle(), QLowEnergyHandle(0xb));
+ QCOMPARE(chars[4].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[4].value(), QByteArray::fromHex("5000a0000000e803"));
+ QVERIFY(chars[4].isValid());
+ QCOMPARE(chars[4].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[4]));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying GATT Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 1);
+
+ // Service Changed
+ QString temp("00002a05-0000-1000-8000-00805f9b34fb");
+ //this should really be readable according to GATT Service spec
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0xe));
+ QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Indicate);
+ QCOMPARE(chars[0].value(), QByteArray());
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 1);
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0xf));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying Device Information";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 9);
+
+ // System ID
+ QString temp("00002a23-0000-1000-8000-00805f9b34fb");
+ //this should really be readable according to GATT Service spec
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x12));
+ QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("6e41ab0000296abc"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+ QCOMPARE(chars[0].descriptors().count(), 0);
+
+ // Model Number
+ temp = QString("00002a24-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x14));
+ QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+ QCOMPARE(chars[1].descriptors().count(), 0);
+
+ // Serial Number
+ temp = QString("00002a25-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x16));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+ QCOMPARE(chars[2].descriptors().count(), 0);
+
+ // Firmware Revision
+ temp = QString("00002a26-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[3].handle(), QLowEnergyHandle(0x18));
+ QCOMPARE(chars[3].properties(),
+ (QLowEnergyCharacteristic::Read));
+ //FW rev. : 1.5 (Oct 23 2013)
+ // Other revisions will fail here
+ QCOMPARE(chars[3].value(), QByteArray::fromHex("312e3520284f637420323320323031332900"));
+ QVERIFY(chars[3].isValid());
+ QVERIFY(info->contains(chars[3]));
+ QCOMPARE(chars[3].descriptors().count(), 0);
+
+ // Hardware Revision
+ temp = QString("00002a27-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[4].handle(), QLowEnergyHandle(0x1a));
+ QCOMPARE(chars[4].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[4].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[4].isValid());
+ QVERIFY(info->contains(chars[4]));
+ QCOMPARE(chars[4].descriptors().count(), 0);
+
+ // Software Revision
+ temp = QString("00002a28-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[5].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[5].handle(), QLowEnergyHandle(0x1c));
+ QCOMPARE(chars[5].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[5].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[5].isValid());
+ QVERIFY(info->contains(chars[5]));
+ QCOMPARE(chars[5].descriptors().count(), 0);
+
+ // Manufacturer Name
+ temp = QString("00002a29-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[6].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[6].handle(), QLowEnergyHandle(0x1e));
+ QCOMPARE(chars[6].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[6].value(), QByteArray::fromHex("546578617320496e737472756d656e747300"));
+ QVERIFY(chars[6].isValid());
+ QVERIFY(info->contains(chars[6]));
+ QCOMPARE(chars[6].descriptors().count(), 0);
+
+ // IEEE
+ temp = QString("00002a2a-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[7].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[7].handle(), QLowEnergyHandle(0x20));
+ QCOMPARE(chars[7].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[7].value(), QByteArray::fromHex("fe006578706572696d656e74616c"));
+ QVERIFY(chars[7].isValid());
+ QVERIFY(info->contains(chars[7]));
+ QCOMPARE(chars[7].descriptors().count(), 0);
+
+ // PnP ID
+ temp = QString("00002a50-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[8].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[8].handle(), QLowEnergyHandle(0x22));
+ QCOMPARE(chars[8].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[8].value(), QByteArray::fromHex("010d0000001001"));
+ QVERIFY(chars[8].isValid());
+ QVERIFY(info->contains(chars[8]));
+ QCOMPARE(chars[8].descriptors().count(), 0);
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Temperature";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 2);
+
+ // Temp Data
+ QString temp("f000aa01-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x25));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x26));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x27));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("54656d702e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Temp Config
+ temp = QString("f000aa02-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x29));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x2a));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("54656d702e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+
+ //Temp Period (introduced by later firmware versions)
+ if (chars.count() > 2) {
+ temp = QString("f000aa03-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x2c));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x2d));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("54656d702e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ }
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying Simple Keys";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 1);
+
+ // Temp Data
+ QString temp("0000ffe1-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x6b));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray());
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x6c));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x6d));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4b6579205072657373205374617465"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Accelerometer";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 3);
+
+ // Accel Data
+ QString temp("f000aa11-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x30));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x31));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x32));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("416363656c2e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Accel Config
+ temp = QString("f000aa12-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x34));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+ QCOMPARE(chars[1].descriptors().count(), 1);
+
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x35));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("416363656c2e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ // Accel Period
+ temp = QString("f000aa13-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x37));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64")); // don't change it or set it to 0x64
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x38));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("416363656c2e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Humidity";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 2); //new firmware has more chars
+
+ // Humidity Data
+ QString temp("f000aa21-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x3b));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x3c));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x3d));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("48756d69642e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Humidity Config
+ temp = QString("f000aa22-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x3f));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x40));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("48756d69642e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ if (chars.count() >= 3) {
+ // New firmware new characteristic
+ // Humidity Period
+ temp = QString("f000aa23-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x42));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x43));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("48756d69642e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ }
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Magnetometer";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 3);
+
+ // Magnetometer Data
+ QString temp("f000aa31-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x46));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x47));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x48));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4d61676e2e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Magnetometer Config
+ temp = QString("f000aa32-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x4a));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x4b));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("4d61676e2e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ // Magnetometer Period
+ temp = QString("f000aa33-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x4d));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("c8")); // don't change it or set it to 0xc8
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x4e));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("4d61676e2e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Pressure";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 3);
+
+ // Pressure Data
+ QString temp("f000aa41-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x51));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x52));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x53));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4261726f6d2e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Pressure Config
+ temp = QString("f000aa42-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x55));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x56));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("4261726f6d2e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ //calibration and period characteristic are swapped, ensure we don't depend on their order
+ QLowEnergyCharacteristic calibration, period;
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ //find calibration characteristic
+ if (ch.uuid() == QBluetoothUuid(QString("f000aa43-0451-4000-b000-000000000000")))
+ calibration = ch;
+ else if (ch.uuid() == QBluetoothUuid(QString("f000aa44-0451-4000-b000-000000000000")))
+ period = ch;
+ }
+
+ if (calibration.isValid()) {
+ // Pressure Calibration
+ temp = QString("f000aa43-0451-4000-b000-000000000000");
+ QCOMPARE(calibration.uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(calibration.handle(), QLowEnergyHandle(0x5b));
+ QCOMPARE(calibration.properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(calibration.value(), QByteArray::fromHex("00000000000000000000000000000000")); // don't change it
+ QVERIFY(calibration.isValid());
+ QVERIFY(info->contains(calibration));
+
+ QCOMPARE(calibration.descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(calibration.descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(calibration.descriptors().at(0).handle(), QLowEnergyHandle(0x5c));
+ QCOMPARE(calibration.descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(calibration.descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(calibration.descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(calibration.descriptors().at(0)));
+
+ QCOMPARE(calibration.descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(calibration.descriptors().at(1).handle(), QLowEnergyHandle(0x5d));
+ QCOMPARE(calibration.descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(calibration.descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(calibration.descriptors().at(1).value(),
+ QByteArray::fromHex("4261726f6d2e2043616c6962722e"));
+ QVERIFY(info->contains(calibration.descriptors().at(1)));
+ }
+
+ if (period.isValid()) {
+ // Period Calibration
+ temp = QString("f000aa44-0451-4000-b000-000000000000");
+ QCOMPARE(period.uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(period.handle(), QLowEnergyHandle(0x58));
+ QCOMPARE(period.properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(period.value(), QByteArray::fromHex("64"));
+ QVERIFY(period.isValid());
+ QVERIFY(info->contains(period));
+
+ QCOMPARE(period.descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(period.descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(period.descriptors().at(0).handle(), QLowEnergyHandle(0x59));
+ QCOMPARE(period.descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(period.descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(period.descriptors().at(0).value(),
+ QByteArray::fromHex("4261726f6d2e20506572696f64"));
+ QVERIFY(info->contains(period.descriptors().at(0)));
+ }
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Gyroscope";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 2);
+
+ // Gyroscope Data
+ QString temp("f000aa51-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x60));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x61));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x62));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4779726f2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Gyroscope Config
+ temp = QString("f000aa52-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x64));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x65));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("4779726f20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ // Gyroscope Period
+ temp = QString("f000aa53-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x67));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x68));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("4779726f20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Test Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 2);
+
+ // Test Data
+ QString temp("f000aa61-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x70));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("3f00"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 1);
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x71));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(0).value(),
+ QByteArray::fromHex("546573742044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ // Test Config
+ temp = QString("f000aa62-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x73));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x74));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("5465737420436f6e666967"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000ccc0-0451-4000-b000-000000000000"))) {
+ qDebug() << "Connection Control Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 3);
+
+ //first characteristic
+ QString temp("f000ccc1-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x77));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x78));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0100"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x79));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("436f6e6e2e20506172616d73"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ //second characteristic
+ temp = QString("f000ccc2-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x7b));
+ QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Write);
+ QCOMPARE(chars[1].value(), QByteArray());
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x7c));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("436f6e6e2e20506172616d7320526571"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ //third characteristic
+ temp = QString("f000ccc3-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x7e));
+ QCOMPARE(chars[2].properties(), QLowEnergyCharacteristic::Write);
+ QCOMPARE(chars[2].value(), QByteArray());
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x7f));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("446973636f6e6e65637420526571"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying OID Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 2);
+
+ // first characteristic
+ QString temp("f000ffc1-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x82));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse));
+ QCOMPARE(chars[0].value(), QByteArray());
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x83));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0100"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x84));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("496d67204964656e74696679"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // second characteristic
+ temp = QString("f000ffc2-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x86));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse));
+ QCOMPARE(chars[1].value(), QByteArray());
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x87));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).value(), QByteArray::fromHex("0100"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ QCOMPARE(chars[1].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(1).handle(), QLowEnergyHandle(0x88));
+ QCOMPARE(chars[1].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(1).value(),
+ QByteArray::fromHex("496d6720426c6f636b"));
+ QVERIFY(info->contains(chars[1].descriptors().at(1)));
+ } else {
+ QFAIL(QString("Service not found" + info->serviceUuid().toString()).toUtf8().constData());
+ }
+}
+
+void tst_QLowEnergyController::tst_defaultBehavior()
+{
+ QList<QBluetoothAddress> foundAddresses;
+ foreach (const QBluetoothHostInfo &info, QBluetoothLocalDevice::allDevices())
+ foundAddresses.append(info.address());
+ const QBluetoothAddress randomAddress("11:22:33:44:55:66");
+
+ // Test automatic detection of local adapter
+ QLowEnergyController controlDefaultAdapter(randomAddress);
+ QCOMPARE(controlDefaultAdapter.remoteAddress(), randomAddress);
+ QCOMPARE(controlDefaultAdapter.state(), QLowEnergyController::UnconnectedState);
+ if (foundAddresses.isEmpty()) {
+ QVERIFY(controlDefaultAdapter.localAddress().isNull());
+ } else {
+ QCOMPARE(controlDefaultAdapter.error(), QLowEnergyController::NoError);
+ QVERIFY(controlDefaultAdapter.errorString().isEmpty());
+ QVERIFY(foundAddresses.contains(controlDefaultAdapter.localAddress()));
+
+ // unrelated uuids don't return valid service object
+ // invalid service uuid
+ QVERIFY(!controlDefaultAdapter.createServiceObject(
+ QBluetoothUuid()));
+ // some random uuid
+ QVERIFY(!controlDefaultAdapter.createServiceObject(
+ QBluetoothUuid(QBluetoothUuid::DeviceName)));
+ }
+
+ QCOMPARE(controlDefaultAdapter.services().count(), 0);
+
+ // Test explicit local adapter
+ if (!foundAddresses.isEmpty()) {
+ QLowEnergyController controlExplicitAdapter(randomAddress,
+ foundAddresses[0]);
+ QCOMPARE(controlExplicitAdapter.remoteAddress(), randomAddress);
+ QCOMPARE(controlExplicitAdapter.localAddress(), foundAddresses[0]);
+ QCOMPARE(controlExplicitAdapter.state(),
+ QLowEnergyController::UnconnectedState);
+ QCOMPARE(controlExplicitAdapter.services().count(), 0);
+
+ // unrelated uuids don't return valid service object
+ // invalid service uuid
+ QVERIFY(!controlExplicitAdapter.createServiceObject(
+ QBluetoothUuid()));
+ // some random uuid
+ QVERIFY(!controlExplicitAdapter.createServiceObject(
+ QBluetoothUuid(QBluetoothUuid::DeviceName)));
+ }
+}
+
+void tst_QLowEnergyController::tst_writeCharacteristic()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ // quick setup - more elaborate test is done by connect()
+ QLowEnergyController control(remoteDevice);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ if (control.state() == QLowEnergyController::ConnectingState
+ || control.error() != QLowEnergyController::NoError) {
+ // default BTLE backend forever hangs in ConnectingState
+ QSKIP("Cannot connect to remote device");
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ const QBluetoothUuid testService(QString("f000aa60-0451-4000-b000-000000000000"));
+ QList<QBluetoothUuid> uuids = control.services();
+ QVERIFY(uuids.contains(testService));
+
+ QLowEnergyService *service = control.createServiceObject(testService, this);
+ QVERIFY(service);
+ service->discoverDetails();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ service->state() == QLowEnergyService::ServiceDiscovered, 30000);
+
+ //test service described by http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
+ const QList<QLowEnergyCharacteristic> chars = service->characteristics();
+
+ QLowEnergyCharacteristic dataChar;
+ QLowEnergyCharacteristic configChar;
+ for (int i = 0; i < chars.count(); i++) {
+ if (chars[i].uuid() == QBluetoothUuid(QString("f000aa61-0451-4000-b000-000000000000")))
+ dataChar = chars[i];
+ else if (chars[i].uuid() == QBluetoothUuid(QString("f000aa62-0451-4000-b000-000000000000")))
+ configChar = chars[i];
+ }
+
+ QVERIFY(dataChar.isValid());
+ QVERIFY(!(dataChar.properties() & ~QLowEnergyCharacteristic::Read)); // only a read char
+ QVERIFY(service->contains(dataChar));
+ QVERIFY(configChar.isValid());
+ QVERIFY(configChar.properties() & QLowEnergyCharacteristic::Write);
+ QVERIFY(service->contains(configChar));
+
+ QCOMPARE(dataChar.value(), QByteArray::fromHex("3f00"));
+ QVERIFY(configChar.value() == QByteArray::fromHex("00")
+ || configChar.value() == QByteArray::fromHex("81"));
+
+ QSignalSpy writeSpy(service,
+ SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
+
+ // *******************************************
+ // test writing of characteristic
+ // enable Blinking LED if not already enabled
+ if (configChar.value() != QByteArray("81")) {
+ service->writeCharacteristic(configChar, QByteArray::fromHex("81")); //0x81 blink LED D1
+ QTRY_VERIFY_WITH_TIMEOUT(!writeSpy.isEmpty(), 10000);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("81"));
+ QList<QVariant> firstSignalData = writeSpy.first();
+ QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>();
+ QByteArray signalValue = firstSignalData[1].toByteArray();
+
+ QCOMPARE(signalValue, QByteArray::fromHex("81"));
+ QVERIFY(signalChar == configChar);
+
+ writeSpy.clear();
+ }
+
+ service->writeCharacteristic(configChar, QByteArray::fromHex("00")); //0x81 blink LED D1
+ QTRY_VERIFY_WITH_TIMEOUT(!writeSpy.isEmpty(), 10000);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("00"));
+ QList<QVariant> firstSignalData = writeSpy.first();
+ QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>();
+ QByteArray signalValue = firstSignalData[1].toByteArray();
+
+ QCOMPARE(signalValue, QByteArray::fromHex("00"));
+ QVERIFY(signalChar == configChar);
+
+ // *******************************************
+ // write wrong value -> error response required
+ QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError)));
+ writeSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(writeSpy.count(), 0);
+
+ // write 2 byte value to 1 byte characteristic
+ service->writeCharacteristic(configChar, QByteArray::fromHex("1111"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::CharacteristicWriteError);
+ QCOMPARE(service->error(), QLowEnergyService::CharacteristicWriteError);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("00"));
+
+ // *******************************************
+ // write to read-only characteristic -> error
+ errorSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ service->writeCharacteristic(dataChar, QByteArray::fromHex("ffff"));
+
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::OperationError);
+ QCOMPARE(service->error(), QLowEnergyService::OperationError);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(dataChar.value(), QByteArray::fromHex("3f00"));
+
+
+ control.disconnectFromDevice();
+
+ // *******************************************
+ // write value while disconnected -> error
+ errorSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ service->writeCharacteristic(configChar, QByteArray::fromHex("ffff"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 2000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::OperationError);
+ QCOMPARE(service->error(), QLowEnergyService::OperationError);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("00"));
+
+ // invalid characteristics still belong to their respective service
+ QVERIFY(service->contains(configChar));
+ QVERIFY(service->contains(dataChar));
+
+ QVERIFY(!service->contains(QLowEnergyCharacteristic()));
+
+ delete service;
+}
+
+void tst_QLowEnergyController::tst_writeDescriptor()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ // quick setup - more elaborate test is done by connect()
+ QLowEnergyController control(remoteDevice);
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ if (control.state() == QLowEnergyController::ConnectingState
+ || control.error() != QLowEnergyController::NoError) {
+ // default BTLE backend forever hangs in ConnectingState
+ QSKIP("Cannot connect to remote device");
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ const QBluetoothUuid testService(QString("f000aa00-0451-4000-b000-000000000000"));
+ QList<QBluetoothUuid> uuids = control.services();
+ QVERIFY(uuids.contains(testService));
+
+ QLowEnergyService *service = control.createServiceObject(testService, this);
+ QVERIFY(service);
+ service->discoverDetails();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ service->state() == QLowEnergyService::ServiceDiscovered, 30000);
+
+ // Temperature service described by
+ // http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
+
+ // 1. Find temperature data characteristic
+ const QLowEnergyCharacteristic tempData = service->characteristic(
+ QBluetoothUuid(QStringLiteral("f000aa01-0451-4000-b000-000000000000")));
+ const QLowEnergyCharacteristic tempConfig = service->characteristic(
+ QBluetoothUuid(QStringLiteral("f000aa02-0451-4000-b000-000000000000")));
+
+ if (!tempData.isValid()) {
+ delete service;
+ control.disconnectFromDevice();
+ QSKIP("Cannot find temperature data characteristic of TI Sensor");
+ }
+
+ // 2. Find temperature data notification descriptor
+ const QLowEnergyDescriptor notification = tempData.descriptor(
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+
+ if (!notification.isValid()) {
+ delete service;
+ control.disconnectFromDevice();
+ QSKIP("Cannot find temperature data notification of TI Sensor");
+ }
+
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+ service->contains(notification);
+ service->contains(tempData);
+ if (tempConfig.isValid()) {
+ service->contains(tempConfig);
+ QCOMPARE(tempConfig.value(), QByteArray::fromHex("00"));
+ }
+
+ // 3. Test writing to descriptor -> activate notifications
+ QSignalSpy descChangedSpy(service,
+ SIGNAL(descriptorChanged(QLowEnergyDescriptor,QByteArray)));
+ QSignalSpy charChangedSpy(service,
+ SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
+ service->writeDescriptor(notification, QByteArray::fromHex("0100"));
+ // verify
+ QTRY_VERIFY_WITH_TIMEOUT(!descChangedSpy.isEmpty(), 3000);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0100"));
+ QList<QVariant> firstSignalData = descChangedSpy.first();
+ QLowEnergyDescriptor signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>();
+ QByteArray signalValue = firstSignalData[1].toByteArray();
+ QCOMPARE(signalValue, QByteArray::fromHex("0100"));
+ QVERIFY(notification == signalDesc);
+ descChangedSpy.clear();
+
+ // 4. Test reception of notifications
+ // activate the temperature sensor if available
+ if (tempConfig.isValid()) {
+ service->writeCharacteristic(tempConfig, QByteArray::fromHex("01"));
+
+ // first signal is confirmation of tempConfig write
+ // subsequent signals are temp data updates
+ QTRY_VERIFY_WITH_TIMEOUT(charChangedSpy.count() >= 5, 10000);
+ QList<QVariant> entry;
+ for (int i = 0; i < charChangedSpy.count(); i++) {
+ entry = charChangedSpy[i];
+ const QLowEnergyCharacteristic ch = entry[0].value<QLowEnergyCharacteristic>();
+ const QByteArray val = entry[1].toByteArray();
+
+ if (i == 0) {
+ QCOMPARE(tempConfig, ch);
+ } else {
+ qDebug() << "Temp update: " << hex << ch.handle() << val.toHex();
+ QCOMPARE(tempData, ch);
+ }
+ }
+
+ service->writeCharacteristic(tempConfig, QByteArray::fromHex("00"));
+ }
+
+ // 5. Test writing to descriptor -> deactivate notifications
+ service->writeDescriptor(notification, QByteArray::fromHex("0000"));
+ // verify
+ QTRY_VERIFY_WITH_TIMEOUT(!descChangedSpy.isEmpty(), 3000);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+ firstSignalData = descChangedSpy.first();
+ signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>();
+ signalValue = firstSignalData[1].toByteArray();
+ QCOMPARE(signalValue, QByteArray::fromHex("0000"));
+ QVERIFY(notification == signalDesc);
+ descChangedSpy.clear();
+
+ // *******************************************
+ // write wrong value -> error response required
+ QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError)));
+ descChangedSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(descChangedSpy.count(), 0);
+
+ // write 4 byte value to 2 byte characteristic
+ service->writeDescriptor(notification, QByteArray::fromHex("11112222"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 30000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::DescriptorWriteError);
+ QCOMPARE(service->error(), QLowEnergyService::DescriptorWriteError);
+ QCOMPARE(descChangedSpy.count(), 0);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+
+ control.disconnectFromDevice();
+
+ // *******************************************
+ // write value while disconnected -> error
+ errorSpy.clear();
+ service->writeDescriptor(notification, QByteArray::fromHex("0100"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 2000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::OperationError);
+ QCOMPARE(service->error(), QLowEnergyService::OperationError);
+ QCOMPARE(descChangedSpy.count(), 0);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+
+ delete service;
+}
+
+QTEST_MAIN(tst_QLowEnergyController)
+
+#include "tst_qlowenergycontroller.moc"