/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of Qt for Device Creation. ** ** $QT_BEGIN_LICENSE:BSD$ ** 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. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "bluetoothdevice.h" #include #include #include #include #include #include #include #include #include "bluetoothapiconstants.h" Q_DECLARE_LOGGING_CATEGORY(boot2QtDemos) typedef struct { qint16 gyrox; qint16 gyroy; qint16 gyroz; qint16 accelx; qint16 accely; qint16 accelz; qint16 magnetomx; qint16 magnetomy; qint16 magnetomz; } movement_data_t; BluetoothDevice::BluetoothDevice() : m_controller(0) , m_irTemperatureService(0) , m_baroService(0) , m_humidityService(0) , m_lightService(0) , m_motionService(0) , m_deviceState(DeviceState::Disconnected) , m_temperatureMeasurementStarted(false) , m_barometerMeasurementStarted(false) , m_humidityMeasurementStarted(false) , m_lightIntensityMeasurementStarted(false) , m_motionMeasurementStarted(false) { m_lastMilliseconds = QDateTime::currentMSecsSinceEpoch(); statusUpdated("Device created"); m_serviceDetailsTimer = new QTimer(this); m_serviceDetailsTimer->setInterval(5000); m_serviceDetailsTimer->setSingleShot(true); connect(m_serviceDetailsTimer, &QTimer::timeout, [=]() { qCDebug(boot2QtDemos) << "Service details timer timeout. No more services details found"; if (isDeviceReady()) setState(Connected); else setState(Disconnected); }); } BluetoothDevice::BluetoothDevice(const QBluetoothDeviceInfo &d) : BluetoothDevice() { m_deviceInfo = d; } BluetoothDevice::~BluetoothDevice() { if (m_controller) { m_controller->disconnect(); delete m_controller; } } QString BluetoothDevice::getAddress() const { #if defined(Q_OS_DARWIN) // On Apple platforms we do not have addresses, // only unique UUIDs generated by Core Bluetooth. return m_deviceInfo.deviceUuid().toString(); #else return m_deviceInfo.address().toString(); #endif } QString BluetoothDevice::getName() const { return m_deviceInfo.name(); } void BluetoothDevice::scanServices() { setState(Scanning); if (!m_controller) { statusUpdated("(Connecting to device...)"); // Connecting signals and slots for connecting to LE services. m_controller = new QLowEnergyController(m_deviceInfo); connect(m_controller, &QLowEnergyController::connected, this, &BluetoothDevice::deviceConnected); connect(m_controller, QOverload::of(&QLowEnergyController::error), this, &BluetoothDevice::errorReceived); connect(m_controller, &QLowEnergyController::disconnected, this, &BluetoothDevice::deviceDisconnected); connect(m_controller, &QLowEnergyController::serviceDiscovered, this, &BluetoothDevice::addLowEnergyService); connect(m_controller, &QLowEnergyController::discoveryFinished, this, &BluetoothDevice::serviceScanDone); m_controller->setRemoteAddressType(QLowEnergyController::PublicAddress); } m_controller->connectToDevice(); } void BluetoothDevice::addLowEnergyService(const QBluetoothUuid &serviceUuid) { if (serviceUuid == QBluetoothUuid(QLatin1String(IRTEMPERATURESENSOR_SERVICE_UUID))) { qCDebug(boot2QtDemos) << "Found infrared temperature service."; m_irTemperatureService = m_controller->createServiceObject(serviceUuid); if (!m_irTemperatureService) { qWarning() << "Could not create infrared temperature service object."; return; } connect(m_irTemperatureService, &QLowEnergyService::stateChanged, this, &BluetoothDevice::temperatureDetailsDiscovered); connect(m_irTemperatureService, &QLowEnergyService::characteristicChanged, this, &BluetoothDevice::updateTemperature); m_irTemperatureService->discoverDetails(); } else if (serviceUuid == QBluetoothUuid(QLatin1String(BAROMETER_SERVICE_UUID))) { qCDebug(boot2QtDemos) << "Found barometer service."; m_baroService = m_controller->createServiceObject(serviceUuid); if (!m_baroService) { qWarning() << "Could not create barometer service object."; return; } connect(m_baroService, &QLowEnergyService::stateChanged, this, &BluetoothDevice::barometerDetailsDiscovered); connect(m_baroService, &QLowEnergyService::characteristicChanged, this, &BluetoothDevice::updatePressure); m_baroService->discoverDetails(); } else if (serviceUuid == QBluetoothUuid(QLatin1String(HUMIDITYSENSOR_SERVICE_UUID))) { qCDebug(boot2QtDemos) << "Found humidity service."; m_humidityService = m_controller->createServiceObject(serviceUuid); if (!m_humidityService) { qWarning() << "Could not create humidity service object."; return; } connect(m_humidityService, &QLowEnergyService::stateChanged, this, &BluetoothDevice::humidityDetailsDiscovered); connect(m_humidityService, &QLowEnergyService::characteristicChanged, this, &BluetoothDevice::updateHumidity); m_humidityService->discoverDetails(); } else if (serviceUuid == QBluetoothUuid(QLatin1String(LIGHTSENSOR_SERVICE_UUID))) { qCDebug(boot2QtDemos) << "Found light service."; m_lightService = m_controller->createServiceObject(serviceUuid); if (!m_lightService) { qWarning() << "Could not create light service object."; return; } connect(m_lightService, &QLowEnergyService::stateChanged, this, &BluetoothDevice::lightIntensityDetailsDiscovered); connect(m_lightService, &QLowEnergyService::characteristicChanged, this, &BluetoothDevice::updateLight); m_lightService->discoverDetails(); } else if (serviceUuid == QBluetoothUuid(QLatin1String(MOTIONSENSOR_SERVICE_UUID))) { qCDebug(boot2QtDemos) << "Found motion service."; m_motionService = m_controller->createServiceObject(serviceUuid); if (!m_motionService) { qWarning() << "Could not create motion service object."; return; } connect(m_motionService, &QLowEnergyService::stateChanged, this, &BluetoothDevice::motionDetailsDiscovered); connect(m_motionService, &QLowEnergyService::characteristicChanged, this, &BluetoothDevice::updateMotionValue); m_motionService->discoverDetails(); } else { qCDebug(boot2QtDemos) << "Found unhandled service with id" << serviceUuid << "."; } } void BluetoothDevice::serviceScanDone() { statusUpdated("(Service scan done!)"); qCDebug(boot2QtDemos) << "ServiceScan done."; if (!m_irTemperatureService) qCDebug(boot2QtDemos) << "Infrared temperature service not found."; if (!m_baroService) qCDebug(boot2QtDemos) << "Barometer service not found."; if (!m_humidityService) qCDebug(boot2QtDemos) << "Humidity service not found."; if (!m_lightService) qCDebug(boot2QtDemos) << "Light service not found."; if (!m_motionService) qCDebug(boot2QtDemos) << "Motion service not found."; m_serviceDetailsTimer->start(); } void BluetoothDevice::temperatureDetailsDiscovered(QLowEnergyService::ServiceState newstate) { if (newstate == QLowEnergyService::ServiceDiscovered) { connect(m_irTemperatureService, &QLowEnergyService::characteristicWritten, [=]() { qCDebug(boot2QtDemos) << "Wrote Characteristic - temperature"; }); connect(m_irTemperatureService, QOverload::of(&QLowEnergyService::error), [=](QLowEnergyService::ServiceError newError) { qCDebug(boot2QtDemos) << "error while writing - temperature:" << newError; }); // data characteristic QLowEnergyCharacteristic characteristic = m_irTemperatureService->characteristic( QBluetoothUuid(QLatin1String("f000aa01-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { const QLowEnergyDescriptor notificationDescriptor = characteristic.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration); if (notificationDescriptor.isValid()) m_irTemperatureService->writeDescriptor(notificationDescriptor, QByteArray::fromHex(ENABLE_NOTIF_STR)); } // configuration characteristic characteristic = m_irTemperatureService->characteristic( QBluetoothUuid(QLatin1String("f000aa02-0451-4000-b000-000000000000"))); if (characteristic.isValid()) m_irTemperatureService->writeCharacteristic(characteristic, QByteArray::fromHex(START_MEASUREMENT_STR), QLowEnergyService::WriteWithResponse); // timeout characteristic characteristic = m_irTemperatureService->characteristic( QBluetoothUuid(QLatin1String("f000aa03-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { m_irTemperatureService->writeCharacteristic(characteristic, QByteArray::fromHex(SENSORTAG_SLOW_TIMER_TIMEOUT_STR), QLowEnergyService::WriteWithResponse); // Period 1 second } m_temperatureMeasurementStarted = true; updateServiceDetails(); } } void BluetoothDevice::barometerDetailsDiscovered(QLowEnergyService::ServiceState newstate) { if (newstate == QLowEnergyService::ServiceDiscovered) { connect(m_baroService, &QLowEnergyService::characteristicWritten, [=]() { qCDebug(boot2QtDemos) << "Wrote Characteristic - barometer"; }); connect(m_baroService, QOverload::of(&QLowEnergyService::error), [=](QLowEnergyService::ServiceError newError) { qCDebug(boot2QtDemos) << "error while writing - barometer:" << newError; }); // data characteristic QLowEnergyCharacteristic characteristic = m_baroService->characteristic( QBluetoothUuid(QLatin1String("f000aa41-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { const QLowEnergyDescriptor notificationDescriptor = characteristic.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration); if (notificationDescriptor.isValid()) m_baroService->writeDescriptor(notificationDescriptor, QByteArray::fromHex(ENABLE_NOTIF_STR)); } // configuration characteristic characteristic = m_baroService->characteristic( QBluetoothUuid(QLatin1String("f000aa42-0451-4000-b000-000000000000"))); if (characteristic.isValid()) m_baroService->writeCharacteristic(characteristic, QByteArray::fromHex(START_MEASUREMENT_STR), QLowEnergyService::WriteWithResponse); // timeout characteristic characteristic = m_baroService->characteristic( QBluetoothUuid(QLatin1String("f000aa44-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { m_baroService->writeCharacteristic(characteristic, QByteArray::fromHex(SENSORTAG_MEDIUM_TIMER_TIMEOUT_STR), QLowEnergyService::WriteWithResponse); // Period 500 ms } m_barometerMeasurementStarted = true; updateServiceDetails(); } } void BluetoothDevice::humidityDetailsDiscovered(QLowEnergyService::ServiceState newstate) { if (newstate == QLowEnergyService::ServiceDiscovered) { connect(m_humidityService, &QLowEnergyService::characteristicWritten, [=]() { qCDebug(boot2QtDemos) << "Wrote Characteristic - humidity"; }); connect(m_humidityService, QOverload::of(&QLowEnergyService::error), [=](QLowEnergyService::ServiceError newError) { qCDebug(boot2QtDemos) << "error while writing - humidity:" << newError; }); // data characteristic QLowEnergyCharacteristic characteristic = m_humidityService->characteristic( QBluetoothUuid(QLatin1String("f000aa21-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { const QLowEnergyDescriptor notificationDescriptor = characteristic.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration); if (notificationDescriptor.isValid()) m_humidityService->writeDescriptor(notificationDescriptor, QByteArray::fromHex(ENABLE_NOTIF_STR)); } // configuration characteristic characteristic = m_humidityService->characteristic( QBluetoothUuid(QLatin1String("f000aa22-0451-4000-b000-000000000000"))); if (characteristic.isValid()) m_humidityService->writeCharacteristic(characteristic, QByteArray::fromHex(START_MEASUREMENT_STR), QLowEnergyService::WriteWithResponse); // timeout characteristic characteristic = m_humidityService->characteristic( QBluetoothUuid(QLatin1String("f000aa23-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { m_humidityService->writeCharacteristic(characteristic, QByteArray::fromHex(SENSORTAG_SLOW_TIMER_TIMEOUT_STR), QLowEnergyService::WriteWithResponse); // Period 500 ms } m_humidityMeasurementStarted = true; updateServiceDetails(); } } void BluetoothDevice::lightIntensityDetailsDiscovered(QLowEnergyService::ServiceState newstate) { if (newstate == QLowEnergyService::ServiceDiscovered) { connect(m_lightService, &QLowEnergyService::characteristicWritten, [=]() { qCDebug(boot2QtDemos) << "Wrote Characteristic - light intensity"; }); connect(m_lightService, QOverload::of(&QLowEnergyService::error), [=](QLowEnergyService::ServiceError newError) { qCDebug(boot2QtDemos) << "error while writing - light intensity:" << newError; }); // data characteristic QLowEnergyCharacteristic characteristic = m_lightService->characteristic( QBluetoothUuid(QLatin1String("f000aa71-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { const QLowEnergyDescriptor notificationDescriptor = characteristic.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration); if (notificationDescriptor.isValid()) m_lightService->writeDescriptor(notificationDescriptor, QByteArray::fromHex(ENABLE_NOTIF_STR)); } // configuration characteristic characteristic = m_lightService->characteristic( QBluetoothUuid(QLatin1String("f000aa72-0451-4000-b000-000000000000"))); if (characteristic.isValid()) m_lightService->writeCharacteristic(characteristic, QByteArray::fromHex(START_MEASUREMENT_STR), QLowEnergyService::WriteWithResponse); // timeout characteristic characteristic = m_lightService->characteristic( QBluetoothUuid(QLatin1String("f000aa73-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { m_lightService->writeCharacteristic(characteristic, QByteArray::fromHex(SENSORTAG_MEDIUM_TIMER_TIMEOUT_STR), QLowEnergyService::WriteWithResponse); // Period 500 ms } m_lightIntensityMeasurementStarted = true; updateServiceDetails(); } } void BluetoothDevice::motionDetailsDiscovered(QLowEnergyService::ServiceState newstate) { // reset the time once more before we start to receive measurements. m_lastMilliseconds = QDateTime::currentMSecsSinceEpoch(); if (newstate == QLowEnergyService::ServiceDiscovered) { connect(m_motionService, &QLowEnergyService::characteristicWritten, [=]() { qCDebug(boot2QtDemos) << "Wrote Characteristic - gyro"; }); connect(m_motionService, QOverload::of(&QLowEnergyService::error), [=](QLowEnergyService::ServiceError newError) { qCDebug(boot2QtDemos) << "error while writing - gyro:" << newError; }); // data characteristic QLowEnergyCharacteristic characteristic = m_motionService->characteristic( QBluetoothUuid(QLatin1String("f000aa81-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { const QLowEnergyDescriptor notificationDescriptor = characteristic.descriptor( QBluetoothUuid::ClientCharacteristicConfiguration); if (notificationDescriptor.isValid()) m_motionService->writeDescriptor(notificationDescriptor, QByteArray::fromHex(ENABLE_NOTIF_STR)); } // configuration characteristic characteristic = m_motionService->characteristic( QBluetoothUuid(QLatin1String("f000aa82-0451-4000-b000-000000000000"))); if (characteristic.isValid()) m_motionService->writeCharacteristic(characteristic, QByteArray::fromHex(MOVEMENT_ENABLE_SENSORS_BITMASK_VALUE), QLowEnergyService::WriteWithResponse); // timeout characteristic characteristic = m_motionService->characteristic( QBluetoothUuid(QLatin1String("f000aa83-0451-4000-b000-000000000000"))); if (characteristic.isValid()) { m_motionService->writeCharacteristic(characteristic, QByteArray::fromHex(SENSORTAG_RAPID_TIMER_TIMEOUT_STR), QLowEnergyService::WriteWithResponse); } m_motionMeasurementStarted = true; updateServiceDetails(); } } void BluetoothDevice::updateTemperature(const QLowEnergyCharacteristic &, const QByteArray &value) { irTemperatureReceived(value); } void BluetoothDevice::updatePressure(const QLowEnergyCharacteristic &, const QByteArray &value) { barometerReceived(value); } void BluetoothDevice::updateHumidity(const QLowEnergyCharacteristic &, const QByteArray &value) { humidityReceived(value); } void BluetoothDevice::updateLight(const QLowEnergyCharacteristic &, const QByteArray &value) { lightIntensityReceived(value); } void BluetoothDevice::updateMotionValue(const QLowEnergyCharacteristic &, const QByteArray &value) { motionReceived(value); } void BluetoothDevice::setState(BluetoothDevice::DeviceState state) { if (m_deviceState != state) { m_deviceState = state; emit stateChanged(); } } double BluetoothDevice::convertIrTemperatureAPIReadingToCelsius(quint16 rawReading) { // Compute and filter final value according to TI Bluetooth LE API const float SCALE_LSB = 0.03125; int it = (int)((rawReading) >> 2); float t = (float)it; return t * SCALE_LSB; } bool BluetoothDevice::isDeviceReady() const { if (m_temperatureMeasurementStarted || m_barometerMeasurementStarted || m_humidityMeasurementStarted || m_lightIntensityMeasurementStarted || m_motionMeasurementStarted) { return true; } return false; } void BluetoothDevice::updateServiceDetails() { if (m_temperatureMeasurementStarted && m_barometerMeasurementStarted && m_humidityMeasurementStarted && m_lightIntensityMeasurementStarted && m_motionMeasurementStarted) { qCDebug(boot2QtDemos) << "Service details timer stop. All service details found"; m_serviceDetailsTimer->stop(); setState(Connected); } else { m_serviceDetailsTimer->start(); } } void BluetoothDevice::irTemperatureReceived(const QByteArray &value) { const unsigned int rawObjectTemperature = (((quint8)value.at(3)) << 8) + ((quint8)value.at(2)); const double objectTemperature = convertIrTemperatureAPIReadingToCelsius(rawObjectTemperature); const unsigned int rawAmbientTemperature = (((quint8)value.at(1)) << 8) + ((quint8)value.at(0)); const double ambientTemperature = convertIrTemperatureAPIReadingToCelsius(rawAmbientTemperature); emit temperatureChanged(ambientTemperature, objectTemperature); } void BluetoothDevice::barometerReceived(const QByteArray &value) { //Merge bytes unsigned int temperature_raw; unsigned int barometer_raw; if (value.length() == 6) { temperature_raw = (((quint8)value.at(2)) << 16) + (((quint8)value.at(1)) << 8) + ((quint8)value.at(0)); barometer_raw = (((quint8)value.at(5)) << 16) + (((quint8)value.at(4)) << 8) + ((quint8)value.at(3)); } else { temperature_raw = (((quint8)value.at(1)) << 8) + ((quint8)value.at(0)); barometer_raw = (((quint8)value.at(3)) << 8) + ((quint8)value.at(2)); } double temperature = static_cast(temperature_raw); temperature /= BAROMETER_API_READING_DIVIDER; double barometer = static_cast(barometer_raw); barometer /= BAROMETER_API_READING_DIVIDER; emit barometerChanged(temperature, barometer); } void BluetoothDevice::humidityReceived(const QByteArray &value) { //Merge bytes unsigned int humidity_raw = (((quint8)value.at(3)) << 8) + ((quint8)value.at(2)); double humidity = static_cast(humidity_raw); humidity = humidity * HUMIDITY_API_READING_MULTIPLIER; emit humidityChanged(humidity); } void BluetoothDevice::lightIntensityReceived(const QByteArray &value) { //Merge bytes uint16_t lightIntensity_raw; lightIntensity_raw = (((quint8)value.at(1)) << 8) + ((quint8)value.at(0)); uint16_t e, m; m = lightIntensity_raw & 0x0FFF; e = (lightIntensity_raw & 0xF000) >> 12; // Compute and final value according to TI Bluetooth LE API double lightIntensity = ((double)m) * (0.01 * (double)qPow(2.0,(qreal)e)); emit lightIntensityChanged(lightIntensity); } void BluetoothDevice::motionReceived(const QByteArray &value) { static MotionSensorData data; data.msSincePreviousData = m_lastMilliseconds; m_lastMilliseconds = QDateTime::currentMSecsSinceEpoch(); data.msSincePreviousData = m_lastMilliseconds - data.msSincePreviousData; movement_data_t values; quint8* writePtr = (quint8*)(&values); for (int i = 0; i < 18; ++i) { *writePtr = (quint8)value.at(i); writePtr++; } // Data is in little endian. Fix here if needed. //Convert gyroscope and accelometer readings to proper units data.gyroScope_x = double(values.gyrox) * GYROSCOPE_API_READING_MULTIPLIER; data.gyroScope_y = double(values.gyroy) * GYROSCOPE_API_READING_MULTIPLIER; data.gyroScope_z = double(values.gyroz) * GYROSCOPE_API_READING_MULTIPLIER; // Accelometer at 8G data.accelometer_x = double(values.accelx) * ACCELOMETER_API_READING_MULTIPLIER; data.accelometer_y = double(values.accely) * ACCELOMETER_API_READING_MULTIPLIER; data.accelometer_z = double(values.accelz) * ACCELOMETER_API_READING_MULTIPLIER; data.magnetometer_x = double(values.magnetomx); data.magnetometer_y = double(values.magnetomy); data.magnetometer_z = double(values.magnetomz); emit motionChanged(data); } void BluetoothDevice::deviceConnected() { if (isDeviceReady()) { setState(Connected); } else { setState(DeviceState::Scanning); statusUpdated("(Discovering services...)"); m_controller->discoverServices(); } } void BluetoothDevice::errorReceived(QLowEnergyController::Error /*error*/) { setState(DeviceState::Error); statusUpdated(QString("Error: %1)").arg(m_controller->errorString())); } void BluetoothDevice::disconnectFromDevice() { // UI always expects disconnect() signal when calling this signal // TODO what is really needed is to extend state() to a multi value // and thus allowing UI to keep track of controller progress in addition to // device scan progress if (m_controller->state() != QLowEnergyController::UnconnectedState) m_controller->disconnectFromDevice(); else deviceDisconnected(); } void BluetoothDevice::deviceDisconnected() { statusUpdated("Disconnect from device"); setState(BluetoothDevice::Disconnected); } BluetoothDevice::DeviceState BluetoothDevice::state() const { return m_deviceState; }