/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2017-2018 Simon Stürz * * * * This file is part of nymea. * * * * This library 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 2.1 of the License, or (at your option) any later version. * * * * This library 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 this library; If not, see * * . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "bluetoothlowenergymanagerimplementation.h" #include "loggingcategories.h" namespace nymeaserver { BluetoothLowEnergyManagerImplementation::BluetoothLowEnergyManagerImplementation(PluginTimer *reconnectTimer, QObject *parent) : BluetoothLowEnergyManager(parent), m_reconnectTimer(reconnectTimer) { // Check which bluetooth adapter are available QList bluetoothAdapters = QBluetoothLocalDevice::allDevices(); if (bluetoothAdapters.isEmpty()) { qCWarning(dcBluetooth()) << "No bluetooth adapter found. Resource not available."; m_available = false; return; } // Create a scanner for each adapter foreach (const QBluetoothHostInfo &hostInfo, bluetoothAdapters) { qCDebug(dcBluetooth()) << "Using adapter:" << hostInfo.name() << hostInfo.address().toString(); QBluetoothLocalDevice localDevice(hostInfo.address()); localDevice.powerOn(); localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable); QBluetoothDeviceDiscoveryAgent *discoveryAgent = new QBluetoothDeviceDiscoveryAgent(hostInfo.address(), this); connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothLowEnergyManagerImplementation::onDeviceDiscovered); connect(discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error))); m_bluetoothDiscoveryAgents.append(discoveryAgent); } // Discovery timer, interval depends on discovery call m_timer = new QTimer(this); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &BluetoothLowEnergyManagerImplementation::onDiscoveryTimeout); // Reconnect timer connect(m_reconnectTimer, &PluginTimer::timeout, this, &BluetoothLowEnergyManagerImplementation::onReconnectTimeout); qCDebug(dcHardware()) << "-->" << name() << "created successfully."; m_available = true; } BluetoothDiscoveryReply *BluetoothLowEnergyManagerImplementation::discoverDevices(int interval) { // Create the reply for this discovery request QPointer reply = new BluetoothDiscoveryReplyImplementation(this); if (!available()) { qCWarning(dcBluetooth()) << "is not avilable."; reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorNotAvailable); reply->setFinished(); return reply.data(); } if (!enabled()) { qCWarning(dcBluetooth()) << "is not enabled."; reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorNotEnabled); reply->setFinished(); return reply.data(); } if (!m_currentReply.isNull()) { qCWarning(dcBluetooth()) << "resource busy. There is already a discovery running."; reply->setError(BluetoothDiscoveryReplyImplementation::BluetoothDiscoveryReplyErrorBusy); reply->setFinished(); return reply.data(); } m_currentReply = reply; m_discoveredDevices.clear(); // Start discovery on all adapters qCDebug(dcBluetooth()) << "Start bluetooth discovery"; foreach (QBluetoothDeviceDiscoveryAgent *discoveryAgent, m_bluetoothDiscoveryAgents) { discoveryAgent->start(); } // Prevent blocking the hardware resource from plugins int finalInterval = interval; if (finalInterval > 30000) { qCWarning(dcBluetooth()) << "Discovery interval out of range. Reset to 30 seconds."; finalInterval = 30000; } if (finalInterval <= 0) { qCWarning(dcBluetooth()) << "Discovery interval out of range. Reset to 5 seconds."; finalInterval = 5000; } m_timer->start(finalInterval); return reply.data(); } BluetoothLowEnergyDevice *BluetoothLowEnergyManagerImplementation::registerDevice(const QBluetoothDeviceInfo &deviceInfo, const QLowEnergyController::RemoteAddressType &addressType) { QPointer bluetoothDevice = new BluetoothLowEnergyDeviceImplementation(deviceInfo, addressType, this); qCDebug(dcBluetooth()) << "Register device" << bluetoothDevice->name() << bluetoothDevice->address().toString(); m_devices.append(bluetoothDevice); return bluetoothDevice.data(); } void BluetoothLowEnergyManagerImplementation::unregisterDevice(BluetoothLowEnergyDevice *bluetoothDevice) { QPointer devicePointer(bluetoothDevice); if (devicePointer.isNull()) { qCWarning(dcBluetooth()) << "Cannot unregister bluetooth device. Looks like the device is already unregistered."; return; } qCDebug(dcBluetooth()) << "Unregister device" << bluetoothDevice->name() << bluetoothDevice->address().toString(); foreach (QPointer dPointer, m_devices) { if (devicePointer.data() == dPointer.data()) { m_devices.removeAll(dPointer); dPointer->deleteLater(); } } } bool BluetoothLowEnergyManagerImplementation::available() const { return m_available; } bool BluetoothLowEnergyManagerImplementation::enabled() const { return m_enabled; } void BluetoothLowEnergyManagerImplementation::setEnabled(bool enabled) { qCDebug(dcBluetooth()) << "Set" << (enabled ? "enabled" : "disabled"); if (m_enabled && enabled) { qCDebug(dcBluetooth()) << "Already enabled."; return; } else if (!m_enabled && !enabled) { qCDebug(dcBluetooth()) << "Already disabled."; return; } bool success = false; if (enabled) { success = enable(); } else { success = disable(); } if (success) { m_enabled = enabled; emit enabledChanged(m_enabled); } } void BluetoothLowEnergyManagerImplementation::onReconnectTimeout() { // Reconnect device if enabled and disconnected foreach (BluetoothLowEnergyDevice *device, m_devices) { if (device->autoConnecting() && !device->connected()) { device->connectDevice(); } } } void BluetoothLowEnergyManagerImplementation::onDiscoveryTimeout() { // Stop discovery on all adapters qCDebug(dcBluetooth()) << "Stop bluetooth discovery"; foreach (QBluetoothDeviceDiscoveryAgent *discoveryAgent, m_bluetoothDiscoveryAgents) { discoveryAgent->stop(); } qCDebug(dcBluetooth()) << "Discovery finished. Found" << m_discoveredDevices.count() << "bluetooth devices."; if (m_currentReply.isNull()) { qCWarning(dcBluetooth()) << "Reply does not exist any more. Please don't delete the reply before it has finished."; m_currentReply.clear(); return; } m_currentReply->setError(BluetoothDiscoveryReply::BluetoothDiscoveryReplyErrorNoError); m_currentReply->setDiscoveredDevices(m_discoveredDevices); m_currentReply->setFinished(); m_currentReply.clear(); } void BluetoothLowEnergyManagerImplementation::onDeviceDiscovered(const QBluetoothDeviceInfo &deviceInfo) { // Add the device to the list if not already added bool alreadyAdded = false; foreach (const QBluetoothDeviceInfo &device, m_discoveredDevices) { if (device.address() == deviceInfo.address()) { alreadyAdded = true; } } // Note: only show low energy devices if (!alreadyAdded && deviceInfo.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) { qCDebug(dcBluetooth()) << "device discovered" << deviceInfo.name() << deviceInfo.address().toString(); m_discoveredDevices.append(deviceInfo); } } void BluetoothLowEnergyManagerImplementation::onDiscoveryError(const QBluetoothDeviceDiscoveryAgent::Error &error) { QBluetoothDeviceDiscoveryAgent *discoveryAgent = static_cast(sender()); qCWarning(dcBluetooth()) << "Discovery error:" << error << discoveryAgent->errorString(); } bool BluetoothLowEnergyManagerImplementation::enable() { if (!available()) { qCWarning(dcBluetooth()) << "Bluetooth hardware not available. Cannot enable Hardware resource"; return false; } foreach (QPointer bluetoothDevice, m_devices) { bluetoothDevice->setEnabled(true); } qCDebug(dcBluetooth()) << "Hardware resource enabled."; return true; } bool BluetoothLowEnergyManagerImplementation::disable() { foreach (QPointer bluetoothDevice, m_devices) { bluetoothDevice->setEnabled(false); } qCDebug(dcBluetooth()) << "Hardware resource disabled."; return true; } }