diff options
author | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-11-23 18:02:09 +0100 |
---|---|---|
committer | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-11-26 13:00:34 +0100 |
commit | be750788c7f5276b0a8686e1f11bf4ca12ee9231 (patch) | |
tree | 17d95e86ba063fad3193f5291fe0fd9db0bfb9f3 /src/bluetooth | |
parent | 9966c69e301a041ad224ef08bc3da45ca249041d (diff) |
QLowEnergyService::writeCharacteristic - implementation for OS X and iOS
writeCharacteristic - implemented with Core Bluetooth for OS X and iOS.
Change-Id: Ia228ff451e1e6d7b6fb7de6cad29198aa9257602
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/bluetooth')
-rw-r--r-- | src/bluetooth/osx/osxbtcentralmanager.mm | 114 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtcentralmanager_p.h | 15 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtutility.mm | 9 | ||||
-rw-r--r-- | src/bluetooth/osx/osxbtutility_p.h | 1 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycharacteristic.h | 1 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_osx.mm | 186 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_osx_p.h | 24 | ||||
-rw-r--r-- | src/bluetooth/qlowenergyservice_osx.mm | 133 |
8 files changed, 468 insertions, 15 deletions
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm index 71622b1c..025313d0 100644 --- a/src/bluetooth/osx/osxbtcentralmanager.mm +++ b/src/bluetooth/osx/osxbtcentralmanager.mm @@ -78,6 +78,8 @@ using namespace QT_NAMESPACE; - (CBCharacteristic *)nextCharacteristicForService:(CBService*)service startingFrom:(CBCharacteristic *)from withProperties:(CBCharacteristicProperties)properties; +- (CBCharacteristic *)characteristicForService:(CBService *)service + withIndex:(NSUInteger)index; - (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic startingFrom:(CBDescriptor *)descriptor; @@ -437,6 +439,60 @@ using namespace QT_NAMESPACE; delegate->serviceDetailsDiscoveryFinished(ObjCStrongReference<CBService>(service, true)); } +- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value + characteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle + serviceUuid:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid + serviceHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))serviceHandle + withResponse:(bool)withResponse +{ + using namespace OSXBluetooth; + + Q_ASSERT_X(!serviceUuid.isNull(), "-write:characteristic:serviceUuid:serviceHandle:withResponse:", + "invalid service uuid"); + Q_ASSERT_X(serviceHandle, "-write:characteristic:serviceUuid:serviceHandle:withResponse:", + "invalid service handle (0)"); + Q_ASSERT_X(serviceHandle < charHandle, "-write:characteristic:serviceUuid:serviceHandle:withResponse:", + "invalid characteristic handle (<= serviceHandle)"); + + + QT_BT_MAC_AUTORELEASEPOOL; + + CBService *const service = [self serviceForUUID:serviceUuid]; + if (!service) { + qCWarning(QT_BT_OSX) << "-write:characteristic:serviceUuid:serviceHandle:withResponse:, " + "service with uuid: " << serviceUuid << " not found"; + return false; + } + + // 'Convert' charHandle into the 'index' and find a characteristic. + CBCharacteristic *const ch = [self characteristicForService:service + withIndex:charHandle - serviceHandle - 1]; + + if (!ch) { + qCWarning(QT_BT_OSX) << "-write:characteristic:serviceUuid:serviceHandle:withResponse:, " + "characteristic with handle: " << charHandle << " not " + "found on service: " << serviceUuid; + return false; + } + + Q_ASSERT_X(peripheral, "-write:characteristic:serviceUuid:serviceHandle:withResponse:", + "invalid peripheral (nil)"); + + ObjCStrongReference<NSData> data(data_from_bytearray(value)); + if (!data) { + // Even if qtData.size() == 0, we still need NSData object. + qCWarning(QT_BT_OSX) << "-write:characteristic:serviceUuid:serviceHandle:withResponse:, " + "failed to allocate NSData object"; + return false; + } + + // TODO: check what happens if I'm using NSData with length 0. + [peripheral writeValue:data.data() forCharacteristic:ch + type: withResponse? CBCharacteristicWriteWithResponse : CBCharacteristicWriteWithoutResponse]; + + return true; +} + // Aux. methods: - (CBService *)serviceForUUID:(const QBluetoothUuid &)qtUuid @@ -560,6 +616,30 @@ using namespace QT_NAMESPACE; return nil; } +- (CBCharacteristic *)characteristicForService:(CBService *)service + withIndex:(NSUInteger)index +{ + Q_ASSERT_X(service, "-characteristicForService:withIndex:", + "invalid service (nil)"); + + QT_BT_MAC_AUTORELEASEPOOL; + + NSArray *const chars = service.characteristics; + + if (!chars || !chars.count) + return nil; + + for (NSUInteger i = 0, j = 0, e = chars.count; i < e; ++i) { + CBCharacteristic *const ch = [chars objectAtIndex:i]; + if (j == index) + return ch; + if (ch.descriptors) + j += ch.descriptors.count + 1; // + 1 for characteristic itself. + } + + return nil; +} + - (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic startingFrom:(CBDescriptor *)descriptor { @@ -1003,4 +1083,38 @@ using namespace QT_NAMESPACE; } } +- (void)peripheral:(CBPeripheral *)aPeripheral + didWriteValueForCharacteristic:(CBCharacteristic *)characteristic + error:(NSError *)error +{ + // From docs: + // + // "This method is invoked only when your app calls the writeValue:forCharacteristic:type: + // method with the CBCharacteristicWriteWithResponse constant specified as the write type. + // If successful, the error parameter is nil. If unsuccessful, + // the error parameter returns the cause of the failure." + + using namespace OSXBluetooth; + + Q_UNUSED(aPeripheral) + Q_UNUSED(characteristic) + Q_UNUSED(error) + + Q_ASSERT_X(delegate, "-peripheral:didWriteValueForCharacteristic:error", + "invalid delegate (null)"); + + if (error) { + // Use NSLog to log the actual error: + NSLog(@"-peripheral:didWriteValueForCharacteristic:error:, failed with error: %@", + error); + // TODO: no char handle at the moment, have to change to char index instead + // and calculate the right handle in the LE controller. + delegate->error(qt_uuid(characteristic.service.UUID), 0, + QLowEnergyService::CharacteristicWriteError); + } else { + ObjCStrongReference<CBCharacteristic> ch(characteristic, true); + delegate->characteristicWriteNotification(ch); + } +} + @end diff --git a/src/bluetooth/osx/osxbtcentralmanager_p.h b/src/bluetooth/osx/osxbtcentralmanager_p.h index 25faab78..1e3028d6 100644 --- a/src/bluetooth/osx/osxbtcentralmanager_p.h +++ b/src/bluetooth/osx/osxbtcentralmanager_p.h @@ -43,6 +43,7 @@ #define OSXBTCENTRALMANAGER_P_H #include "qlowenergycontroller.h" +#include "qlowenergyservice.h" #include "qbluetoothuuid.h" #include "osxbtutility_p.h" @@ -66,6 +67,7 @@ public: typedef QT_MANGLE_NAMESPACE(OSXBTCentralManager) ObjCCentralManager; typedef ObjCStrongReference<NSArray> LEServices; typedef ObjCStrongReference<CBService> LEService; + typedef ObjCStrongReference<CBCharacteristic> LECharacteristic; virtual ~CentralManagerDelegate(); @@ -73,6 +75,7 @@ public: virtual void connectSuccess() = 0; virtual void serviceDiscoveryFinished(LEServices services) = 0; virtual void serviceDetailsDiscoveryFinished(LEService service) = 0; + virtual void characteristicWriteNotification(LECharacteristic ch) = 0; virtual void disconnected() = 0; // General errors. @@ -80,6 +83,10 @@ public: // Service related errors. virtual void error(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error error) = 0; + // Characteristics related errors. + virtual void error(const QBluetoothUuid &serviceUuid, + QLowEnergyHandle charHandle, + QLowEnergyService::ServiceError error) = 0; }; enum CentralManagerState @@ -133,6 +140,14 @@ QT_END_NAMESPACE - (void)discoverServices; - (bool)discoverServiceDetails:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid; +// Characteristic's handle here is a 'relative' == valueHandle - service->startHandle +// to simplify mapping between Qt's handles and Core Bluetooth's data structures. +- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value + characteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle + serviceUuid:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid + serviceHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))serviceHandle + withResponse:(bool)writeWithResponse; + @end #endif diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm index 1b07e2cb..26f33e6e 100644 --- a/src/bluetooth/osx/osxbtutility.mm +++ b/src/bluetooth/osx/osxbtutility.mm @@ -246,6 +246,15 @@ QByteArray qt_bytearray(NSData *data) return value; } +ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData) +{ + if (!qtData.size()) + return ObjCStrongReference<NSData>([[NSData alloc] init], false); + + ObjCStrongReference<NSData> result([NSData dataWithBytes:qtData.constData() length:qtData.size()], true); + return result; +} + } QT_END_NAMESPACE diff --git a/src/bluetooth/osx/osxbtutility_p.h b/src/bluetooth/osx/osxbtutility_p.h index 6e226a25..d30ee93c 100644 --- a/src/bluetooth/osx/osxbtutility_p.h +++ b/src/bluetooth/osx/osxbtutility_p.h @@ -288,6 +288,7 @@ ObjCStrongReference<CBUUID> cb_uuid(const QBluetoothUuid &qtUuid); bool equal_uuids(const QBluetoothUuid &qtUuid, CBUUID *cbUuid); bool equal_uuids(CBUUID *cbUuid, const QBluetoothUuid &qtUuid); QByteArray qt_bytearray(NSData *data); +ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData); } // namespace OSXBluetooth diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h index e0ed2976..256aea68 100644 --- a/src/bluetooth/qlowenergycharacteristic.h +++ b/src/bluetooth/qlowenergycharacteristic.h @@ -91,6 +91,7 @@ protected: friend class QLowEnergyService; friend class QLowEnergyControllerPrivate; + friend class QLowEnergyControllerPrivateOSX; QLowEnergyCharacteristicPrivate *data; QLowEnergyCharacteristic(QSharedPointer<QLowEnergyServicePrivate> p, QLowEnergyHandle handle); diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm index 9a86c392..f41423d8 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_osx.mm @@ -122,6 +122,36 @@ UUIDList qt_servicesUuids(NSArray *services) return uuids; } +QLowEnergyHandle qt_findCharacteristicHandle(QLowEnergyHandle serviceHandle, + CBService *service, CBCharacteristic *ch) +{ + // This mapping from CB -> Qt Qt -> CB is quite verbose and annoying, + // but duplicating data structures (CB char-tree, Qt char-tree, etc.) + // is even more annoying. + + Q_ASSERT_X(serviceHandle, "qt_findCharacteristicHandle", + "invalid service handle (0)"); + Q_ASSERT_X(service, "qt_findCharacteristicHandle", + "invalid service (nil)"); + Q_ASSERT_X(ch, "qt_findCharacteristicHandle", + "invalid characteristic (nil)"); + + NSArray *const chars = service.characteristics; + if (!chars || !chars.count) + return 0; // Invalid handle, to be .. handled by the caller. + + QLowEnergyHandle handle = serviceHandle + 1; + for (CBCharacteristic *candidate in chars) { + if (candidate == ch) + return handle; + NSArray *const ds = candidate.descriptors; + if (ds && ds.count) + handle += ds.count + 1; // + 1 is for char itself. + } + + return 0; +} + } QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController *q) @@ -345,6 +375,46 @@ void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService s qtService->stateChanged(QLowEnergyService::ServiceDiscovered); } +void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(LECharacteristic ch) +{ + Q_ASSERT_X(ch, "characteristicWriteNotification", "invalid characteristic (nil)"); + + QT_BT_MAC_AUTORELEASEPOOL; + + CBService *const cbService = [ch service]; + const QBluetoothUuid serviceUuid(OSXBluetooth::qt_uuid(cbService.UUID)); + if (!discoveredServices.contains(serviceUuid)) { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::characteristicWriteNotification(), " + "unknown service uuid: " << serviceUuid; + return; + } + + ServicePrivate service(discoveredServices.value(serviceUuid)); + Q_ASSERT_X(service->startHandle, "characteristicWriteNotification", + "invalid service handle (0)"); + + const QLowEnergyHandle charHandle = + qt_findCharacteristicHandle(service->startHandle, cbService, ch); + + if (!charHandle) { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::characteristicWriteNotification(), " + "unknown characteristic"; + return; + } + + QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle)); + if (!characteristic.isValid()) { + qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::characteristicWriteNotification(), " + "unknown characteristic"; + return; + } + + // TODO: check that this 'value' is what we need! + const QByteArray data(OSXBluetooth::qt_bytearray([ch value])); + updateValueOfCharacteristic(charHandle, data, false); + emit service->characteristicWritten(characteristic, data); +} + void QLowEnergyControllerPrivateOSX::disconnected() { controllerState = QLowEnergyController::UnconnectedState; @@ -397,6 +467,22 @@ void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid, } } +void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid, + QLowEnergyHandle charHandle, + QLowEnergyService::ServiceError errorCode) +{ + Q_UNUSED(charHandle) + + if (!discoveredServices.contains(serviceUuid)) { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::error(), " + "unknown service uuid: " << serviceUuid; + return; + } + + ServicePrivate service(discoveredServices.value(serviceUuid)); + service->setError(errorCode); +} + void QLowEnergyControllerPrivateOSX::connectToDevice() { Q_ASSERT_X(isValid(), "connectToDevice", "invalid private controller"); @@ -471,6 +557,106 @@ void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid } } +void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, + QLowEnergyHandle charHandle, const QByteArray &newValue, + bool writeWithResponse) +{ + Q_ASSERT_X(!service.isNull(), "writeCharacteristic", "invalid service (null)"); + + if (!isValid()) { + qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), " + "invalid controller"; + return; + } + + // We can work only with services, found on a given peripheral + // (== created by the given LE controller), + // otherwise we can not write anything at all. + if (!discoveredServices.contains(service->uuid)) { + qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), " + "no service with uuid: " << service << "found"; + return; + } + + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), " + "no characteristic with handle: " << charHandle << "found"; + return; + } + + const bool result = [centralManager write:newValue + characteristic:charHandle + serviceUuid:service->uuid + serviceHandle:service->startHandle + withResponse:writeWithResponse]; + if (!result) + service->setError(QLowEnergyService::CharacteristicWriteError); +} + +quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle, + const QByteArray &value, + bool appendValue) +{ + QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); + if (!service.isNull() && service->characteristicList.contains(charHandle)) { + if (appendValue) + service->characteristicList[charHandle].value += value; + else + service->characteristicList[charHandle].value = value; + + return service->characteristicList[charHandle].value.size(); + } + + return 0; +} + +void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, + QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, + const QByteArray &newValue) +{ + Q_UNUSED(service) + Q_UNUSED(charHandle) + Q_UNUSED(descriptorHandle) + Q_UNUSED(newValue) +} + +QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivateOSX::serviceForHandle(QLowEnergyHandle handle) +{ + foreach (QSharedPointer<QLowEnergyServicePrivate> service, discoveredServices.values()) { + if (service->startHandle <= handle && handle <= service->endHandle) + return service; + } + + return QSharedPointer<QLowEnergyServicePrivate>(); +} + +QLowEnergyCharacteristic QLowEnergyControllerPrivateOSX::characteristicForHandle(QLowEnergyHandle charHandle) +{ + QSharedPointer<QLowEnergyServicePrivate> service(serviceForHandle(charHandle)); + if (service.isNull()) + return QLowEnergyCharacteristic(); + + if (service->characteristicList.isEmpty()) + return QLowEnergyCharacteristic(); + + // Check whether it is the handle of a characteristic header + if (service->characteristicList.contains(charHandle)) + return QLowEnergyCharacteristic(service, charHandle); + + // Check whether it is the handle of the characteristic value or its descriptors + QList<QLowEnergyHandle> charHandles(service->characteristicList.keys()); + std::sort(charHandles.begin(), charHandles.end()); + + for (int i = charHandles.size() - 1; i >= 0; --i) { + if (charHandles.at(i) > charHandle) + continue; + + return QLowEnergyCharacteristic(service, charHandles.at(i)); + } + + return QLowEnergyCharacteristic(); +} + void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode) { // This function does not emit! diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h index f9764c03..a8f8aaa0 100644 --- a/src/bluetooth/qlowenergycontroller_osx_p.h +++ b/src/bluetooth/qlowenergycontroller_osx_p.h @@ -34,6 +34,7 @@ #ifndef QLOWENERGYCONTROLLER_OSX_P_H #define QLOWENERGYCONTROLLER_OSX_P_H +#include "qlowenergyserviceprivate_p.h" #include "osx/osxbtcentralmanager_p.h" #include "qlowenergycontroller_p.h" #include "qlowenergycontroller.h" @@ -41,6 +42,7 @@ #include "qbluetoothaddress.h" #include "qbluetoothuuid.h" +#include <QtCore/qsharedpointer.h> #include <QtCore/qglobal.h> #include <QtCore/qstring.h> #include <QtCore/qmap.h> @@ -52,6 +54,7 @@ class QLowEnergyControllerPrivateOSX : public QLowEnergyControllerPrivate, public OSXBluetooth::CentralManagerDelegate { friend class QLowEnergyController; + friend class QLowEnergyService; public: QLowEnergyControllerPrivateOSX(QLowEnergyController *q); QLowEnergyControllerPrivateOSX(QLowEnergyController *q, @@ -67,15 +70,36 @@ private: void serviceDiscoveryFinished(LEServices services) Q_DECL_OVERRIDE; void serviceDetailsDiscoveryFinished(LEService service) Q_DECL_OVERRIDE; + void characteristicWriteNotification(LECharacteristic ch) Q_DECL_OVERRIDE; void disconnected() Q_DECL_OVERRIDE; void error(QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE; void error(const QBluetoothUuid &serviceUuid, QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE; + void error(const QBluetoothUuid &serviceUuid, + QLowEnergyHandle charHandle, + QLowEnergyService::ServiceError error) Q_DECL_OVERRIDE; void connectToDevice(); void discoverServices(); void discoverServiceDetails(const QBluetoothUuid &serviceUuid); + void writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service, + QLowEnergyHandle charHandle, const QByteArray &newValue, + bool writeWithResponse); + + quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle, + const QByteArray &value, + bool appendValue); + + void writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service, + QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, + const QByteArray &newValue); + + + // 'Lookup' functions: + QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(QLowEnergyHandle serviceHandle); + QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle charHandle); + void setErrorDescription(QLowEnergyController::Error errorCode); void invalidateServices(); diff --git a/src/bluetooth/qlowenergyservice_osx.mm b/src/bluetooth/qlowenergyservice_osx.mm index e2f5d35f..f9c124e4 100644 --- a/src/bluetooth/qlowenergyservice_osx.mm +++ b/src/bluetooth/qlowenergyservice_osx.mm @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Javier S. Pedro <maemo@javispedro.com> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -31,18 +32,50 @@ ** ****************************************************************************/ +#include "qlowenergycontroller_osx_p.h" +#include "qlowenergyserviceprivate_p.h" #include "qlowenergycharacteristic.h" #include "qlowenergydescriptor.h" #include "qlowenergyservice.h" #include "qbluetoothuuid.h" +#include <QtCore/qcoreapplication.h> #include <QtCore/qstring.h> #include <QtCore/qlist.h> +#include <algorithm> + +QT_BEGIN_NAMESPACE + +namespace { + +QLowEnergyControllerPrivateOSX *qt_mac_le_controller(QSharedPointer<QLowEnergyServicePrivate> d_ptr) +{ + if (d_ptr.isNull()) + return Q_NULLPTR; + + return static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr->controller.data()); +} + +} + QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> d, QObject *parent) : QObject(parent), d_ptr(d) { + qRegisterMetaType<QLowEnergyService::ServiceState>("QLowEnergyService::ServiceState"); + qRegisterMetaType<QLowEnergyService::ServiceError>("QLowEnergyService::ServiceError"); + + connect(d.data(), SIGNAL(error(QLowEnergyService::ServiceError)), + this, SIGNAL(error(QLowEnergyService::ServiceError))); + connect(d.data(), SIGNAL(stateChanged(QLowEnergyService::ServiceState)), + this, SIGNAL(stateChanged(QLowEnergyService::ServiceState))); + connect(d.data(), SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)), + this, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray))); + connect(d.data(), SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)), + this, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray))); + connect(d.data(), SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)), + this, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray))); } QLowEnergyService::~QLowEnergyService() @@ -51,69 +84,137 @@ QLowEnergyService::~QLowEnergyService() QList<QBluetoothUuid> QLowEnergyService::includedServices() const { - return QList<QBluetoothUuid>(); + return d_ptr->includedServices; } QLowEnergyService::ServiceTypes QLowEnergyService::type() const { - return PrimaryService; + return d_ptr->type; } QLowEnergyService::ServiceState QLowEnergyService::state() const { - return InvalidService; + return d_ptr->state; } QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const { - Q_UNUSED(uuid) + foreach (const QLowEnergyHandle handle, d_ptr->characteristicList.keys()) { + if (d_ptr->characteristicList[handle].uuid == uuid) + return QLowEnergyCharacteristic(d_ptr, handle); + } return QLowEnergyCharacteristic(); } QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const { - return QList<QLowEnergyCharacteristic>(); + QList<QLowEnergyCharacteristic> result; + QList<QLowEnergyHandle> handles(d_ptr->characteristicList.keys()); + + std::sort(handles.begin(), handles.end()); + + foreach (const QLowEnergyHandle &handle, handles) { + QLowEnergyCharacteristic characteristic(d_ptr, handle); + result.append(characteristic); + } + + return result; } QBluetoothUuid QLowEnergyService::serviceUuid() const { - return QBluetoothUuid(); + return d_ptr->uuid; } QString QLowEnergyService::serviceName() const { - return QString(); + bool ok = false; + const quint16 clsId = d_ptr->uuid.toUInt16(&ok); + if (ok) { + QBluetoothUuid::ServiceClassUuid uuid + = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId); + const QString name = QBluetoothUuid::serviceClassToString(uuid); + if (!name.isEmpty()) + return name; + } + + return qApp ? qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") : + QStringLiteral("Unknown Service"); } void QLowEnergyService::discoverDetails() { + QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); + + if (!controller || d_ptr->state == InvalidService) { + d_ptr->setError(OperationError); + return; + } + + if (d_ptr->state != DiscoveryRequired) + return; + + d_ptr->setState(QLowEnergyService::DiscoveringServices); + controller->discoverServiceDetails(d_ptr->uuid); } QLowEnergyService::ServiceError QLowEnergyService::error() const { - return NoError; + return d_ptr->lastError; } bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const { - Q_UNUSED(characteristic) + if (characteristic.d_ptr.isNull() || !characteristic.data) + return false; + + if (d_ptr == characteristic.d_ptr + && d_ptr->characteristicList.contains(characteristic.attributeHandle())) { + return true; + } return false; } -void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &characteristic, - const QByteArray &newValue, +void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &ch, const QByteArray &newValue, WriteMode mode) { - Q_UNUSED(characteristic) - Q_UNUSED(newValue) - Q_UNUSED(mode) + // Not a characteristic of this service + if (!contains(ch)) + return; + + if (state() != ServiceDiscovered) + d_ptr->setError(QLowEnergyService::OperationError); + + QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr); + if (!controller) + return; + + // Don't write if properties don't permit it + if (mode == WriteWithResponse && (ch.properties() & QLowEnergyCharacteristic::Write)) + controller->writeCharacteristic(ch.d_ptr, ch.attributeHandle(), newValue, true); + else if (mode == WriteWithoutResponse && (ch.properties() & QLowEnergyCharacteristic::WriteNoResponse)) + controller->writeCharacteristic(ch.d_ptr, ch.attributeHandle(), newValue, false); + else + d_ptr->setError(QLowEnergyService::OperationError); } bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const { - Q_UNUSED(descriptor) + if (descriptor.d_ptr.isNull() || !descriptor.data) + return false; + + const QLowEnergyHandle charHandle = descriptor.characteristicHandle(); + if (!charHandle) + return false; + + if (d_ptr == descriptor.d_ptr && d_ptr->characteristicList.contains(charHandle) + && d_ptr->characteristicList[charHandle].descriptorList.contains(descriptor.handle())) + { + return true; + } + return false; } @@ -123,3 +224,5 @@ void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor, Q_UNUSED(descriptor) Q_UNUSED(newValue) } + +QT_END_NAMESPACE |