/* **************************************************************************** * Copyright 2019 Open Systems Development BV * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the "Software"), * * to deal in the Software without restriction, including without limitation * * the rights to use, copy, modify, merge, publish, distribute, sublicense, * * and/or sell copies of the Software, and to permit persons to whom the * * Software is furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * * DEALINGS IN THE SOFTWARE. * * ***************************************************************************/ // osdev::components::bluetooth #include "devicehandler.h" #include "deviceinfo.h" #include #include #include using namespace osdev::components::bluetooth; DeviceHandler::DeviceHandler( QObject *parent ) : BluetoothBaseClass( parent ) , m_pControl( nullptr ) , m_pService( nullptr ) , m_notificationDescriptor() , m_pCurrentDevice( nullptr ) , m_foundRequestedService( false ) , m_measuring( false ) , m_measurements() , m_addressType( QLowEnergyController::PublicAddress ) , m_requestedServiceUuid() , m_requestedCharacteristicUuid() { } void DeviceHandler::setAddressType( AddressType type ) { switch( type ) { case DeviceHandler::AddressType::PublicAddress: { m_addressType = QLowEnergyController::PublicAddress; break; } case DeviceHandler::AddressType::RandomAddress: { m_addressType = QLowEnergyController::RandomAddress; break; } } } DeviceHandler::AddressType DeviceHandler::addressType() const { if( m_addressType == QLowEnergyController::RandomAddress ) return DeviceHandler::AddressType::RandomAddress; return DeviceHandler::AddressType::PublicAddress; } void DeviceHandler::setDevice( DeviceInfo *device ) { clearMessages(); m_pCurrentDevice = device; // Disconnect and delete old connection if( m_pControl ) { m_pControl->disconnectFromDevice(); delete m_pControl; m_pControl = nullptr; } // Create new controller and connect it if device is available. if( m_pCurrentDevice ) { // Make connections //! [connect signals-1] m_pControl = QLowEnergyController::createCentral( m_pCurrentDevice->getDevice(), this ); m_pControl->setRemoteAddressType( m_addressType ); connect( m_pControl, &QLowEnergyController::serviceDiscovered, this, &DeviceHandler::slotServiceDiscovered ); connect( m_pControl, &QLowEnergyController::discoveryFinished, this, &DeviceHandler::slotServiceScanDone ); connect( m_pControl, static_cast(&QLowEnergyController::error), this, [this](QLowEnergyController::Error error) { Q_UNUSED( error ); emit signalConnectionStateChanged( false ); setError( "Cannot connect to remote device." ); } ); connect( m_pControl, &QLowEnergyController::disconnected, this, [this]() { emit signalConnectionStateChanged( false ); setError( "LowEnergy controller disconnected" ); }); connect( m_pControl, &QLowEnergyController::connected, this, [this]() { emit signalConnectionStateChanged( true ); setInfo( "LowEnergy controller connected... Search Services" ); m_pControl->discoverServices(); }); // Connect m_pControl->connectToDevice(); } } void DeviceHandler::setRequestedServiceUuid( const QBluetoothUuid &service_uuid ) { m_requestedServiceUuid = service_uuid; } QBluetoothUuid DeviceHandler::requestedServiceUuid() const { return m_requestedServiceUuid; } void DeviceHandler::setRequestedCharacteristicUuid( const QBluetoothUuid &char_uuid ) { m_requestedCharacteristicUuid = char_uuid; } QBluetoothUuid DeviceHandler::requestedCharacteristicUuid() const { return m_requestedCharacteristicUuid; } void DeviceHandler::slotStartMeasurement() { } void DeviceHandler::slotStopMeasurement() { } void DeviceHandler::slotServiceDiscovered( const QBluetoothUuid &gatt ) { if( gatt == m_requestedServiceUuid ) { setInfo( "Requested service discovered. Waiting for service scan to be done..." ); m_foundRequestedService = true; } } void DeviceHandler::slotServiceScanDone() { setInfo( "Service scan done." ); // Delete old service if available. if( m_pService ) { delete m_pService; m_pService = nullptr; } // If Requested service is found, create a new service. if( m_foundRequestedService ) m_pService = m_pControl->createServiceObject( m_requestedServiceUuid, this ); if( m_pService ) { qInfo() << "Connected to service : " << m_requestedServiceUuid; connect( m_pService, &QLowEnergyService::stateChanged, this, &DeviceHandler::slotServiceStateChanged ); connect( m_pService, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::slotUpdateMeasuredValue ); connect( m_pService, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::slotConfirmedDescriptorWrite ); m_pService->discoverDetails(); } else { setError( "Requested Service was not found." ); } } void DeviceHandler::slotServiceStateChanged( QLowEnergyService::ServiceState state ) { switch( state ) { case QLowEnergyService::DiscoveringServices: setInfo( tr("Discovering Services..." ) ); break; case QLowEnergyService::ServiceDiscovered: { setInfo( tr( "Service discovered." ) ); const QLowEnergyCharacteristic fChar = m_pService->characteristic( m_requestedCharacteristicUuid ); if( !fChar.isValid() ) { setError( "Requasted Data not Found." ); break; } m_notificationDescriptor = fChar.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration ); if( m_notificationDescriptor.isValid() ) { m_pService->writeDescriptor( m_notificationDescriptor, QByteArray::fromHex( "0100" ) ); } break; } default: // nothing for now break; } emit signalAliveChanged(); } void DeviceHandler::slotUpdateMeasuredValue( const QLowEnergyCharacteristic &c, const QByteArray &value ) { // Ignore any other characteristic change -> shouldn't really happen, though if( c.uuid() != m_requestedCharacteristicUuid ) return; qInfo() << "QByteArray() : " << value << " => Size : " << value.size(); QString read_value = QString( value ); emit signalReceivedValue( read_value ); } void DeviceHandler::slotConfirmedDescriptorWrite( const QLowEnergyDescriptor &d, const QByteArray &value ) { if( d.isValid() && d == m_notificationDescriptor && value == QByteArray::fromHex( "0000" ) ) { // disabled notifications -> assume disconnect intent m_pControl->disconnectFromDevice(); delete m_pService; m_pService = nullptr; } } void DeviceHandler::slotDisconnectService() { m_foundRequestedService = false; // disable notifications if( m_notificationDescriptor.isValid() && m_pService && m_notificationDescriptor.value() == QByteArray::fromHex( "0100" ) ) { m_pService->writeDescriptor( m_notificationDescriptor, QByteArray::fromHex( "0000" ) ); } else { if( m_pControl ) m_pControl->disconnectFromDevice(); delete m_pService; m_pService = nullptr; } } bool DeviceHandler::measuring() const { return m_measuring; } bool DeviceHandler::alive() const { if( m_pService ) return m_pService->state() == QLowEnergyService::ServiceDiscovered; return false; }