// SPDX-License-Identifier: LGPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of libnymea-app. * * libnymea-app is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * libnymea-app is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libnymea-app. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "bluetoothservicediscovery.h" #include #include "logging.h" NYMEA_LOGGING_CATEGORY(dcBluetoothDiscovery, "BluetoothDiscovery") BluetoothServiceDiscovery::BluetoothServiceDiscovery(NymeaHosts *nymeaHosts, QObject *parent) : QObject(parent), m_nymeaHosts(nymeaHosts) { m_nymeaServiceUuid = QBluetoothUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b")); m_localDevice = new QBluetoothLocalDevice(this); connect(m_localDevice, &QBluetoothLocalDevice::hostModeStateChanged, this, &BluetoothServiceDiscovery::onHostModeChanged); m_serviceDiscovery = new QBluetoothServiceDiscoveryAgent(m_localDevice->address()); connect(m_serviceDiscovery, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, this, &BluetoothServiceDiscovery::onServiceDiscovered); connect(m_serviceDiscovery, &QBluetoothServiceDiscoveryAgent::finished, this, &BluetoothServiceDiscovery::onServiceDiscoveryFinished); } bool BluetoothServiceDiscovery::discovering() const { return m_discovering; } bool BluetoothServiceDiscovery::available() const { if (!m_localDevice) return false; return m_localDevice->isValid() && m_localDevice->hostMode() != QBluetoothLocalDevice::HostPoweredOff; } void BluetoothServiceDiscovery::discover() { m_enabed = true; if (!m_localDevice->isValid() || m_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) { qCWarning(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Bluetooth device not available. Not starting discovery."; return; } m_serviceDiscovery->setUuidFilter(m_nymeaServiceUuid); if (m_discovering) return; qCDebug(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Service scan started for service: " << m_nymeaServiceUuid.toString(); setDiscovering(true); // Delay restarting as Bluez might not be ready just yet QTimer::singleShot(500, this, [this]() { m_serviceDiscovery->start(QBluetoothServiceDiscoveryAgent::FullDiscovery); }); } void BluetoothServiceDiscovery::stopDiscovery() { m_enabed = false; setDiscovering(false); m_serviceDiscovery->stop(); } void BluetoothServiceDiscovery::setDiscovering(const bool &discovering) { if (m_discovering == discovering) return; m_discovering = discovering; emit discoveringChanged(m_discovering); } void BluetoothServiceDiscovery::onHostModeChanged(const QBluetoothLocalDevice::HostMode &mode) { qCDebug(dcBluetoothDiscovery()) << "Bluetooth host mode changed" << mode; if (mode != QBluetoothLocalDevice::HostPoweredOff && m_enabed) { qCDebug(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Bluetooth device available. Starting discovery."; discover(); } if (mode == QBluetoothLocalDevice::HostPoweredOff) { qCDebug(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Bluetooth adapter disabled. Stopping discovering"; m_serviceDiscovery->stop(); } } void BluetoothServiceDiscovery::onServiceDiscovered(const QBluetoothServiceInfo &serviceInfo) { qCDebug(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Discovered service on" << serviceInfo.device().name() << serviceInfo.device().address().toString(); qCDebug(dcBluetoothDiscovery()) << "\tDevive name:" << serviceInfo.device().name(); qCDebug(dcBluetoothDiscovery()) << "\tService name:" << serviceInfo.serviceName(); qCDebug(dcBluetoothDiscovery()) << "\tDescription:" << serviceInfo.attribute(QBluetoothServiceInfo::ServiceDescription).toString(); qCDebug(dcBluetoothDiscovery()) << "\tProvider:" << serviceInfo.attribute(QBluetoothServiceInfo::ServiceProvider).toString(); qCDebug(dcBluetoothDiscovery()) << "\tDocumentation:" << serviceInfo.attribute(QBluetoothServiceInfo::DocumentationUrl).toString(); qCDebug(dcBluetoothDiscovery()) << "\tL2CAP protocol service multiplexer:" << serviceInfo.protocolServiceMultiplexer(); qCDebug(dcBluetoothDiscovery()) << "\tRFCOMM server channel:" << serviceInfo.serverChannel(); if (serviceInfo.serviceClassUuids().isEmpty()) return; if (serviceInfo.serviceClassUuids().first() == QBluetoothUuid(QUuid("997936b5-d2cd-4c57-b41b-c6048320cd2b"))) { qCDebug(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Found nymea rfcom service!"; } } void BluetoothServiceDiscovery::onServiceDiscoveryFinished() { // qDebug() << "BluetoothServiceDiscovery: Service discovery finished."; setDiscovering(false); foreach (const QBluetoothServiceInfo &serviceInfo, m_serviceDiscovery->discoveredServices()) { onServiceDiscovered(serviceInfo); } // If discover was called, but never stopDiscover, continue discovery if (m_enabed) { if (!m_localDevice->isValid() || m_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) { qCWarning(dcBluetoothDiscovery()) << "BluetoothServiceDiscovery: Not restarting discovery, the bluetooth adapter is not available."; return; } discover(); } }