6#include <QtBluetooth/QBluetoothHostInfo>
7#include <QtBluetooth/QBluetoothUuid>
8#include <QtConcurrent/QtConcurrentRun>
9#include <QtCore/QCoreApplication>
10#include <QtCore/QPermissions>
11#include <QtCore/QTimer>
12#include <QtCore/QVariantMap>
22 qCDebug(BluetoothLinkLog) <<
this;
28 , _device(copy->device())
29 , _connectedRssi(copy->connectedRssi())
30 , _serviceUuid(copy->_serviceUuid)
31 , _readCharacteristicUuid(copy->readCharacteristicUuid())
32 , _writeCharacteristicUuid(copy->writeCharacteristicUuid())
34 qCDebug(BluetoothLinkLog) <<
this;
37BluetoothConfiguration::~BluetoothConfiguration()
41 if (_adapterWatcher) {
42 _adapterWatcher->cancel();
43 _adapterWatcher->waitForFinished();
46 qCDebug(BluetoothLinkLog) <<
this;
49bool BluetoothConfiguration::_createLocalDevice(
const QBluetoothAddress &address)
52 _localDevice->disconnect();
53 _localDevice->deleteLater();
54 _localDevice =
nullptr;
57 _localDevice =
new QBluetoothLocalDevice(address,
this);
59 if (!_localDevice->isValid()) {
60 qCWarning(BluetoothLinkLog) <<
"Failed to initialize Bluetooth adapter:" << address.toString();
61 _localDevice->deleteLater();
62 _localDevice =
nullptr;
66 _connectLocalDeviceSignals();
68 qCDebug(BluetoothLinkLog) <<
"Initialized Bluetooth adapter:" << _localDevice->name() << _localDevice->address().toString();
73void BluetoothConfiguration::_connectLocalDeviceSignals()
75 if (!_localDevice || !_localDevice->isValid()) {
79 (void) connect(_localDevice.data(), &QBluetoothLocalDevice::hostModeStateChanged,
80 this, &BluetoothConfiguration::_onHostModeStateChanged);
81 (void) connect(_localDevice.data(), &QBluetoothLocalDevice::deviceConnected,
82 this, &BluetoothConfiguration::_onDeviceConnected);
83 (void) connect(_localDevice.data(), &QBluetoothLocalDevice::deviceDisconnected,
84 this, &BluetoothConfiguration::_onDeviceDisconnected);
85 (void) connect(_localDevice.data(), &QBluetoothLocalDevice::pairingFinished,
86 this, &BluetoothConfiguration::_onPairingFinished);
87 (void) connect(_localDevice.data(), &QBluetoothLocalDevice::errorOccurred,
88 this, &BluetoothConfiguration::_onLocalDeviceErrorOccurred);
91void BluetoothConfiguration::_initDeviceDiscoveryAgent()
93 if (!_deviceDiscoveryAgent) {
94 _deviceDiscoveryAgent =
new QBluetoothDeviceDiscoveryAgent(
this);
96 _deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(15000);
98 (void) connect(_deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
99 this, &BluetoothConfiguration::_deviceDiscovered);
100 (void) connect(_deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::canceled,
102 (void) connect(_deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::finished,
103 this, &BluetoothConfiguration::_onDiscoveryFinished);
104 (void) connect(_deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::errorOccurred,
105 this, &BluetoothConfiguration::_onDiscoveryErrorOccurred);
107 (void) connect(_deviceDiscoveryAgent.data(), &QBluetoothDeviceDiscoveryAgent::deviceUpdated,
108 this, &BluetoothConfiguration::_deviceUpdated);
112 if (!_availableAdapters.isEmpty()) {
113 const QVariantMap adapter = _availableAdapters.first().toMap();
114 const QBluetoothAddress address(adapter.value(
"address").toString());
115 if (!address.isNull()) {
116 (void) _createLocalDevice(address);
119 _refreshAvailableAdaptersAsync();
124void BluetoothConfiguration::_refreshAvailableAdaptersAsync()
126 if (_adapterEnumerationInProgress) {
130 _adapterEnumerationInProgress =
true;
132 if (!_adapterWatcher) {
133 _adapterWatcher =
new QFutureWatcher<QList<QBluetoothHostInfo>>(
this);
134 (void) connect(_adapterWatcher.data(), &QFutureWatcher<QList<QBluetoothHostInfo>>::finished,
this, [
this]() {
135 _adapterEnumerationInProgress =
false;
136 _updateAvailableAdapters(_adapterWatcher->future().result());
140 _adapterWatcher->setFuture(QtConcurrent::run([]() {
141 return QBluetoothLocalDevice::allDevices();
145void BluetoothConfiguration::_updateAvailableAdapters(
const QList<QBluetoothHostInfo> &hosts)
147 QVariantList adapters;
148 const QBluetoothAddress selectedAddress = (_localDevice && _localDevice->isValid())
149 ? _localDevice->address()
150 : QBluetoothAddress();
152 for (
const QBluetoothHostInfo &host : hosts) {
154 adapter[
"name"] = host.name();
155 adapter[
"address"] = host.address().toString();
156 adapter[
"selected"] = (!selectedAddress.isNull() && (host.address() == selectedAddress));
157 adapters.append(adapter);
160 if (_availableAdapters != adapters) {
161 _availableAdapters = adapters;
166bool BluetoothConfiguration::_ensureLocalDevice()
168 if (_localDevice && _localDevice->isValid()) {
173 for (
const QVariant &entry : std::as_const(_availableAdapters)) {
174 const QBluetoothAddress address(entry.toMap().value(
"address").toString());
175 if (!address.isNull() && _createLocalDevice(address)) {
181 const QList<QBluetoothHostInfo> hosts = QBluetoothLocalDevice::allDevices();
182 if (!hosts.isEmpty()) {
183 _updateAvailableAdapters(hosts);
184 for (
const QBluetoothHostInfo &host : hosts) {
185 if (!host.address().isNull() && _createLocalDevice(host.address())) {
192 _localDevice =
new QBluetoothLocalDevice(
this);
193 if (_localDevice->isValid()) {
194 _connectLocalDeviceSignals();
199 _localDevice->deleteLater();
200 _localDevice =
nullptr;
204void BluetoothConfiguration::_deviceUpdated(
const QBluetoothDeviceInfo &info, QBluetoothDeviceInfo::Fields updatedFields)
206 qCDebug(BluetoothLinkLog) <<
"Device Updated:" << info.name() <<
"Fields:" << updatedFields;
208 if (!info.isValid() || (updatedFields == QBluetoothDeviceInfo::Field::None)) {
209 qCDebug(BluetoothLinkVerboseLog) <<
"Ignoring device update: invalid or no updated fields"
210 << info.name() << info.address().toString() << updatedFields;
215 for (QBluetoothDeviceInfo &dev : _deviceList) {
216 if (dev.address() == info.address()) {
218 if (updatedFields & QBluetoothDeviceInfo::Field::RSSI) {
219 dev.setRssi(info.rssi());
221 if (updatedFields & QBluetoothDeviceInfo::Field::ManufacturerData) {
222 const QMultiHash<quint16, QByteArray> data = info.manufacturerData();
223 for (quint16
id : info.manufacturerIds()) {
224 dev.setManufacturerData(
id, data.value(
id));
227 if (updatedFields & QBluetoothDeviceInfo::Field::ServiceData) {
228 const QMultiHash<QBluetoothUuid, QByteArray> data = info.serviceData();
229 for (
const QBluetoothUuid &uuid : info.serviceIds()) {
230 dev.setServiceData(uuid, data.value(uuid));
233 if (updatedFields & QBluetoothDeviceInfo::Field::All) {
237 if (_device.address() == dev.address()) {
244 if (!found && _isDeviceCompatibleWithMode(info)) {
245 _deviceList.append(info);
247 if (_device.address() == info.address()) {
253bool BluetoothConfiguration::_isDeviceCompatibleWithMode(
const QBluetoothDeviceInfo &info)
const
257 if (info.coreConfigurations() == QBluetoothDeviceInfo::UnknownCoreConfiguration) {
261 if (_mode == BluetoothMode::ModeLowEnergy) {
262 return (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
264 return (info.coreConfigurations() & QBluetoothDeviceInfo::BaseRateCoreConfiguration) ||
265 (info.coreConfigurations() & QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
269void BluetoothConfiguration::setMode(BluetoothMode mode)
283 LinkConfiguration::copyFrom(source);
286 if (!bluetoothSource) {
287 qCWarning(BluetoothLinkLog) <<
"Invalid source configuration type";
291 if (_mode != bluetoothSource->mode()) {
292 _mode = bluetoothSource->mode();
296 if (_device.address() != bluetoothSource->device().address()) {
297 _device = bluetoothSource->device();
301 if (_serviceUuid != bluetoothSource->_serviceUuid) {
302 _serviceUuid = bluetoothSource->_serviceUuid;
306 if (_readCharacteristicUuid != bluetoothSource->readCharacteristicUuid()) {
307 _readCharacteristicUuid = bluetoothSource->readCharacteristicUuid();
311 if (_writeCharacteristicUuid != bluetoothSource->writeCharacteristicUuid()) {
312 _writeCharacteristicUuid = bluetoothSource->writeCharacteristicUuid();
316 if (_connectedRssi != bluetoothSource->connectedRssi()) {
317 _connectedRssi = bluetoothSource->connectedRssi();
322void BluetoothConfiguration::loadSettings(QSettings &settings,
const QString &root)
324 settings.beginGroup(root);
326 _mode =
static_cast<BluetoothMode
>(settings.value(
"mode",
static_cast<int>(BluetoothMode::ModeClassic)).toInt());
328 const QString deviceName = settings.value(
"deviceName").toString();
329 const QBluetoothAddress address(settings.value(
"address").toString());
331 if (!deviceName.isEmpty() && !address.isNull()) {
332 _device = QBluetoothDeviceInfo(address, deviceName, 0);
335 _serviceUuid = QBluetoothUuid(settings.value(
"serviceUuid", _serviceUuid.toString()).toString());
336 _readCharacteristicUuid = QBluetoothUuid(settings.value(
"readCharUuid", _readCharacteristicUuid.toString()).toString());
337 _writeCharacteristicUuid = QBluetoothUuid(settings.value(
"writeCharUuid", _writeCharacteristicUuid.toString()).toString());
342void BluetoothConfiguration::saveSettings(QSettings &settings,
const QString &root)
const
344 settings.beginGroup(root);
346 settings.setValue(
"mode",
static_cast<int>(_mode));
347 settings.setValue(
"deviceName", _device.name());
348 settings.setValue(
"address", _device.address().toString());
349 settings.setValue(
"serviceUuid", _serviceUuid.toString());
350 settings.setValue(
"readCharUuid", _readCharacteristicUuid.toString());
351 settings.setValue(
"writeCharUuid", _writeCharacteristicUuid.toString());
356QString BluetoothConfiguration::settingsTitle()
const
358 if (isBluetoothAvailable()) {
359 return (_mode == BluetoothMode::ModeLowEnergy) ? tr(
"Bluetooth Low Energy Link Settings") : tr(
"Bluetooth Link Settings");
361 return tr(
"Bluetooth Not Available");
364bool BluetoothConfiguration::isBluetoothAvailable()
369QVariantList BluetoothConfiguration::devicesModel()
const
371 return _devicesModelCache;
374void BluetoothConfiguration::setConnectedRssi(qint16 rssi)
376 if (_connectedRssi != rssi) {
377 _connectedRssi = rssi;
383qint16 BluetoothConfiguration::selectedRssi()
const
385 const QBluetoothAddress selAddr = _device.address();
386 if (!selAddr.isNull()) {
387 for (
const QBluetoothDeviceInfo &dev : _deviceList) {
388 if (dev.address() == selAddr) {
396void BluetoothConfiguration::setServiceUuid(
const QString &uuid)
398 const QBluetoothUuid newUuid(uuid);
399 if (!uuid.isEmpty() && newUuid.isNull()) {
400 emit
errorOccurred(tr(
"Invalid service UUID format: %1").arg(uuid));
403 if (_serviceUuid != newUuid) {
404 _serviceUuid = newUuid;
409void BluetoothConfiguration::setReadUuid(
const QString &uuid)
411 const QBluetoothUuid newUuid(uuid);
412 if (!uuid.isEmpty() && newUuid.isNull()) {
413 emit
errorOccurred(tr(
"Invalid read characteristic UUID format: %1").arg(uuid));
416 if (_readCharacteristicUuid != newUuid) {
417 _readCharacteristicUuid = newUuid;
422void BluetoothConfiguration::setWriteUuid(
const QString &uuid)
424 const QBluetoothUuid newUuid(uuid);
425 if (!uuid.isEmpty() && newUuid.isNull()) {
426 emit
errorOccurred(tr(
"Invalid write characteristic UUID format: %1").arg(uuid));
429 if (_writeCharacteristicUuid != newUuid) {
430 _writeCharacteristicUuid = newUuid;
435void BluetoothConfiguration::startScan()
437 _initDeviceDiscoveryAgent();
439 if (!_deviceDiscoveryAgent) {
444 QBluetoothPermission permission;
445 permission.setCommunicationModes(QBluetoothPermission::Access);
446 const Qt::PermissionStatus status = QCoreApplication::instance()->checkPermission(permission);
447 if (status == Qt::PermissionStatus::Undetermined) {
448 QCoreApplication::instance()->requestPermission(permission,
this, [
this](
const QPermission &perm) {
449 if (perm.status() == Qt::PermissionStatus::Granted) {
452 errorOccurred(tr(
"Bluetooth Permission Denied"));
456 }
else if (status != Qt::PermissionStatus::Granted) {
462 if (_localDevice && _localDevice->isValid() && (_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff)) {
463 _localDevice->powerOn();
469 if (_mode == BluetoothMode::ModeLowEnergy) {
470 _deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
472 _deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::ClassicMethod);
478void BluetoothConfiguration::stopScan()
480 if (_deviceDiscoveryAgent && scanning()) {
481 _deviceDiscoveryAgent->stop();
485void BluetoothConfiguration::setDevice(
const QString &name)
487 for (
const QBluetoothDeviceInfo &info : std::as_const(_deviceList)) {
488 if (info.name() == name) {
490 _applySelectedDevice(info);
496void BluetoothConfiguration::setDeviceByAddress(
const QString &address)
498 if (address.isEmpty()) {
501 const QBluetoothAddress addr(address);
506 for (
const QBluetoothDeviceInfo &info : std::as_const(_deviceList)) {
507 if (info.address() == addr) {
509 _applySelectedDevice(info);
515void BluetoothConfiguration::_applySelectedDevice(
const QBluetoothDeviceInfo &info)
519 if (_mode == BluetoothMode::ModeLowEnergy) {
520 const QList<QBluetoothUuid> serviceUuids = info.serviceUuids();
521 if (!serviceUuids.isEmpty()) {
522 QBluetoothUuid serviceUuid = QBluetoothUuid();
523 for (
const QBluetoothUuid &uuid : serviceUuids) {
524 if ((uuid == NORDIC_UART_SERVICE) || (uuid == TI_SENSORTAG_SERVICE)) {
527 if (uuid == NORDIC_UART_SERVICE) {
528 _readCharacteristicUuid = NORDIC_UART_RX_CHAR;
529 _writeCharacteristicUuid = NORDIC_UART_TX_CHAR;
531 _readCharacteristicUuid = TI_SENSORTAG_CHAR;
532 _writeCharacteristicUuid = TI_SENSORTAG_CHAR;
538 if (serviceUuid.isNull()) {
539 _serviceUuid = serviceUuids.first();
540 _readCharacteristicUuid = QBluetoothUuid();
541 _writeCharacteristicUuid = QBluetoothUuid();
543 _serviceUuid = serviceUuid;
555bool BluetoothConfiguration::scanning()
const
557 return _deviceDiscoveryAgent && _deviceDiscoveryAgent->isActive();
560void BluetoothConfiguration::_requestHostMode(QBluetoothLocalDevice::HostMode mode)
562 if (!_localDevice || !_localDevice->isValid()) {
567 if (_localDevice->hostMode() == mode) {
572 if (_hostModeRequestPending) {
573 _queuedHostMode = mode;
574 _hasQueuedHostModeRequest =
true;
575 if (_requestedHostMode != mode) {
576 qCDebug(BluetoothLinkLog) <<
"Host mode change already pending; queueing request"
577 << _requestedHostMode <<
"->" << mode;
582 _requestedHostMode = mode;
583 _hasQueuedHostModeRequest =
false;
584 _hostModeRequestPending =
true;
585 _localDevice->setHostMode(mode);
588void BluetoothConfiguration::_deviceDiscovered(
const QBluetoothDeviceInfo &info)
590 if (!info.isValid()) {
591 qCDebug(BluetoothLinkVerboseLog) <<
"Ignoring discovered device: invalid device info"
592 << info.name() << info.address().toString();
596 if (!_isDeviceCompatibleWithMode(info)) {
597 qCDebug(BluetoothLinkVerboseLog) <<
"Ignoring discovered device: incompatible with mode"
598 << ((_mode == BluetoothMode::ModeLowEnergy) ?
"BLE" :
"Classic")
599 << info.name() << info.address().toString()
600 <<
"CoreCfg:" << info.coreConfigurations();
604 int existingIndex = -1;
605 for (
int i = 0; i < _deviceList.size(); ++i) {
606 if (_deviceList[i].address() == info.address()) {
612 if (existingIndex >= 0) {
613 _deviceList[existingIndex] = info;
619 if (_mode == BluetoothMode::ModeLowEnergy) {
620 int sameNameIndex = -1;
621 for (
int i = 0; i < _deviceList.size(); ++i) {
622 if (_deviceList[i].name() == info.name()) {
627 if (sameNameIndex >= 0) {
628 const bool replace = (_deviceList[sameNameIndex].address() != info.address());
630 _deviceList[sameNameIndex] = info;
633 if ((_device.name() == info.name()) && (_device.address() != info.address())) {
645 _deviceList.append(info);
648 qCDebug(BluetoothLinkLog) << ((_mode == BluetoothMode::ModeLowEnergy) ?
"BLE" :
"Classic")
649 <<
"device discovered:" << info.name()
650 <<
"Address:" << info.address().toString()
651 <<
"RSSI:" << info.rssi();
654void BluetoothConfiguration::_onDiscoveryFinished()
660void BluetoothConfiguration::_updateDeviceList()
663 for (
const QBluetoothDeviceInfo &info : std::as_const(_deviceList)) {
665 device[
"name"] = info.name();
666 device[
"address"] = info.address().toString();
667 if (info.rssi() != 0) {
668 device[
"rssi"] = info.rssi();
670 model.append(device);
672 if (_devicesModelCache != model) {
673 _devicesModelCache = model;
674 qCDebug(BluetoothLinkVerboseLog) <<
"Bluetooth devices model updated. Count:" << _devicesModelCache.size();
679void BluetoothConfiguration::_onDiscoveryErrorOccurred(QBluetoothDeviceDiscoveryAgent::Error
error)
682 if (_deviceDiscoveryAgent) {
683 errorString = _deviceDiscoveryAgent->errorString();
688 qCWarning(BluetoothLinkLog) <<
"Bluetooth discovery error:" <<
error <<
errorString;
692void BluetoothConfiguration::_onHostModeStateChanged(QBluetoothLocalDevice::HostMode mode)
694 _hostModeRequestPending =
false;
698 case QBluetoothLocalDevice::HostPoweredOff:
699 modeString = tr(
"Powered Off");
701 case QBluetoothLocalDevice::HostConnectable:
702 modeString = tr(
"Connectable");
704 case QBluetoothLocalDevice::HostDiscoverable:
705 modeString = tr(
"Discoverable");
707 case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry:
708 modeString = tr(
"Discoverable (Limited Inquiry)");
712 qCDebug(BluetoothLinkLog) <<
"Bluetooth adapter mode changed to:" << modeString;
715 if (_hasQueuedHostModeRequest) {
716 const QBluetoothLocalDevice::HostMode queuedMode = _queuedHostMode;
717 _hasQueuedHostModeRequest =
false;
718 if (queuedMode != mode) {
719 QTimer::singleShot(0,
this, [
this, queuedMode]() {
720 _requestHostMode(queuedMode);
725 if ((mode == QBluetoothLocalDevice::HostPoweredOff) && scanning()) {
731void BluetoothConfiguration::_onDeviceConnected(
const QBluetoothAddress &address)
733 qCDebug(BluetoothLinkLog) <<
"Device connected to adapter:" << address.toString();
734 for (
const QBluetoothDeviceInfo &dev : std::as_const(_deviceList)) {
735 if (dev.address() == address) {
736 qCDebug(BluetoothLinkLog) <<
"Connected device:" << dev.name();
742void BluetoothConfiguration::_onDeviceDisconnected(
const QBluetoothAddress &address)
744 qCDebug(BluetoothLinkLog) <<
"Device disconnected from adapter:" << address.toString();
745 for (
const QBluetoothDeviceInfo &dev : std::as_const(_deviceList)) {
746 if (dev.address() == address) {
747 qCDebug(BluetoothLinkLog) <<
"Disconnected device:" << dev.name();
753void BluetoothConfiguration::_onPairingFinished(
const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing)
755 QString pairingStatus;
757 case QBluetoothLocalDevice::Unpaired:
758 pairingStatus = tr(
"Unpaired");
760 case QBluetoothLocalDevice::Paired:
761 pairingStatus = tr(
"Paired");
763 case QBluetoothLocalDevice::AuthorizedPaired:
764 pairingStatus = tr(
"Authorized Paired");
768 qCDebug(BluetoothLinkLog) <<
"Pairing finished for device:" << address.toString() <<
"Status:" << pairingStatus;
771 for (
const QBluetoothDeviceInfo &dev : std::as_const(_deviceList)) {
772 if (dev.address() == address) {
773 deviceName = dev.name();
778 if (pairing == QBluetoothLocalDevice::Unpaired) {
779 const QString msg = deviceName.isEmpty()
780 ? tr(
"Device %1 unpaired").arg(address.toString())
781 : tr(
"Device %1 (%2) unpaired").arg(deviceName, address.toString());
782 qCInfo(BluetoothLinkLog) << msg;
784 const QString msg = deviceName.isEmpty()
785 ? tr(
"Device %1 paired successfully").arg(address.toString())
786 : tr(
"Device %1 (%2) paired successfully").arg(deviceName, address.toString());
787 qCInfo(BluetoothLinkLog) << msg;
794void BluetoothConfiguration::_onLocalDeviceErrorOccurred(QBluetoothLocalDevice::Error
error)
798 case QBluetoothLocalDevice::PairingError:
801 case QBluetoothLocalDevice::MissingPermissionsError:
804 case QBluetoothLocalDevice::UnknownError:
806 errorString = tr(
"Unknown Bluetooth Adapter Error");
810 qCWarning(BluetoothLinkLog) <<
"Local Bluetooth device error:" <<
error <<
errorString;
814void BluetoothConfiguration::requestPairing(
const QString &address)
816 if (!_localDevice || !_localDevice->isValid()) {
821 if (_mode != BluetoothMode::ModeClassic) {
822 emit
errorOccurred(tr(
"Pairing is only supported for Classic Bluetooth"));
826 const QBluetoothAddress addr(address);
832 qCDebug(BluetoothLinkLog) <<
"Requesting pairing with device:" << address;
833 _localDevice->requestPairing(addr, QBluetoothLocalDevice::Paired);
836void BluetoothConfiguration::removePairing(
const QString &address)
838 if (!_localDevice || !_localDevice->isValid()) {
843 if (_mode != BluetoothMode::ModeClassic) {
844 emit
errorOccurred(tr(
"Unpairing is only supported for Classic Bluetooth"));
848 const QBluetoothAddress addr(address);
854 qCDebug(BluetoothLinkLog) <<
"Removing pairing with device:" << address;
855 _localDevice->requestPairing(addr, QBluetoothLocalDevice::Unpaired);
858QString BluetoothConfiguration::getPairingStatus(
const QString &address)
const
860 if (!_localDevice || !_localDevice->isValid()) {
861 return tr(
"Adapter unavailable");
864 if (_mode != BluetoothMode::ModeClassic) {
865 return tr(
"N/A (BLE mode)");
868 const QBluetoothAddress addr(address);
870 return tr(
"Invalid address");
873 const QBluetoothLocalDevice::Pairing pairingStatus = _localDevice->pairingStatus(addr);
874 switch (pairingStatus) {
875 case QBluetoothLocalDevice::Unpaired:
876 return tr(
"Unpaired");
877 case QBluetoothLocalDevice::Paired:
879 case QBluetoothLocalDevice::AuthorizedPaired:
880 return tr(
"Authorized Paired");
882 return tr(
"Unknown");
886bool BluetoothConfiguration::isPaired(
const QString &address)
const
888 if (!_localDevice || !_localDevice->isValid() || _mode != BluetoothMode::ModeClassic) {
892 const QBluetoothAddress addr(address);
897 const QBluetoothLocalDevice::Pairing status = _localDevice->pairingStatus(addr);
898 return (status == QBluetoothLocalDevice::Paired || status == QBluetoothLocalDevice::AuthorizedPaired);
901bool BluetoothConfiguration::isDiscoverable()
const
903 if (!_localDevice || !_localDevice->isValid()) {
907 const QBluetoothLocalDevice::HostMode mode = _localDevice->hostMode();
908 return (mode == QBluetoothLocalDevice::HostDiscoverable || mode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry);
911bool BluetoothConfiguration::isAdapterAvailable()
const
913 return _localDevice && _localDevice->isValid();
916QString BluetoothConfiguration::getAdapterAddress()
const
918 if (!_localDevice || !_localDevice->isValid()) {
921 return _localDevice->address().toString();
924QString BluetoothConfiguration::getAdapterName()
const
926 if (!_localDevice || !_localDevice->isValid()) {
929 return _localDevice->name();
932bool BluetoothConfiguration::isAdapterPoweredOn()
const
934 if (!_localDevice || !_localDevice->isValid()) {
937 return _localDevice->hostMode() != QBluetoothLocalDevice::HostPoweredOff;
940QVariantList BluetoothConfiguration::getAllPairedDevices()
const
942 QVariantList pairedDevices;
944 if (!_localDevice || !_localDevice->isValid()) {
945 return pairedDevices;
948 const QList<QBluetoothAddress> connectedAddresses = _localDevice->connectedDevices();
950 for (
const QBluetoothDeviceInfo &dev : std::as_const(_deviceList)) {
951 const QBluetoothLocalDevice::Pairing pairingStatus = _localDevice->pairingStatus(dev.address());
952 if (pairingStatus != QBluetoothLocalDevice::Unpaired) {
954 device[
"name"] = dev.name();
955 device[
"address"] = dev.address().toString();
956 device[
"paired"] =
true;
958 if (pairingStatus == QBluetoothLocalDevice::AuthorizedPaired) {
959 device[
"pairingStatus"] = tr(
"Authorized");
961 device[
"pairingStatus"] = tr(
"Paired");
964 device[
"connected"] = connectedAddresses.contains(dev.address());
966 if (dev.rssi() != 0) {
967 device[
"rssi"] = dev.rssi();
970 pairedDevices.append(device);
974 return pairedDevices;
977QVariantList BluetoothConfiguration::getConnectedDevices()
const
979 QVariantList connectedDevices;
981 if (!_localDevice || !_localDevice->isValid()) {
982 return connectedDevices;
985 const QList<QBluetoothAddress> connectedAddresses = _localDevice->connectedDevices();
987 for (
const QBluetoothAddress &addr : connectedAddresses) {
989 device[
"address"] = addr.toString();
990 device[
"connected"] =
true;
993 for (
const QBluetoothDeviceInfo &dev : std::as_const(_deviceList)) {
994 if (dev.address() == addr) {
995 deviceName = dev.name();
996 if (dev.rssi() != 0) {
997 device[
"rssi"] = dev.rssi();
1003 device[
"name"] = deviceName.isEmpty() ? tr(
"Unknown Device") : deviceName;
1004 connectedDevices.append(device);
1007 return connectedDevices;
1010void BluetoothConfiguration::powerOnAdapter()
1012 if (!_ensureLocalDevice()) {
1014 _refreshAvailableAdaptersAsync();
1018 const bool wasPoweredOff = (_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff);
1020 qCDebug(BluetoothLinkLog) <<
"Powering on Bluetooth adapter";
1024 if (wasPoweredOff) {
1025 _requestedHostMode = QBluetoothLocalDevice::HostConnectable;
1026 _hostModeRequestPending =
true;
1029 _localDevice->powerOn();
1033 if (wasPoweredOff && !_deferredPowerOnFixPending) {
1034 _deferredPowerOnFixPending =
true;
1035 QTimer::singleShot(1500,
this, [
this]() {
1036 _deferredPowerOnFixPending =
false;
1037 if (!_localDevice || !_localDevice->isValid()) {
1041 if (_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) {
1042 _hostModeRequestPending = false;
1043 qCDebug(BluetoothLinkLog) <<
"Power-on fallback: requesting connectable host mode";
1044 _requestHostMode(QBluetoothLocalDevice::HostConnectable);
1045 }
else if (_hostModeRequestPending) {
1047 _hostModeRequestPending =
false;
1049 if (_hasQueuedHostModeRequest) {
1050 const QBluetoothLocalDevice::HostMode queuedMode = _queuedHostMode;
1051 _hasQueuedHostModeRequest =
false;
1052 if (queuedMode != _localDevice->hostMode()) {
1053 _requestHostMode(queuedMode);
1063void BluetoothConfiguration::powerOffAdapter()
1065 if (!_localDevice || !_localDevice->isValid()) {
1074 if (_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) {
1078 qCDebug(BluetoothLinkLog) <<
"Powering off Bluetooth adapter";
1079 _requestHostMode(QBluetoothLocalDevice::HostPoweredOff);
1082void BluetoothConfiguration::setAdapterDiscoverable(
bool discoverable)
1084 if (!_localDevice || !_localDevice->isValid()) {
1090 if (_localDevice->hostMode() == QBluetoothLocalDevice::HostDiscoverable) {
1093 qCDebug(BluetoothLinkLog) <<
"Making Bluetooth adapter discoverable";
1094 _requestHostMode(QBluetoothLocalDevice::HostDiscoverable);
1096 if (_localDevice->hostMode() == QBluetoothLocalDevice::HostConnectable) {
1099 qCDebug(BluetoothLinkLog) <<
"Making Bluetooth adapter connectable only";
1100 _requestHostMode(QBluetoothLocalDevice::HostConnectable);
1104QVariantList BluetoothConfiguration::getAllAvailableAdapters()
1106 if (_availableAdapters.isEmpty()) {
1107 _refreshAvailableAdaptersAsync();
1109 return _availableAdapters;
1112void BluetoothConfiguration::selectAdapter(
const QString &address)
1114 const QBluetoothAddress addr(address);
1115 if (addr.isNull()) {
1120 if (!_availableAdapters.isEmpty()) {
1122 for (
const QVariant &entry : std::as_const(_availableAdapters)) {
1123 const QVariantMap adapter = entry.toMap();
1124 if (QBluetoothAddress(adapter.value(
"address").toString()) == addr) {
1131 _refreshAvailableAdaptersAsync();
1140 if (!_createLocalDevice(addr)) {
1142 _refreshAvailableAdaptersAsync();
1146 bool selectionUpdated =
false;
1147 for (QVariant &entry : _availableAdapters) {
1148 QVariantMap adapter = entry.toMap();
1149 if (adapter.isEmpty()) {
1153 const bool selected = (QBluetoothAddress(adapter.value(
"address").toString()) == addr);
1154 if (adapter.value(
"selected").toBool() != selected) {
1155 adapter[
"selected"] = selected;
1157 selectionUpdated =
true;
1161 if (_availableAdapters.isEmpty() && _localDevice && _localDevice->isValid()) {
1162 QVariantMap adapter;
1163 adapter[
"name"] = _localDevice->name();
1164 adapter[
"address"] = _localDevice->address().toString();
1165 adapter[
"selected"] =
true;
1166 _availableAdapters.append(adapter);
1167 selectionUpdated =
true;
1170 if (selectionUpdated) {
1175QString BluetoothConfiguration::getHostMode()
const
1177 if (!_localDevice || !_localDevice->isValid()) {
1178 return tr(
"Unavailable");
1181 switch (_localDevice->hostMode()) {
1182 case QBluetoothLocalDevice::HostPoweredOff:
1183 return tr(
"Powered Off");
1184 case QBluetoothLocalDevice::HostConnectable:
1185 return tr(
"Connectable");
1186 case QBluetoothLocalDevice::HostDiscoverable:
1187 return tr(
"Discoverable");
1188 case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry:
1189 return tr(
"Discoverable (Limited)");
1191 return tr(
"Unknown");
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void serviceUuidChanged()
void errorOccurred(const QString &errorString)
void devicesModelChanged()
void pairingStatusChanged()
void connectedRssiChanged()
void selectedRssiChanged()
void adapterStateChanged()
Interface holding link specific settings.
bool isBluetoothAvailable()
Check if Bluetooth is available on this device.