Index: sources/bluetooth/BLEScanner.cpp =================================================================== diff -u -r9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/bluetooth/BLEScanner.cpp (.../BLEScanner.cpp) (revision 9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e) +++ sources/bluetooth/BLEScanner.cpp (.../BLEScanner.cpp) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -87,9 +87,15 @@ */ void BLEScanner::doSelectDevice(const QString &addr) { - onSetMacAddress(addr); + if (addr != macAddress) + onSetMacAddress(addr); + + discoveryAgent->stop(); + emit didFinishScan(); + for (const QBluetoothDeviceInfo &deviceInfo : devices) { if (deviceInfo.address().toString() == addr) { + selectedDeviceInfo = deviceInfo; onConnectToDevice(deviceInfo); return; } @@ -103,9 +109,13 @@ */ void BLEScanner::onDeviceDiscovered(const QBluetoothDeviceInfo& deviceInfo) { - qDebug() << __FUNCTION__ << deviceInfo.address(); - devices.append(deviceInfo); - emit didDiscoverDevice(deviceInfo); + for (const QString &prefix : omronDeviceNamesPrefixes) { + if (deviceInfo.name().toLower().contains(prefix.toLower())) + { + devices.append(deviceInfo); + emit didDiscoverDevice(deviceInfo); + } + } } /*! @@ -253,10 +263,7 @@ qDebug() << "Invalid service state"; break; } - } - - } /*! @@ -268,17 +275,14 @@ */ void BLEScanner::onCharacteristicChanged(const QLowEnergyCharacteristic &c, const QByteArray &byteArray) { - qDebug() << "@@@@@@@@ Data Read @@@@@@@@@@: " << c.name() << byteArray; if (c.uuid() != QBluetoothUuid(QBluetoothUuid::BloodPressureMeasurement)) { qDebug() << "Ignoring data read for " << c.uuid(); return; } - doParseMeasurement(byteArray); - } /*! @@ -328,7 +332,6 @@ qDebug() << "measurement_status: " << measurement.measurement_status; emit didReceiveBPMeasurement(measurement); - } /*! @@ -347,7 +350,7 @@ delete omronBloodPressureService; omronBloodPressureService = nullptr; } - + emit didConnectToDevice(selectedDeviceInfo); } /*! @@ -359,7 +362,6 @@ void BLEScanner::onServiceCharacteristicsRead(const QLowEnergyCharacteristic &c,const QByteArray &byteArray) { qDebug() << __FUNCTION__ << c.name() << " data: " << byteArray; - } /*! @@ -389,7 +391,6 @@ connect(service, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), this, SLOT(onConfirmedDescriptorWrite(QLowEnergyDescriptor, QByteArray))); } - } /*! @@ -400,6 +401,7 @@ void BLEScanner::onControllerError(const QLowEnergyController::Error &error) { qDebug() << __FUNCTION__ << "LE controller error: " << error; + emit didReceiveControllerError(error); } /*! @@ -451,5 +453,6 @@ connect(lowEnergyController, SIGNAL(disconnected()), this, SLOT(onDeviceDisconnected())); + emit didStartConnectingToDevice(); lowEnergyController->connectToDevice(); } Index: sources/bluetooth/BLEScanner.h =================================================================== diff -u -r9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/bluetooth/BLEScanner.h (.../BLEScanner.h) (revision 9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e) +++ sources/bluetooth/BLEScanner.h (.../BLEScanner.h) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -50,12 +50,10 @@ }; typedef bp_measurement bp_measurement_t; - void doParseMeasurement(const QByteArray &byteArray); - void doRequestBPMeasurement(); - private: QList devices; + QBluetoothDeviceInfo selectedDeviceInfo; QString macAddress = ""; QBluetoothDeviceDiscoveryAgent *discoveryAgent; QLowEnergyController *lowEnergyController; @@ -72,6 +70,7 @@ const QString omronBatteryLevelServiceName = QString("{0000180f-0000-1000-8000-00805f9b34fb}"); const QString omronUnknownServiceName = QString("{ecbe3980-c9a2-11e1-b1bd-0002a5d5c51b}"); const QString omronBloodPressureServiceName = QString("{00001810-0000-1000-8000-00805f9b34fb}"); + const QStringList omronDeviceNamesPrefixes = {"BP7000", "BLEsmart"}; QStringList characteristicsToAcquireNotify; @@ -85,11 +84,16 @@ void didReceiveScanForDevicesError(QBluetoothDeviceDiscoveryAgent::Error error); void didFinishScan(); void didDiscoverDevice(const QBluetoothDeviceInfo &device); + void didStartConnectingToDevice(); + void didReceiveControllerError(const QLowEnergyController::Error &error); + void didConnectToDevice(const QBluetoothDeviceInfo &deviceInfo); public slots: bool doInit(); void doSelectDevice(const QString &addr); void doScanForDevices(); + void doParseMeasurement(const QByteArray &byteArray); + void doRequestBPMeasurement(); private slots: Index: sources/gui/qml/globals/Fonts.qml =================================================================== diff -u -rb61d8a3e01fef66eee8095c9cddf835d9bb32b66 -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/gui/qml/globals/Fonts.qml (.../Fonts.qml) (revision b61d8a3e01fef66eee8095c9cddf835d9bb32b66) +++ sources/gui/qml/globals/Fonts.qml (.../Fonts.qml) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -67,4 +67,5 @@ readonly property int primingFontSize : 20 readonly property int crTreatmentFontSize : 30 readonly property int crTreatmentTableFontSize : 24 + readonly property int bleTitleFontSize : 30 } Index: sources/gui/qml/globals/Variables.qml =================================================================== diff -u -rbe1b2d8f110b741f3d630df438da07d411110543 -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/gui/qml/globals/Variables.qml (.../Variables.qml) (revision be1b2d8f110b741f3d630df438da07d411110543) +++ sources/gui/qml/globals/Variables.qml (.../Variables.qml) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -113,6 +113,8 @@ readonly property int settingsBLEButtonWidth : 300 readonly property int settingsBLEButtonHeight : 75 + readonly property int settingsBLEMargin : 75 + readonly property int settingsBLEListViewMargin : 25 readonly property int settingsOptionWidth : 550 readonly property int settingsOptionHeight : 50 Index: sources/gui/qml/pages/SettingsBluetooth.qml =================================================================== diff -u -r9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/gui/qml/pages/SettingsBluetooth.qml (.../SettingsBluetooth.qml) (revision 9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e) +++ sources/gui/qml/pages/SettingsBluetooth.qml (.../SettingsBluetooth.qml) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -35,43 +35,114 @@ onClicked: _root.clickedBack() } - TouchRect { - id: _scan_for_devices + TitleText { id: _title anchors.horizontalCenter: _root.horizontalCenter anchors.top: _root.top - anchors.topMargin: 75 + anchors.left: _root.left + anchors.topMargin: Variables.settingsBLEMargin + text: qsTr("Bluetooth") + } + + TouchRect { id: _scanForDevices + anchors.left: _root.left + anchors.top: _root.top + anchors.topMargin: 6*Variables.settingsBLEMargin + anchors.leftMargin: 2*Variables.settingsBLEMargin height: Variables.settingsBLEButtonHeight width: Variables.settingsBLEButtonWidth - text.text: "Scan for devices" + text.text: qsTr("Scan for devices") animated: true onClicked: vBluetooth.doScanForDevices() } - Rectangle { - id: _status - anchors.left: _scan_for_devices.right - anchors.top: _root.top - anchors.topMargin: 75 + Rectangle { id: _status + anchors.bottom: _scanForDevices.bottom + anchors.left: _scanForDevices.left + anchors.bottomMargin: Variables.settingsBLEMargin color: "transparent" height: Variables.settingsBLEButtonHeight width: Variables.settingsBLEButtonWidth Text { id: _status_text - anchors.centerIn: parent - text: vBluetooth.status + text: qsTr("Status: " + vBluetooth.status) color: Colors.textMain font.pixelSize: Fonts.fontPixelDialogText } } - ListView { - id: _devices + Text { id: _pairedDevicesLabel + anchors.top: _root.top + anchors.right: _devices.left + anchors.rightMargin: Variables.settingsBLEMargin + anchors.topMargin: 2*Variables.settingsBLEMargin + + font.pixelSize: Fonts.bleTitleFontSize + color: Colors.textMain + text: qsTr("My Devices") + } + + ListView { id: _paired_devices + model: vBluetooth.pairedDevices + anchors.top: _pairedDevicesLabel.bottom + anchors.topMargin: Variables.settingsBLEListViewMargin + anchors.horizontalCenter: _pairedDevicesLabel.horizontalCenter + anchors.bottom: _root.bottom + anchors.bottomMargin: Variables.mainMenuHeight + Variables.notificationHeight + width: Variables.settingsBLEButtonWidth + clip: true + spacing: 10 + + delegate: TouchRect { + id: _pairedDevicesRect + anchors.horizontalCenter: parent.horizontalCenter + height: 85 + width: parent.width + color: Colors.backgroundMain + border.color: Colors.borderButton + radius: 5 + animated: true + + Text { + id: _pairedDevice + anchors.bottom: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + text: modelData.name + color: Colors.textMain + } + + Text { + id: _pairedDevicesAddress + anchors.top: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + text: modelData.address + color: Colors.textMain + } + + onClicked: { + vBluetooth.doSelectDevice(modelData.address) + } + } + } + + Text { id: _devicesLabel + anchors.top: _root.top + anchors.right: _root.right + anchors.rightMargin: 3*Variables.settingsBLEMargin + anchors.topMargin: 2*Variables.settingsBLEMargin + + font.pixelSize: Fonts.bleTitleFontSize + color: Colors.textMain + text: qsTr("Devices") + } + + ListView { id: _devices model: vBluetooth.devices - anchors.top: _scan_for_devices.bottom - anchors.topMargin: 10 + anchors.top: _devicesLabel.bottom + anchors.topMargin: Variables.settingsBLEListViewMargin + anchors.horizontalCenter: _devicesLabel.horizontalCenter anchors.bottom: _root.bottom + anchors.bottomMargin: Variables.mainMenuHeight + Variables.notificationHeight width: Variables.settingsBLEButtonWidth - anchors.horizontalCenter: _root.horizontalCenter clip: true spacing: 10 @@ -106,10 +177,4 @@ } } } - - Connections { target: vBluetooth - onDidScanFinished: { - console.debug("Scan Finished."); - } - } } Index: sources/view/VBluetooth.cpp =================================================================== diff -u -r9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/view/VBluetooth.cpp (.../VBluetooth.cpp) (revision 9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e) +++ sources/view/VBluetooth.cpp (.../VBluetooth.cpp) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -9,6 +9,7 @@ VBluetooth::VBluetooth(QObject *parent) : QObject(parent) { + // incoming connect(&_BLEScanner, SIGNAL(didReceiveScanForDevicesError(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onScanForDevicesError(QBluetoothDeviceDiscoveryAgent::Error))); @@ -18,11 +19,23 @@ connect(&_BLEScanner, SIGNAL(didDiscoverDevice(QBluetoothDeviceInfo)), this, SLOT(onDeviceDiscovered(const QBluetoothDeviceInfo))); + connect(&_BLEScanner, SIGNAL(didStartConnectingToDevice()), + this, SLOT(onConnectingToDevice())); + + connect(&_BLEScanner, SIGNAL(didReceiveControllerError(QLowEnergyController::Error)), + this, SLOT(onControllerError(QLowEnergyController::Error))); + + connect(&_BLEScanner, SIGNAL(didConnectToDevice(QBluetoothDeviceInfo)), + this, SLOT(onConnectedToDevice(QBluetoothDeviceInfo))); + + // outgoing connect(this, SIGNAL(didSelectDevice(const QString)), &_BLEScanner, SLOT(doSelectDevice(const QString))); connect(this, SIGNAL(didRequestScanForDevices()), &_BLEScanner, SLOT(doScanForDevices())); + + } /*! @@ -36,8 +49,7 @@ bleDevices.clear(); emit didDevicesChanged(); emit didRequestScanForDevices(); - status = "Scanning..."; - emit didStatusChanged(); + onUpdateStatus(tr("Scanning...")); } /*! @@ -50,13 +62,12 @@ { switch (error) { case QBluetoothDeviceDiscoveryAgent::PoweredOffError: - status = tr("Error: The bluetooth module is powered off."); + onUpdateStatus(tr("Error: The bluetooth module is powered off.")); break; default: - status = tr("Error: Scan for devices error."); + onUpdateStatus(tr("Error: Scan for devices error.")); break; } - emit didStatusChanged(); } /*! @@ -67,7 +78,8 @@ void VBluetooth::onDeviceDiscovered(const QBluetoothDeviceInfo &device) { VBluetoothDeviceInfo *info = new VBluetoothDeviceInfo(device); - bleDevices.append(info); + if (!isDeviceAlreadyPaired(device)) + bleDevices.append(info); qDebug() << "VBluetooth: Discovered " << device.address(); emit didDevicesChanged(); } @@ -79,8 +91,7 @@ void VBluetooth::onScanForDevicesFinished() { emit didScanFinished(); - status = "Scan Finished."; - emit didStatusChanged(); + onUpdateStatus(tr("Scan Finished.")); } /*! @@ -94,6 +105,16 @@ } /*! + * \brief VBluetooth::getDevices + * Gets the paired devices BLE modelData for QML + * \return QVariant - the modelData + */ +QVariant VBluetooth::doGetPairedDevices() +{ + return QVariant::fromValue(pairedDevices); +} + +/*! * \brief VBluetooth::onSelectedDevice * Emits a signal that the device was selected * \param addr - the selected BLE mac address @@ -103,3 +124,135 @@ emit didSelectDevice(addr); } +/*! + * \brief onUpdateStatus + * Updates the status and emits a signal to notify the QML + * \param message + */ +void VBluetooth::onUpdateStatus(const QString &message) +{ + status = message; + emit didStatusChanged(); +} + +/*! + * \brief VBluetooth::onConnectingToDevice + * Called when attempting to connect to a device + */ +void VBluetooth::onConnectingToDevice() +{ + onUpdateStatus(tr("Connecting...")); +} + +/*! + * \brief VBluetooth::controllerErrorToString + * Converts a QLowEnergyController::Error enum to QString + * \param error - the QLowEnergyController::Error enum + * \return QString - the enum converted to a QString + */ +QString VBluetooth::controllerErrorToString(QLowEnergyController::Error error) +{ + switch (error) + { + case QLowEnergyController::NoError: + return tr("No Error."); + break; + case QLowEnergyController::UnknownError: + return tr("Unknown Error."); + break; + case QLowEnergyController::UnknownRemoteDeviceError: + return tr("Unknown Remote Device Error."); + break; + case QLowEnergyController::NetworkError: + return tr("Bluetooth Network Error."); + break; + case QLowEnergyController::InvalidBluetoothAdapterError: + return tr("Bluetooth Adapter Error."); + break; + case QLowEnergyController::ConnectionError: + case QLowEnergyController::AdvertisingError: + case QLowEnergyController::RemoteHostClosedError: + return tr("Connection Error."); + break; + default: + const QMetaObject *mo = qt_getEnumMetaObject(error); + int idx = mo->indexOfEnumerator(qt_getEnumName(error)); + const char *text = mo->enumerator(idx).valueToKey(error); + if (!QString(text).isEmpty()) { + return tr(text); + } + } + + return tr("Unexpected Error."); +} + +/*! + * \brief VBluetooth::onControllerError + * Called when BLE interface reports a controller error. Updates + * the bluetooth status message according to the error reported. + * \param error - the low energy controller error reported + */ +void VBluetooth::onControllerError(QLowEnergyController::Error error) +{ + onUpdateStatus(controllerErrorToString(error)); +} + +/*! + * \brief VBluetooth::isDeviceAlreadyPaired + * Determines if a device has already been paired + * \param deviceInfo - the device info to check if paired + * \return bool - true if already paired, false otherwise + */ +bool VBluetooth::isDeviceAlreadyPaired(const QBluetoothDeviceInfo &deviceInfo) +{ + bool alreadyPaired = false; + for (QObject *device : pairedDevices) { + VBluetoothDeviceInfo *pairedDevice = static_cast(device); + if (pairedDevice->getAddress() == deviceInfo.address().toString()) + alreadyPaired = true; + } + return alreadyPaired; +} + +/*! + * \brief VBluetooth::removeFromDevices + * Removes a device from the non-paired bluetooth devices list + * Adds that devices to the non-paired bluetooth devices blacklist + * \param info - the bluetooth device info object to remove + */ +void VBluetooth::removeFromDevices(const VBluetoothDeviceInfo *info) +{ + QList idxs; + int i = 0; + for (QObject *bleDevice : bleDevices) { + VBluetoothDeviceInfo *device = static_cast(bleDevice); + if (device->getAddress() == info->getAddress()) + idxs.append(i); + ++i; + } + for (int i : idxs) + bleDevices.removeAt(i); + + // update the QML + emit didDevicesChanged(); +} + +/*! + * \brief VBluetooth::onConnectedToDevice + * Slot called when BLEScanner connects to a device. Moves a device + * from the devices list to the my devices list + * \param deviceInfo - the device connected to + */ +void VBluetooth::onConnectedToDevice(const QBluetoothDeviceInfo &deviceInfo) +{ + VBluetoothDeviceInfo *info = new VBluetoothDeviceInfo(deviceInfo); + bool alreadyPaired = isDeviceAlreadyPaired(deviceInfo); + if (!alreadyPaired) + { + pairedDevices.append(info); + emit didPairedDevicesChanged(); + + removeFromDevices(info); + } + onUpdateStatus(tr("Connected.")); +} Index: sources/view/VBluetooth.h =================================================================== diff -u -r9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e -r7249125bb71e6fab4139590ee777c64ece9cf3be --- sources/view/VBluetooth.h (.../VBluetooth.h) (revision 9efb7cf51c882dc1f374df0b2a8b8c20efafaa4e) +++ sources/view/VBluetooth.h (.../VBluetooth.h) (revision 7249125bb71e6fab4139590ee777c64ece9cf3be) @@ -4,6 +4,7 @@ // Qt #include #include +#include // Project #include "VBluetoothDeviceInfo.h" @@ -23,11 +24,13 @@ protected: Q_PROPERTY(QVariant devices READ doGetDevices NOTIFY didDevicesChanged) + Q_PROPERTY(QVariant pairedDevices READ doGetPairedDevices NOTIFY didPairedDevicesChanged) Q_PROPERTY(QVariant status READ getStatus NOTIFY didStatusChanged) public slots: void doScanForDevices(); QVariant doGetDevices(); + QVariant doGetPairedDevices(); void doSelectDevice(const QString &addr); signals: @@ -36,16 +39,26 @@ void didScanFinished(); void didStatusChanged(); void didRequestScanForDevices(); + void didPairedDevicesChanged(); private: QList bleDevices; + QList pairedDevices; QString status; QString getStatus() { return status; } + QString controllerErrorToString(QLowEnergyController::Error error); + bool isDeviceAlreadyPaired(const QBluetoothDeviceInfo &deviceInfo); + void removeFromDevices(const VBluetoothDeviceInfo *info); + private slots: void onScanForDevicesError(QBluetoothDeviceDiscoveryAgent::Error error); void onScanForDevicesFinished(); void onDeviceDiscovered(const QBluetoothDeviceInfo &device); + void onConnectingToDevice(); + void onUpdateStatus(const QString &message); + void onControllerError(QLowEnergyController::Error error); + void onConnectedToDevice(const QBluetoothDeviceInfo &deviceInfo); }; } #endif // VBLUETOOTH_H