diff options
Diffstat (limited to 'tests/bttestui/btlocaldevice.cpp')
-rw-r--r-- | tests/bttestui/btlocaldevice.cpp | 1008 |
1 files changed, 875 insertions, 133 deletions
diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp index 55b99fc3..19e78bb3 100644 --- a/tests/bttestui/btlocaldevice.cpp +++ b/tests/bttestui/btlocaldevice.cpp @@ -1,38 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtBluetooth module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "btlocaldevice.h" +#include <QCoreApplication> #include <QDebug> #include <QTimer> -#ifdef Q_OS_ANDROID -#include <QtAndroidExtras/QtAndroid> -#endif #include <QtBluetooth/QBluetoothServiceInfo> +#include <QtBluetooth/QLowEnergyCharacteristicData> +#include <QtBluetooth/QLowEnergyDescriptorData> +#include <QtBluetooth/QLowEnergyAdvertisingParameters> #define BTCHAT_DEVICE_ADDR "00:15:83:38:17:C3" @@ -45,12 +21,80 @@ #define SOCKET_PROTOCOL QBluetoothServiceInfo::RfcommProtocol //#define SOCKET_PROTOCOL QBluetoothServiceInfo::L2capProtocol -BtLocalDevice::BtLocalDevice(QObject *parent) : - QObject(parent), securityFlags(QBluetooth::NoSecurity) +using namespace Qt::Literals::StringLiterals; +// Main service used for testing read/write/notify +static constexpr auto leServiceUuid{"10f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1}; +static constexpr auto leCharUuid1{"11f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1}; +static constexpr auto leCharUuid2{"12f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1}; +// Service for testing the included services +static constexpr auto leSecondServiceUuid{"20f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1}; +static constexpr auto leSecondServiceCharUuid1{"21f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1}; +// Service for testing the secondary service and other miscellaneous +static constexpr auto leThirdServiceUuid{"30f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1}; +static constexpr auto leThirdServiceCharUuid1{"31f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1}; + +// Used for finding a matching LE peripheral device. Typically the default BtTestUi is ok +// when running against macOS/iOS/Linux peripheral, but with Android this needs to be adjusted +// to device's name. We can't use bluetooth address for matching as the public address of the +// peripheral may change +static const auto leRemotePeriphreralDeviceName = "BtTestUi"_L1; +static const qsizetype leCharacteristicSize = 4; // Set to 1...512 bytes +static QByteArray leCharacteristicValue = QByteArray{leCharacteristicSize, 1}; +static QByteArray leDescriptorValue = "a descriptor value"_ba; +static auto leSecondCharacteristicValue = QByteArray{leCharacteristicSize, 2}; +static quint8 leCharacteristicValueUpdate = 1; +static char leDescriptorValueUpdate = 'b'; + +// String tables to shorten the enum strings to fit the screen estate. +// The values in the tables must be in same order as the corresponding enums +static constexpr const char* controllerStateString[] = { + "Unconnected", + "Connecting", + "Connected", + "Discovering", + "Discovered", + "Closing", + "Advertising", +}; + +static constexpr const char* controllerErrorString[] = { + "None", + "UnknownError", + "UnknownRemDev", + "NetworkError", + "InvAdapter", + "ConnectionErr", + "AdvertisingErr", + "RemHostClosed", + "AuthError", + "MissingPerm", + "RssiError" +}; + +static constexpr const char* serviceStateString[] = { + "InvalidService", + "RemoteService", + "RemDiscovering", + "RemDiscovered", + "LocalService", +}; + +static constexpr const char* serviceErrorString[] = { + "None", + "Operation", + "CharWrite", + "DescWrite", + "Unknown", + "CharRead", + "DescRead", +}; + +BtLocalDevice::BtLocalDevice(QObject *parent) + : QObject(parent), securityFlags(QBluetooth::Security::NoSecurity) { localDevice = new QBluetoothLocalDevice(this); - connect(localDevice, &QBluetoothLocalDevice::error, - this, &BtLocalDevice::error); + connect(localDevice, &QBluetoothLocalDevice::errorOccurred, this, + &BtLocalDevice::errorOccurred); connect(localDevice, &QBluetoothLocalDevice::hostModeStateChanged, this, &BtLocalDevice::hostModeStateChanged); connect(localDevice, &QBluetoothLocalDevice::pairingFinished, @@ -59,19 +103,17 @@ BtLocalDevice::BtLocalDevice(QObject *parent) : this, &BtLocalDevice::connected); connect(localDevice, &QBluetoothLocalDevice::deviceDisconnected, this, &BtLocalDevice::disconnected); - connect(localDevice, &QBluetoothLocalDevice::pairingDisplayConfirmation, - this, &BtLocalDevice::pairingDisplayConfirmation); - connect(localDevice, &QBluetoothLocalDevice::pairingDisplayPinCode, - this, &BtLocalDevice::pairingDisplayPinCode); if (localDevice->isValid()) { deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BtLocalDevice::deviceDiscovered); + connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::deviceUpdated, + this, &BtLocalDevice::deviceUpdated); connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BtLocalDevice::discoveryFinished); - connect(deviceAgent, QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(&QBluetoothDeviceDiscoveryAgent::error), - this, &BtLocalDevice::discoveryError); + connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred, this, + &BtLocalDevice::discoveryError); connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &BtLocalDevice::discoveryCanceled); @@ -82,14 +124,13 @@ BtLocalDevice::BtLocalDevice(QObject *parent) : this, &BtLocalDevice::serviceDiscoveryFinished); connect(serviceAgent, &QBluetoothServiceDiscoveryAgent::canceled, this, &BtLocalDevice::serviceDiscoveryCanceled); - connect(serviceAgent, QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(&QBluetoothServiceDiscoveryAgent::error), - this, &BtLocalDevice::serviceDiscoveryError); + connect(serviceAgent, &QBluetoothServiceDiscoveryAgent::errorOccurred, this, + &BtLocalDevice::serviceDiscoveryError); socket = new QBluetoothSocket(SOCKET_PROTOCOL, this); connect(socket, &QBluetoothSocket::stateChanged, this, &BtLocalDevice::socketStateChanged); - connect(socket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error), - this, &BtLocalDevice::socketError); + connect(socket, &QBluetoothSocket::errorOccurred, this, &BtLocalDevice::socketError); connect(socket, &QBluetoothSocket::connected, this, &BtLocalDevice::socketConnected); connect(socket, &QBluetoothSocket::disconnected, this, &BtLocalDevice::socketDisconnected); connect(socket, &QIODevice::readyRead, this, &BtLocalDevice::readData); @@ -100,13 +141,7 @@ BtLocalDevice::BtLocalDevice(QObject *parent) : server = new QBluetoothServer(SOCKET_PROTOCOL, this); connect(server, &QBluetoothServer::newConnection, this, &BtLocalDevice::serverNewConnection); - connect(server, QOverload<QBluetoothServer::Error>::of(&QBluetoothServer::error), - this, &BtLocalDevice::serverError); - } else { - deviceAgent = nullptr; - serviceAgent = nullptr; - socket = nullptr; - server = nullptr; + connect(server, &QBluetoothServer::errorOccurred, this, &BtLocalDevice::serverError); } } @@ -120,9 +155,9 @@ BtLocalDevice::~BtLocalDevice() } } -int BtLocalDevice::secFlags() const +QBluetooth::SecurityFlags BtLocalDevice::secFlags() const { - return static_cast<int>(securityFlags); + return securityFlags; } void BtLocalDevice::setSecFlags(int newFlags) @@ -137,15 +172,17 @@ void BtLocalDevice::setSecFlags(int newFlags) QString BtLocalDevice::hostMode() const { - switch (localDevice->hostMode()) { - case QBluetoothLocalDevice::HostDiscoverable: - return QStringLiteral("HostMode: Discoverable"); - case QBluetoothLocalDevice::HostConnectable: - return QStringLiteral("HostMode: Connectable"); - case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry: - return QStringLiteral("HostMode: DiscoverableLimit"); - case QBluetoothLocalDevice::HostPoweredOff: - return QStringLiteral("HostMode: Powered Off"); + if (localDevice) { + switch (localDevice->hostMode()) { + case QBluetoothLocalDevice::HostDiscoverable: + return QStringLiteral("HostMode: Discoverable"); + case QBluetoothLocalDevice::HostConnectable: + return QStringLiteral("HostMode: Connectable"); + case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry: + return QStringLiteral("HostMode: DiscoverableLimit"); + case QBluetoothLocalDevice::HostPoweredOff: + return QStringLiteral("HostMode: Powered Off"); + } } return QStringLiteral("HostMode: <None>"); @@ -153,17 +190,16 @@ QString BtLocalDevice::hostMode() const void BtLocalDevice::setHostMode(int newMode) { - localDevice->setHostMode(static_cast<QBluetoothLocalDevice::HostMode>(newMode)); + if (localDevice) + localDevice->setHostMode(static_cast<QBluetoothLocalDevice::HostMode>(newMode)); } void BtLocalDevice::requestPairingUpdate(bool isPairing) { QBluetoothAddress baddr(BTCHAT_DEVICE_ADDR); - if (baddr.isNull()) + if (!localDevice || baddr.isNull()) return; - - if (isPairing) { //toggle between authorized and non-authorized pairing to achieve better //level of testing @@ -177,7 +213,7 @@ void BtLocalDevice::requestPairingUpdate(bool isPairing) localDevice->requestPairing(baddr, QBluetoothLocalDevice::Unpaired); } - for (int i = 0; i < foundTestServers.count(); i++) { + for (qsizetype i = 0; i < foundTestServers.size(); ++i) { if (isPairing) localDevice->requestPairing(foundTestServers.at(i).device().address(), QBluetoothLocalDevice::Paired); @@ -202,37 +238,18 @@ void BtLocalDevice::disconnected(const QBluetoothAddress &addr) qDebug() << "Newly disconnected device" << addr.toString(); } -void BtLocalDevice::pairingDisplayConfirmation(const QBluetoothAddress &address, const QString &pin) -{ - qDebug() << "PairingDisplayConfirmation" << address << pin; - QTimer::singleShot(3000, this, SLOT(confirmPairing())); -} - -void BtLocalDevice::pairingDisplayPinCode(const QBluetoothAddress &address, const QString &pin) -{ - qDebug() << "PairingDisplayPinCode" << address << pin; -} - -void BtLocalDevice::confirmPairing() -{ - static bool confirm = false; - confirm = !confirm; //toggle - qDebug() << "######" << "Sending pairing confirmation: " << confirm; - localDevice->pairingConfirmation(confirm); -} - void BtLocalDevice::cycleSecurityFlags() { - if (securityFlags.testFlag(QBluetooth::Secure)) - setSecFlags(QBluetooth::NoSecurity); - else if (securityFlags.testFlag(QBluetooth::Encryption)) - setSecFlags(secFlags() | QBluetooth::Secure); - else if (securityFlags.testFlag(QBluetooth::Authentication)) - setSecFlags(secFlags() | QBluetooth::Encryption); - else if (securityFlags.testFlag(QBluetooth::Authorization)) - setSecFlags(secFlags() | QBluetooth::Authentication); + if (securityFlags.testFlag(QBluetooth::Security::Secure)) + setSecFlags(QBluetooth::SecurityFlags(QBluetooth::Security::NoSecurity)); + else if (securityFlags.testFlag(QBluetooth::Security::Encryption)) + setSecFlags(secFlags() | QBluetooth::Security::Secure); + else if (securityFlags.testFlag(QBluetooth::Security::Authentication)) + setSecFlags(secFlags() | QBluetooth::Security::Encryption); + else if (securityFlags.testFlag(QBluetooth::Security::Authorization)) + setSecFlags(secFlags() | QBluetooth::Security::Authentication); else - setSecFlags(secFlags() | QBluetooth::Authorization); + setSecFlags(secFlags() | QBluetooth::Security::Authorization); } void BtLocalDevice::deviceDiscovered(const QBluetoothDeviceInfo &info) @@ -255,12 +272,29 @@ void BtLocalDevice::deviceDiscovered(const QBluetoothDeviceInfo &info) if (info.serviceClasses() & QBluetoothDeviceInfo::InformationService) services += "Information|"; - services.truncate(services.length()-1); //cut last '/' + services.truncate(services.size()-1); //cut last '/' qDebug() << "Found new device: " << info.name() << info.isValid() << info.address().toString() << info.rssi() << info.majorDeviceClass() << info.minorDeviceClass() << services; + // With LE we match the device by its name as the public bluetooth address can change + if (info.name() == leRemotePeriphreralDeviceName) { + qDebug() << "#### Matching LE peripheral device found"; + leRemotePeripheralDevice = info; + latestRSSI = QByteArray::number(info.rssi()); + emit leChanged(); + } +} +void BtLocalDevice::deviceUpdated(const QBluetoothDeviceInfo &info, + QBluetoothDeviceInfo::Fields updateFields) +{ + if (info.name() == leRemotePeriphreralDeviceName + && updateFields & QBluetoothDeviceInfo::Field::RSSI) { + qDebug() << "#### LE peripheral RSSI updated during scan"; + latestRSSI = QByteArray::number(info.rssi()); + emit leChanged(); + } } void BtLocalDevice::discoveryFinished() @@ -347,8 +381,8 @@ void BtLocalDevice::serviceDiscovered(const QBluetoothServiceInfo &info) bool matchingService = (info.serviceUuid() == QBluetoothUuid(QString(TEST_SERVICE_UUID))); #ifdef Q_OS_ANDROID - if (QtAndroid::androidSdkVersion() >= 23) //bug introduced by Android 6.0.1 - matchingService = matchingService + // QTBUG-61392 + matchingService = matchingService || (info.serviceUuid() == QBluetoothUuid(QString(TEST_REVERSE_SERVICE_UUID))); #endif @@ -357,7 +391,7 @@ void BtLocalDevice::serviceDiscovered(const QBluetoothServiceInfo &info) { //This is here to detect the test server for SPP testing later on bool alreadyKnown = false; - for (const QBluetoothServiceInfo& found : qAsConst(foundTestServers)) { + for (const QBluetoothServiceInfo& found : std::as_const(foundTestServers)) { if (found.device().address() == info.device().address()) { alreadyKnown = true; break; @@ -395,7 +429,7 @@ void BtLocalDevice::dumpServiceDiscovery() qDebug() << "Device Discovery active:" << deviceAgent->isActive(); qDebug() << "Error:" << deviceAgent->error() << deviceAgent->errorString(); const QList<QBluetoothDeviceInfo> list = deviceAgent->discoveredDevices(); - qDebug() << "Discovered Devices:" << list.count(); + qDebug() << "Discovered Devices:" << list.size(); for (const QBluetoothDeviceInfo &info : list) qDebug() << info.name() << info.address().toString() << info.rssi(); @@ -404,14 +438,14 @@ void BtLocalDevice::dumpServiceDiscovery() qDebug() << "Service Discovery active:" << serviceAgent->isActive(); qDebug() << "Error:" << serviceAgent->error() << serviceAgent->errorString(); const QList<QBluetoothServiceInfo> list = serviceAgent->discoveredServices(); - qDebug() << "Discovered Services:" << list.count(); + qDebug() << "Discovered Services:" << list.size(); for (const QBluetoothServiceInfo &i : list) { qDebug() << i.device().address().toString() << i.device().name() << i.serviceName(); } qDebug() << "###### TestServer offered by:"; - for (const QBluetoothServiceInfo& found : qAsConst(foundTestServers)) { + for (const QBluetoothServiceInfo& found : std::as_const(foundTestServers)) { qDebug() << found.device().name() << found.device().address().toString(); } } @@ -514,13 +548,14 @@ void BtLocalDevice::dumpSocketInformation() qDebug() << "socket bytesAvailable()" << socket->bytesAvailable(); QString tmp; switch (socket->error()) { - case QBluetoothSocket::NoSocketError: tmp += "NoSocketError"; break; - case QBluetoothSocket::UnknownSocketError: tmp += "UnknownSocketError"; break; - case QBluetoothSocket::HostNotFoundError: tmp += "HostNotFoundError"; break; - case QBluetoothSocket::ServiceNotFoundError: tmp += "ServiceNotFound"; break; - case QBluetoothSocket::NetworkError: tmp += "NetworkError"; break; - //case QBluetoothSocket::OperationError: tmp+= "OperationError"; break; - case QBluetoothSocket::UnsupportedProtocolError: tmp += "UnsupportedProtocolError"; break; + case QBluetoothSocket::SocketError::NoSocketError: tmp += "NoSocketError"; break; + case QBluetoothSocket::SocketError::UnknownSocketError: tmp += "UnknownSocketError"; break; + case QBluetoothSocket::SocketError::HostNotFoundError: tmp += "HostNotFoundError"; break; + case QBluetoothSocket::SocketError::ServiceNotFoundError: tmp += "ServiceNotFound"; break; + case QBluetoothSocket::SocketError::NetworkError: tmp += "NetworkError"; break; + //case QBluetoothSocket::SocketError::OperationError: tmp+= "OperationError"; break; + case QBluetoothSocket::SocketError::UnsupportedProtocolError: tmp += "UnsupportedProtocolError"; break; + case QBluetoothSocket::SocketError::MissingPermissionsError: tmp += "MissingPermissionsError"; break; default: tmp+= "Undefined"; break; } @@ -533,7 +568,7 @@ void BtLocalDevice::dumpSocketInformation() void BtLocalDevice::writeData() { const char * testData = "ABCABC\n"; - if (socket && socket->state() == QBluetoothSocket::ConnectedState) { + if (socket && socket->state() == QBluetoothSocket::SocketState::ConnectedState) { socket->write(testData); } for (QBluetoothSocket* client : serverSockets) { @@ -549,7 +584,7 @@ void BtLocalDevice::readData() qDebug() << ">> peer(" << socket->peerName() << socket->peerAddress() << socket->peerPort() << ") local(" << socket->localName() << socket->localAddress() << socket->localPort() - << ")>>" << QString::fromUtf8(line.constData(), line.length()); + << ")>>" << QString::fromUtf8(line.constData(), line.size()); } } } @@ -581,7 +616,7 @@ void BtLocalDevice::serverListenPort() QBluetoothServiceInfo::Sequence profileSequence; QBluetoothServiceInfo::Sequence classId; - classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); + classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort)); classId << QVariant::fromValue(quint16(0x100)); profileSequence.append(QVariant::fromValue(classId)); serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, @@ -589,7 +624,7 @@ void BtLocalDevice::serverListenPort() classId.clear(); classId << QVariant::fromValue(QBluetoothUuid(QString(TEST_SERVICE_UUID))); - classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); + classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort)); serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); // Service name, description and provider @@ -604,20 +639,20 @@ void BtLocalDevice::serverListenPort() // Service Discoverability QBluetoothServiceInfo::Sequence browseSequence; - browseSequence << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); + browseSequence << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::PublicBrowseGroup)); serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, browseSequence); // Protocol descriptor list QBluetoothServiceInfo::Sequence protocolDescriptorList; QBluetoothServiceInfo::Sequence protocol; - protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::L2cap)); if (server->serverType() == QBluetoothServiceInfo::L2capProtocol) protocol << QVariant::fromValue(server->serverPort()); protocolDescriptorList.append(QVariant::fromValue(protocol)); if (server->serverType() == QBluetoothServiceInfo::RfcommProtocol) { protocol.clear(); - protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::Rfcomm)) << QVariant::fromValue(quint8(server->serverPort())); protocolDescriptorList.append(QVariant::fromValue(protocol)); } @@ -682,8 +717,7 @@ void BtLocalDevice::serverNewConnection() connect(client, &QIODevice::readyRead, this, &BtLocalDevice::clientSocketReadyRead); connect(client, &QBluetoothSocket::stateChanged, this, &BtLocalDevice::socketStateChanged); - connect(client, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error), - this, &BtLocalDevice::socketError); + connect(client, &QBluetoothSocket::errorOccurred, this, &BtLocalDevice::socketError); connect(client, &QBluetoothSocket::connected, this, &BtLocalDevice::socketConnected); connect(client, &QBluetoothSocket::bytesWritten, this, [](qint64 bytesWritten){ qDebug() << "Bytes Written to Server socket:" << bytesWritten; @@ -712,7 +746,7 @@ void BtLocalDevice::clientSocketReadyRead() while (socket->canReadLine()) { const QByteArray line = socket->readLine().trimmed(); - QString lineString = QString::fromUtf8(line.constData(), line.length()); + QString lineString = QString::fromUtf8(line.constData(), line.size()); qDebug() << ">>(" << server->serverAddress() << server->serverPort() <<")>>" << lineString; @@ -729,7 +763,7 @@ void BtLocalDevice::clientSocketReadyRead() void BtLocalDevice::dumpServerInformation() { - static QBluetooth::SecurityFlags secFlag = QBluetooth::Authentication; + static QBluetooth::SecurityFlags secFlag = QBluetooth::Security::Authentication; if (server) { qDebug() << "*******************************"; qDebug() << "server port:" <<server->serverPort() @@ -740,14 +774,14 @@ void BtLocalDevice::dumpServerInformation() << "hasPending:" << server->hasPendingConnections() << "maxPending:" << server->maxPendingConnections(); qDebug() << "security:" << server->securityFlags() << "Togling security flag"; - if (secFlag == QBluetooth::Authentication) - secFlag = QBluetooth::Encryption; + if (secFlag == QBluetooth::SecurityFlags(QBluetooth::Security::Authentication)) + secFlag = QBluetooth::Security::Encryption; else - secFlag = QBluetooth::Authentication; + secFlag = QBluetooth::Security::Authentication; //server->setSecurityFlags(secFlag); - for (const QBluetoothSocket *client : qAsConst(serverSockets)) { + for (const QBluetoothSocket *client : std::as_const(serverSockets)) { qDebug() << "##" << client->localAddress().toString() << client->localName() << client->localPort(); qDebug() << "##" << client->peerAddress().toString() @@ -756,13 +790,14 @@ void BtLocalDevice::dumpServerInformation() qDebug() << "Pending bytes: " << client->bytesAvailable(); QString tmp; switch (client->error()) { - case QBluetoothSocket::NoSocketError: tmp += "NoSocketError"; break; - case QBluetoothSocket::UnknownSocketError: tmp += "UnknownSocketError"; break; - case QBluetoothSocket::HostNotFoundError: tmp += "HostNotFoundError"; break; - case QBluetoothSocket::ServiceNotFoundError: tmp += "ServiceNotFound"; break; - case QBluetoothSocket::NetworkError: tmp += "NetworkError"; break; - case QBluetoothSocket::UnsupportedProtocolError: tmp += "UnsupportedProtocolError"; break; - //case QBluetoothSocket::OperationError: tmp+= "OperationError"; break; + case QBluetoothSocket::SocketError::NoSocketError: tmp += "NoSocketError"; break; + case QBluetoothSocket::SocketError::UnknownSocketError: tmp += "UnknownSocketError"; break; + case QBluetoothSocket::SocketError::HostNotFoundError: tmp += "HostNotFoundError"; break; + case QBluetoothSocket::SocketError::ServiceNotFoundError: tmp += "ServiceNotFound"; break; + case QBluetoothSocket::SocketError::NetworkError: tmp += "NetworkError"; break; + case QBluetoothSocket::SocketError::UnsupportedProtocolError: tmp += "UnsupportedProtocolError"; break; + //case QBluetoothSocket::SocketError::OperationError: tmp+= "OperationError"; break; + case QBluetoothSocket::SocketError::MissingPermissionsError: tmp += "MissingPermissionsError"; break; default: tmp += QString::number(static_cast<int>(client->error())); break; } @@ -771,12 +806,36 @@ void BtLocalDevice::dumpServerInformation() } } +template <typename T> +void printError(const QLatin1StringView name, T* ptr) +{ + if (!ptr) + return; + qDebug() << name << "error:" << ptr->error(); +} + +void BtLocalDevice::dumpErrors() +{ + qDebug() << "###### Errors"; + printError("Device agent"_L1, deviceAgent); + printError("Service agent"_L1, serviceAgent); + printError("LE Central"_L1, leCentralController.get()); + printError("LE Central Service"_L1, leCentralService.get()); + printError("LE Peripheral"_L1, lePeripheralController.get()); + if (!lePeripheralServices.isEmpty()) + printError("LE Peripheral Service"_L1, lePeripheralServices[0].get()); + printError("Socket"_L1, socket); + printError("Server"_L1, server); +} + void BtLocalDevice::dumpInformation() { + if (!localDevice) + return; qDebug() << "###### default local device"; dumpLocalDevice(localDevice); const QList<QBluetoothHostInfo> list = QBluetoothLocalDevice::allDevices(); - qDebug() << "Found local devices: " << list.count(); + qDebug() << "Found local devices: " << list.size(); for (const QBluetoothHostInfo &info : list) { qDebug() << " " << info.address().toString() << " " <<info.name(); } @@ -826,17 +885,21 @@ void BtLocalDevice::dumpInformation() QBluetoothServiceDiscoveryAgent validSAgent(localDevice->address()); validSAgent.start(); qDebug() << "######" << (validSAgent.error() == QBluetoothServiceDiscoveryAgent::NoError) << "(Expected: true)"; + + dumpLeInfo(); } void BtLocalDevice::powerOn() { + if (!localDevice) + return; qDebug() << "Powering on"; localDevice->powerOn(); } void BtLocalDevice::reset() { - emit error(static_cast<QBluetoothLocalDevice::Error>(1000)); + emit errorOccurred(static_cast<QBluetoothLocalDevice::Error>(1000)); if (serviceAgent) { serviceAgent->clear(); } @@ -850,3 +913,682 @@ void BtLocalDevice::dumpLocalDevice(QBluetoothLocalDevice *dev) qDebug() << " Address" << dev->address().toString(); qDebug() << " HostMode" << dev->hostMode(); } + +void BtLocalDevice::peripheralCreate() +{ + qDebug() << "######" << "LE create peripheral"; + if (lePeripheralController) { + qDebug() << "Peripheral already existed"; + return; + } + + lePeripheralController.reset(QLowEnergyController::createPeripheral()); + emit leChanged(); + + QObject::connect(lePeripheralController.get(), &QLowEnergyController::errorOccurred, + [this](QLowEnergyController::Error error) { + qDebug() << "QLowEnergyController peripheral errorOccurred:" << error; + emit leChanged(); + }); + QObject::connect(lePeripheralController.get(), &QLowEnergyController::stateChanged, + [this](QLowEnergyController::ControllerState state) { + qDebug() << "QLowEnergyController peripheral stateChanged:" << state; + emit leChanged(); + }); +} + +void BtLocalDevice::peripheralAddServices() +{ + qDebug() << "######" << "LE add services"; + if (!lePeripheralController) { + qDebug() << "Create peripheral first"; + return; + } + if (lePeripheralServiceData.isEmpty()) { + // Create service data + { + QLowEnergyServiceData sd; + sd.setType(QLowEnergyServiceData::ServiceTypePrimary); + sd.setUuid(QBluetoothUuid(leServiceUuid)); + + QLowEnergyCharacteristicData charData; + charData.setUuid(QBluetoothUuid(leCharUuid1)); + charData.setValue(leCharacteristicValue); + charData.setValueLength(leCharacteristicSize, leCharacteristicSize); + charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read + | QLowEnergyCharacteristic::PropertyType::Write + | QLowEnergyCharacteristic::PropertyType::Notify + | QLowEnergyCharacteristic::ExtendedProperty); + + const QLowEnergyDescriptorData clientConfig( + QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration, + QLowEnergyCharacteristic::CCCDDisable); + charData.addDescriptor(clientConfig); + + const QLowEnergyDescriptorData userDescription( + QBluetoothUuid::DescriptorType::CharacteristicUserDescription, + leDescriptorValue); + charData.addDescriptor(userDescription); + + const QLowEnergyDescriptorData extendedProperties( + QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties, + // From bluetooth specs: length 2 bytes + // bit 0: reliable write, bit 1: writable auxiliaries + QByteArray::fromHex("0300")); + charData.addDescriptor(extendedProperties); + + sd.addCharacteristic(charData); + + // Set another characteristic without notifications + QLowEnergyCharacteristicData secondCharData; + secondCharData.setUuid(QBluetoothUuid(leCharUuid2)); + secondCharData.setValue(leSecondCharacteristicValue); + secondCharData.setValueLength(leCharacteristicSize, leCharacteristicSize); + secondCharData.setProperties(QLowEnergyCharacteristic::PropertyType::Read + | QLowEnergyCharacteristic::PropertyType::Write); + sd.addCharacteristic(secondCharData); + lePeripheralServiceData << sd; + } + { + QLowEnergyServiceData sd; + sd.setType(QLowEnergyServiceData::ServiceTypePrimary); + sd.setUuid(QBluetoothUuid(leSecondServiceUuid)); + + QLowEnergyCharacteristicData charData; + charData.setUuid(QBluetoothUuid(leSecondServiceCharUuid1)); + charData.setValue("second service char"_ba); + charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read); + sd.addCharacteristic(charData); + lePeripheralServiceData << sd; + } + { + QLowEnergyServiceData sd; + sd.setType(QLowEnergyServiceData::ServiceTypeSecondary); + sd.setUuid(QBluetoothUuid(leThirdServiceUuid)); + + QLowEnergyCharacteristicData charData; + charData.setUuid(QBluetoothUuid(leThirdServiceCharUuid1)); + charData.setValue("third service char"_ba); + charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read); + sd.addCharacteristic(charData); + lePeripheralServiceData << sd; + } + } + + Q_ASSERT(lePeripheralServiceData.size() == 3); + // Free previous services if any + lePeripheralServices.clear(); + // Add first service, and then set the first service as the included service for the second + auto service = lePeripheralController->addService(lePeripheralServiceData[0]); + if (service) { + lePeripheralServiceData[1].setIncludedServices({service}); + // Then add the services to controller + lePeripheralServices.emplaceBack(service); + lePeripheralServices.emplaceBack( + lePeripheralController->addService(lePeripheralServiceData[1])); + lePeripheralServices.emplaceBack( + lePeripheralController->addService(lePeripheralServiceData[2])); + } + + emit leChanged(); + + if (lePeripheralServices.isEmpty()) { + qDebug() << "Peripheral service creation failed"; + return; + } + + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::characteristicWritten, + [](const QLowEnergyCharacteristic&, const QByteArray& value){ + qDebug() << "LE peripheral service characteristic value written" << value; + }); + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::characteristicRead, + [](const QLowEnergyCharacteristic&, const QByteArray& value){ + qDebug() << "LE peripheral service characteristic value read" << value; + }); + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::characteristicChanged, + [](const QLowEnergyCharacteristic&, const QByteArray& value){ + qDebug() << "LE peripheral service characteristic value changed" << value; + }); + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::descriptorRead, + [](const QLowEnergyDescriptor&, const QByteArray& value){ + qDebug() << "LE peripheral service descriptor value read" << value; + }); + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::descriptorWritten, + [](const QLowEnergyDescriptor&, const QByteArray& value){ + qDebug() << "LE peripheral service descriptor value written" << value; + }); + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::errorOccurred, + [this](QLowEnergyService::ServiceError error){ + qDebug() << "LE peripheral service errorOccurred:" << error; + emit leChanged(); + }); + QObject::connect(lePeripheralServices[0].get(), &QLowEnergyService::stateChanged, + [this](QLowEnergyService::ServiceState state){ + qDebug() << "LE peripheral service state changed:" << state; + emit leChanged(); + }); +} + +void BtLocalDevice::peripheralStartAdvertising() +{ + qDebug() << "######" << "LE start advertising"; + if (!lePeripheralController) { + qDebug() << "Create peripheral first"; + return; + } + + if (leAdvertisingData.localName().isEmpty()) { + // Create advertisement data + leAdvertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral); + leAdvertisingData.setIncludePowerLevel(true); + leAdvertisingData.setLocalName(leRemotePeriphreralDeviceName); + + leAdvertisingData.setManufacturerData(0xCAFE, "maker"); + // Here we use short unrelated UUID so we can fit both service UUID and manufacturer data + // into the advertisement. This is for testing purposes + leAdvertisingData.setServices( + {QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::AlertNotificationService)}); + // May result in too big advertisement data, can be useful for testing such scenario + // leAdvertisingData.setServices({QBluetoothUuid(leServiceUuid)}); + } + // Start advertising. For testing the advertising can be started without valid services + qDebug() << "Starting advertising, services are valid:" << !lePeripheralServiceData.isEmpty(); + lePeripheralController->startAdvertising(QLowEnergyAdvertisingParameters{}, + leAdvertisingData, leAdvertisingData); +} + +void BtLocalDevice::peripheralStopAdvertising() +{ + qDebug() << "######" << "LE stop advertising"; + if (!lePeripheralController) { + qDebug() << "Peripheral does not exist"; + return; + } + lePeripheralController->stopAdvertising(); +} + +void BtLocalDevice::centralCharacteristicWrite() +{ + qDebug() << "######" << "LE central write characteristic"; + if (!leCentralController || !leCentralService) { + qDebug() << "Central or central service does not exist"; + return; + } + auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1)); + if (characteristic.isValid()) { + // Update value at the beginning and end so we can check whole data is sent in large writes + // Value is offset'd with 100 to easily see which end did the write when testing + leCharacteristicValueUpdate += 1; + leCharacteristicValue[0] = leCharacteristicValueUpdate + 100; + leCharacteristicValue[leCharacteristicSize - 1] = leCharacteristicValueUpdate + 100; + qDebug() << " Central writes value:" << leCharacteristicValue; + leCentralService->writeCharacteristic(characteristic, leCharacteristicValue); + } else { + qDebug() << "Characteristic was invalid"; + } +} + +void BtLocalDevice::centralCharacteristicRead() +{ + qDebug() << "######" << "LE central read characteristic"; + if (!leCentralController || !leCentralService) { + qDebug() << "Central or central service does not exist"; + return; + } + auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1)); + if (characteristic.isValid()) { + qDebug() << " Value before issuing read():" << characteristic.value(); + leCentralService->readCharacteristic(characteristic); + } else { + qDebug() << "Characteristic was invalid"; + } +} + +void BtLocalDevice::centralDescriptorWrite() +{ + qDebug() << "######" << "LE central write descriptor"; + if (!leCentralController || !leCentralService) { + qDebug() << "Central or central service does not exist"; + return; + } + auto descriptor = leCentralService->characteristic(QBluetoothUuid(leCharUuid1)) + .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription); + + if (descriptor.isValid()) { + leDescriptorValue[0] = leDescriptorValueUpdate++; + qDebug() << " Central writes value: " << leDescriptorValue; + leCentralService->writeDescriptor(descriptor, leDescriptorValue); + } else { + qDebug() << "Descriptor was invalid"; + } +} + +void BtLocalDevice::centralDescriptorRead() +{ + qDebug() << "######" << "LE central read descriptor"; + if (!leCentralController || !leCentralService) { + qDebug() << "Central or central service does not exist"; + return; + } + auto descriptor = leCentralService->characteristic(QBluetoothUuid(leCharUuid1)) + .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription); + if (descriptor.isValid()) { + qDebug() << " Value before issuing read():" << descriptor.value(); + leCentralService->readDescriptor(descriptor); + } else { + qDebug() << "Descriptor was invalid"; + } +} + + +void BtLocalDevice::peripheralCharacteristicWrite() +{ + qDebug() << "######" << "LE peripheral write characteristic"; + if (!lePeripheralController || lePeripheralServices.isEmpty()) { + qDebug() << "Peripheral or peripheral service does not exist"; + return; + } + + auto characteristic = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1)); + if (characteristic.isValid()) { + // Update value at the beginning and end so we can check whole data is sent in large writes + leCharacteristicValue[0] = ++leCharacteristicValueUpdate; + leCharacteristicValue[leCharacteristicSize - 1] = leCharacteristicValueUpdate; + qDebug() << " Peripheral writes value:" << leCharacteristicValue; + lePeripheralServices[0]->writeCharacteristic(characteristic, leCharacteristicValue); + } else { + qDebug() << "Characteristic was invalid"; + } +} + +void BtLocalDevice::peripheralCharacteristicRead() +{ + qDebug() << "######" << "LE peripheral read characteristic"; + if (!lePeripheralController || lePeripheralServices.isEmpty()) { + qDebug() << "Peripheral or peripheral service does not exist"; + return; + } + auto characteristic = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1)); + if (characteristic.isValid()) + qDebug() << " Value:" << characteristic.value(); + else + qDebug() << "Characteristic was invalid"; +} + +void BtLocalDevice::peripheralDescriptorWrite() +{ + qDebug() << "######" << "LE peripheral write descriptor"; + if (!lePeripheralController || lePeripheralServices.isEmpty()) { + qDebug() << "Peripheral or peripheral service does not exist"; + return; + } + auto descriptor = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1)) + .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription); + + if (descriptor.isValid()) { + leDescriptorValue[0] = leDescriptorValueUpdate++; + qDebug() << " Peripheral writes value: " << leDescriptorValue; + lePeripheralServices[0]->writeDescriptor(descriptor, leDescriptorValue); + } else { + qDebug() << "Descriptor was invalid"; + } +} + +void BtLocalDevice::peripheralDescriptorRead() +{ + qDebug() << "######" << "LE peripheral read descriptor"; + if (!lePeripheralController || lePeripheralServices.isEmpty()) { + qDebug() << "Peripheral or peripheral service does not exist"; + return; + } + auto descriptor = lePeripheralServices[0]->characteristic(QBluetoothUuid(leCharUuid1)) + .descriptor(QBluetoothUuid::DescriptorType::CharacteristicUserDescription); + if (descriptor.isValid()) + qDebug() << " Value:" << descriptor.value(); + else + qDebug() << "Descriptor was invalid"; +} + +void BtLocalDevice::startLeDeviceDiscovery() +{ + qDebug() << "######" << "LE device discovery start for:" << leRemotePeriphreralDeviceName; + if (deviceAgent) + deviceAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); +} + +void BtLocalDevice::centralStartServiceDiscovery() +{ + qDebug() << "######" << "LE service discovery start"; + if (!leCentralController) { + qDebug() << "Create and connect central first"; + return; + } + leCentralController->discoverServices(); +} + +void BtLocalDevice::centralCreate() +{ + qDebug() << "######" << "Central create"; + if (leCentralController) { + qDebug() << "Central already existed"; + return; + } + + if (!leRemotePeripheralDevice.isValid()) { + qDebug() << "Creation failed, needs successful LE device discovery first"; + return; + } + + if (deviceAgent && deviceAgent->isActive()) { + qDebug() << "###### Stopping device discovery agent"; + deviceAgent->stop(); + } + + leCentralController.reset(QLowEnergyController::createCentral(leRemotePeripheralDevice)); + emit leChanged(); + + if (!leCentralController) { + qDebug() << "LE Central creation failed"; + return; + } + + QObject::connect(leCentralController.get(), &QLowEnergyController::errorOccurred, + [](QLowEnergyController::Error error) { + qDebug() << "QLowEnergyController central errorOccurred:" << error; + }); + QObject::connect(leCentralController.get(), &QLowEnergyController::discoveryFinished, []() { + qDebug() << "QLowEnergyController central service discovery finished"; + }); + QObject::connect(leCentralController.get(), &QLowEnergyController::serviceDiscovered, + [](const QBluetoothUuid &newService){ + qDebug() << "QLowEnergyController central service discovered:" << newService; + }); + QObject::connect(leCentralController.get(), &QLowEnergyController::stateChanged, + [this](QLowEnergyController::ControllerState state) { + qDebug() << "QLowEnergyController central stateChanged:" << state; + if (state == QLowEnergyController::UnconnectedState) + latestRSSI = "N/A"_ba; + emit leChanged(); + }); + QObject::connect(leCentralController.get(), &QLowEnergyController::rssiRead, + [this](qint16 rssi) { + qDebug() << "QLowEnergyController central RSSI updated:" << rssi; + latestRSSI = QByteArray::number(rssi); + emit leChanged(); + }); +} + +void BtLocalDevice::centralDiscoverServiceDetails() +{ + qDebug() << "###### Discover Service details"; + if (!leCentralController) { + qDebug() << "Central does not exist"; + return; + } + leCentralService.reset( + leCentralController->createServiceObject(QBluetoothUuid(leServiceUuid))); + emit leChanged(); + if (!leCentralService) { + qDebug() << "Service creation failed, cannot discover details"; + return; + } + QObject::connect(leCentralService.get(), &QLowEnergyService::stateChanged, + [this](QLowEnergyService::ServiceState state){ + qDebug() << "LE central service state changed:" << state; + emit leChanged(); + }); + QObject::connect(leCentralService.get(), &QLowEnergyService::characteristicWritten, + [](const QLowEnergyCharacteristic&, const QByteArray& value){ + qDebug() << "LE central service characteristic value written" << value; + }); + QObject::connect(leCentralService.get(), &QLowEnergyService::characteristicRead, + [](const QLowEnergyCharacteristic&, const QByteArray& value){ + qDebug() << "LE central service characteristic value read" << value; + }); + QObject::connect(leCentralService.get(), &QLowEnergyService::characteristicChanged, + [](const QLowEnergyCharacteristic&, const QByteArray& value){ + qDebug() << "LE central service characteristic value changed" << value; + }); + QObject::connect(leCentralService.get(), &QLowEnergyService::descriptorRead, + [](const QLowEnergyDescriptor&, const QByteArray& value){ + qDebug() << "LE central service descriptor value read" << value; + }); + QObject::connect(leCentralService.get(), &QLowEnergyService::descriptorWritten, + [this](const QLowEnergyDescriptor&, const QByteArray& value){ + qDebug() << "LE central service descriptor value written" << value; + emit leChanged(); + }); + QObject::connect(leCentralService.get(), &QLowEnergyService::errorOccurred, + [](QLowEnergyService::ServiceError error){ + qDebug() << "LE central service error occurred:" << error; + }); + leCentralService->discoverDetails(QLowEnergyService::FullDiscovery); +} + +void BtLocalDevice::centralConnect() +{ + qDebug() << "######" << "Central connect"; + if (!leCentralController) { + qDebug() << "Create central first"; + return; + } + leCentralController->connectToDevice(); +} + +void BtLocalDevice::dumpLeInfo() +{ + const auto controllerDump = [](QLowEnergyController* controller) { + qDebug() << " State:" << controller->state(); + qDebug() << " Role:" << controller->role(); + qDebug() << " Error:" << controller->error(); + qDebug() << " ErrorString:" << controller->errorString(); + qDebug() << " MTU:" << controller->mtu(); + qDebug() << " Local Address:" << controller->localAddress(); + qDebug() << " RemoteAddress:" << controller->remoteAddress(); + qDebug() << " RemoteName:" << controller->remoteName(); + qDebug() << " Services count:" << controller->services().size(); + }; + qDebug() << "######" << "LE Peripheral controller"; + if (lePeripheralController) + controllerDump(lePeripheralController.get()); + + qDebug() << "######" << "LE Central controller"; + if (leCentralController) + controllerDump(leCentralController.get()); + + qDebug() << "######" << "LE Found peripheral device"; + if (leRemotePeripheralDevice.isValid()) { + qDebug() << " Name:" << leRemotePeripheralDevice.name(); + qDebug() << " UUID:" << leRemotePeripheralDevice.deviceUuid(); + qDebug() << " Address:" << leRemotePeripheralDevice.address(); + } + + const auto serviceDump = [](QLowEnergyService* service){ + qDebug() << " Name:" << service->serviceName(); + qDebug() << " Uuid:" << service->serviceUuid(); + qDebug() << " Error:" << service->error(); + auto characteristics = service->characteristics(); + for (const auto& characteristic : characteristics) { + qDebug() << " Characteristic"; + qDebug() << " Uuid" << characteristic.uuid(); + qDebug() << " Value" << characteristic.value(); + + } + }; + + qDebug() << "######" << "LE Central-side service"; + if (leCentralService) + serviceDump(leCentralService.get()); + + qDebug() << "######" << "LE Peripheral-side service"; + if (!lePeripheralServices.isEmpty()) + serviceDump(lePeripheralServices[0].get()); +} + +void BtLocalDevice::centralSubscribeUnsubscribe() +{ + qDebug() << "######" << "LE Central (Un)Subscribe"; + if (!leCentralService) { + qDebug() << "Service object does not exist"; + return; + } + auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1)); + if (!characteristic.isValid()) { + qDebug() << "Characteristic is not valid"; + return; + } + + auto descriptor = characteristic.descriptor( + QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); + if (!descriptor.isValid()) { + qDebug() << "Descriptor is not valid"; + return; + } + if (descriptor.value() == QByteArray::fromHex("0000")) { + qDebug() << " Subscribing notifications"; + leCentralService->writeDescriptor(descriptor, QByteArray::fromHex("0100")); + } else { + qDebug() << " Unsubscribing notifications"; + leCentralService->writeDescriptor(descriptor, QByteArray::fromHex("0000")); + } + emit leChanged(); +} + +void BtLocalDevice::centralDelete() +{ + qDebug() << "######" << "Delete central" << leCentralController.get(); + leCentralController.reset(nullptr); + latestRSSI = "(N/A)"_ba; + emit leChanged(); +} + +void BtLocalDevice::centralDisconnect() +{ + qDebug() << "######" << "LE central disconnect"; + if (!leCentralController) { + qDebug() << "Create central first"; + return; + } + leCentralController->disconnectFromDevice(); +} + +void BtLocalDevice::peripheralDelete() +{ + qDebug() << "######" << "Delete peripheral" << lePeripheralController.get(); + lePeripheralController.reset(nullptr); + emit leChanged(); +} + +void BtLocalDevice::peripheralDisconnect() +{ + qDebug() << "######" << "LE peripheral disconnect"; + if (!lePeripheralController) { + qDebug() << "Create peripheral first"; + return; + } + lePeripheralController->disconnectFromDevice(); +} + +bool BtLocalDevice::centralExists() const +{ + return leCentralController.get(); +} + +bool BtLocalDevice::centralSubscribed() const +{ + if (!leCentralService) + return false; + + auto characteristic = leCentralService->characteristic(QBluetoothUuid(leCharUuid1)); + if (!characteristic.isValid()) + return false; + + auto descriptor = characteristic.descriptor( + QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); + if (!descriptor.isValid()) + return false; + + return (descriptor.value() != QByteArray::fromHex("0000")); +} + +QByteArray BtLocalDevice::centralState() const +{ + if (!leCentralController) + return "(N/A)"_ba; + + return controllerStateString[leCentralController->state()]; +} + +QByteArray BtLocalDevice::centralServiceState() const +{ + if (!leCentralService) + return "(N/A)"_ba; + + return serviceStateString[leCentralService->state()]; +} + +QByteArray BtLocalDevice::centralError() const +{ + if (!leCentralController) + return "(N/A)"_ba; + + return controllerErrorString[leCentralController->error()]; +} + +QByteArray BtLocalDevice::centralServiceError() const +{ + if (!leCentralService) + return "(N/A)"_ba; + + return serviceErrorString[leCentralService->error()]; +} + +void BtLocalDevice::centralReadRSSI() const +{ + qDebug() << "######" << "LE central readRSSI"; + if (!leCentralController) + return; + leCentralController->readRssi(); +} + +QByteArray BtLocalDevice::centralRSSI() const +{ + return latestRSSI; +} + +QByteArray BtLocalDevice::peripheralState() const +{ + if (!lePeripheralController) + return "(N/A)"_ba; + + return controllerStateString[lePeripheralController->state()]; +} + +QByteArray BtLocalDevice::peripheralServiceState() const +{ + if (lePeripheralServices.isEmpty()) + return "(N/A)"_ba; + + return serviceStateString[lePeripheralServices[0]->state()]; +} + +QByteArray BtLocalDevice::peripheralError() const +{ + if (!lePeripheralController) + return "(N/A)"_ba; + + return controllerErrorString[lePeripheralController->error()]; +} + +QByteArray BtLocalDevice::peripheralServiceError() const +{ + if (lePeripheralServices.isEmpty()) + return "(N/A)"_ba; + + return serviceErrorString[lePeripheralServices[0]->error()]; +} + +bool BtLocalDevice::peripheralExists() const +{ + return lePeripheralController.get(); +} |