Line data Source code
1 : // SPDX-FileCopyrightText: 2022-2023 Paul Colby <git@colby.id.au> 2 : // SPDX-License-Identifier: LGPL-3.0-or-later 3 : 4 : /*! 5 : * \file 6 : * Defines the DeviceInfoService and DeviceInfoServicePrivate classes. 7 : */ 8 : 9 : #include <qtpokit/deviceinfoservice.h> 10 : #include "deviceinfoservice_p.h" 11 : 12 : #include <QtEndian> 13 : 14 : /*! 15 : * \class DeviceInfoService 16 : * 17 : * The DeviceInfoService class accesses the `Device Info` service of Pokit devices. 18 : */ 19 : 20 : /*! 21 : * Constructs a new Pokit service with \a parent. 22 : */ 23 640 : DeviceInfoService::DeviceInfoService(QLowEnergyController * const controller, QObject * parent) 24 640 : : AbstractPokitService(new DeviceInfoServicePrivate(controller, this), parent) 25 : { 26 : 27 640 : } 28 : 29 : /*! 30 : * \cond internal 31 : * Constructs a new Pokit service with \a parent, and private implementation \a d. 32 : */ 33 0 : DeviceInfoService::DeviceInfoService( 34 0 : DeviceInfoServicePrivate * const d, QObject * const parent) 35 0 : : AbstractPokitService(d, parent) 36 : { 37 : 38 0 : } 39 : /// \endcond 40 : 41 : /*! 42 : * Destroys this DeviceInfoService object. 43 : */ 44 285 : DeviceInfoService::~DeviceInfoService() 45 : { 46 : 47 285 : } 48 : 49 19 : bool DeviceInfoService::readCharacteristics() 50 : { 51 13 : const bool r1 = readFirmwareRevisionCharacteristic(); 52 13 : const bool r2 = readHardwareRevisionCharacteristic(); 53 13 : const bool r3 = readSoftwareRevisionCharacteristic(); 54 13 : const bool r4 = readManufacturerCharacteristics(); 55 13 : const bool r5 = readModelNumberCharacteristic(); 56 32 : const bool r6 = ((service() != nullptr) && (service()->characteristic(CharacteristicUuids::serialNumber).isValid())) 57 19 : ? readSerialNumberCharacteristic() : true; 58 32 : return (r1 && r2 && r3 && r4 && r5 && r6); 59 : } 60 : 61 : /*! 62 : * Read the `Device Info` service's `Firmware Revision` characteristic. 63 : * 64 : * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the 65 : * underlying controller it not yet connected to the Pokit device, or the device's services have 66 : * not yet been discovered). 67 : * 68 : * Emits firmwareRevisionRead() if/when the characteristic has been read successfully. 69 : */ 70 32 : bool DeviceInfoService::readFirmwareRevisionCharacteristic() 71 : { 72 : Q_D(DeviceInfoService); 73 38 : return d->readCharacteristic(CharacteristicUuids::firmwareRevision); 74 : } 75 : 76 : /*! 77 : * Read the `Device Info` service's `Hardware Revision` characteristic. 78 : * 79 : * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the 80 : * underlying controller it not yet connected to the Pokit device, or the device's services have 81 : * not yet been discovered). 82 : * 83 : * Emits hardwareRevisionRead() if/when the characteristic has been read successfully. 84 : */ 85 32 : bool DeviceInfoService::readHardwareRevisionCharacteristic() 86 : { 87 : Q_D(DeviceInfoService); 88 38 : return d->readCharacteristic(CharacteristicUuids::hardwareRevision); 89 : } 90 : 91 : /*! 92 : * Read the `Device Info` service's `Manufacturer Name` characteristic. 93 : * 94 : * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the 95 : * underlying controller it not yet connected to the Pokit device, or the device's services have 96 : * not yet been discovered). 97 : * 98 : * Emits manufacturerNameRead() if/when the characteristic has been read successfully. 99 : */ 100 32 : bool DeviceInfoService::readManufacturerCharacteristics() 101 : { 102 : Q_D(DeviceInfoService); 103 38 : return d->readCharacteristic(CharacteristicUuids::manufacturerName); 104 : } 105 : 106 : /*! 107 : * Read the `Device Info` service's `Model Number` characteristic. 108 : * 109 : * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the 110 : * underlying controller it not yet connected to the Pokit device, or the device's services have 111 : * not yet been discovered). 112 : * 113 : * Emits modelNumberRead() if/when the characteristic has been read successfully. 114 : */ 115 32 : bool DeviceInfoService::readModelNumberCharacteristic() 116 : { 117 : Q_D(DeviceInfoService); 118 38 : return d->readCharacteristic(CharacteristicUuids::modelNumber); 119 : } 120 : 121 : /*! 122 : * Read the `Device Info` service's `Software Revision` characteristic. 123 : * 124 : * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the 125 : * underlying controller it not yet connected to the Pokit device, or the device's services have 126 : * not yet been discovered). 127 : * 128 : * Emits softwareRevisionRead() if/when the characteristic has been read successfully. 129 : */ 130 32 : bool DeviceInfoService::readSoftwareRevisionCharacteristic() 131 : { 132 : Q_D(DeviceInfoService); 133 38 : return d->readCharacteristic(CharacteristicUuids::softwareRevision); 134 : } 135 : 136 : /*! 137 : * Read the `Device Info` service's (undocumented) `Serial Number` characteristic. 138 : * 139 : * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the 140 : * underlying controller it not yet connected to the Pokit device, or the device's services have 141 : * not yet been discovered). 142 : * 143 : * Emits serialNumberRead() if/when the characteristic has been read successfully. 144 : */ 145 19 : bool DeviceInfoService::readSerialNumberCharacteristic() 146 : { 147 : Q_D(DeviceInfoService); 148 19 : return d->readCharacteristic(CharacteristicUuids::serialNumber); 149 : } 150 : 151 : /*! 152 : * Returns the most recent value of the `Device Info` service's `Manufacturer Name` characteristic. 153 : * 154 : * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is 155 : * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a 156 : * null QString is returned. 157 : */ 158 355 : QString DeviceInfoService::manufacturer() const 159 : { 160 : Q_D(const DeviceInfoService); 161 : const QLowEnergyCharacteristic characteristic = 162 355 : d->getCharacteristic(CharacteristicUuids::manufacturerName); 163 710 : return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()) : QString(); 164 355 : } 165 : 166 : /*! 167 : * Returns the most recent value of the `Device Info` service's `Model Number` characteristic. 168 : * 169 : * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is 170 : * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a 171 : * null QString is returned. 172 : */ 173 355 : QString DeviceInfoService::modelNumber() const 174 : { 175 : Q_D(const DeviceInfoService); 176 : const QLowEnergyCharacteristic characteristic = 177 355 : d->getCharacteristic(CharacteristicUuids::modelNumber); 178 710 : return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()) : QString(); 179 355 : } 180 : 181 : /*! 182 : * Returns the most recent value of the `Device Info` service's `Hardware Revision` characteristic. 183 : * 184 : * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is 185 : * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a 186 : * null QString is returned. 187 : */ 188 355 : QString DeviceInfoService::hardwareRevision() const 189 : { 190 : Q_D(const DeviceInfoService); 191 : const QLowEnergyCharacteristic characteristic = 192 355 : d->getCharacteristic(CharacteristicUuids::hardwareRevision); 193 710 : return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()) : QString(); 194 355 : } 195 : 196 : /*! 197 : * Returns the most recent value of the `Device Info` service's `Firmware Revision` characteristic. 198 : * 199 : * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is 200 : * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a 201 : * null QString is returned. 202 : */ 203 355 : QString DeviceInfoService::firmwareRevision() const 204 : { 205 : Q_D(const DeviceInfoService); 206 : const QLowEnergyCharacteristic characteristic = 207 355 : d->getCharacteristic(CharacteristicUuids::firmwareRevision); 208 710 : return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()) : QString(); 209 355 : } 210 : 211 : /*! 212 : * Returns the most recent value of the `Device Info` service's `Software Revision` characteristic. 213 : * 214 : * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is 215 : * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a 216 : * null QString is returned. 217 : */ 218 355 : QString DeviceInfoService::softwareRevision() const 219 : { 220 : Q_D(const DeviceInfoService); 221 : const QLowEnergyCharacteristic characteristic = 222 355 : d->getCharacteristic(CharacteristicUuids::softwareRevision); 223 710 : return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()) : QString(); 224 355 : } 225 : 226 : /*! 227 : * Returns the most recent value of the `Device Info` service's (undocumented) `Serial Number` 228 : * characteristic. 229 : * 230 : * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is 231 : * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a 232 : * null QString is returned. 233 : */ 234 355 : QString DeviceInfoService::serialNumber() const 235 : { 236 : Q_D(const DeviceInfoService); 237 : const QLowEnergyCharacteristic characteristic = 238 355 : d->getCharacteristic(CharacteristicUuids::serialNumber); 239 : /*! 240 : * \cond internal 241 : * \pokitApi Unlike other string characteristics, Pokit (Pro) devices always appear to add a trailing 242 : * `null` byte to serial number strings. So here we strip any that are present. 243 : * \endcond 244 : */ 245 710 : return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()).remove(QLatin1Char('\0')) : QString(); 246 355 : } 247 : 248 : /*! 249 : * \fn DeviceInfoService::manufacturerRead 250 : * 251 : * This signal is emitted when the `Manufacturer Name` characteristic has been read successfully. 252 : * 253 : * \see readManufacturerCharacteristic 254 : * \see manufacturer 255 : */ 256 : 257 : /*! 258 : * \fn DeviceInfoService::modelNumberRead 259 : * 260 : * This signal is emitted when the `Model Number` characteristic has been read successfully. 261 : * 262 : * \see readModelNumberCharacteristic 263 : * \see modelNumber 264 : */ 265 : 266 : /*! 267 : * \fn DeviceInfoService::hardwareRevisionRead 268 : * 269 : * This signal is emitted when the `Hardware Revision` characteristic has been read successfully. 270 : * 271 : * \see readHardwareRevisionCharacteristic 272 : * \see hardwareRevision 273 : */ 274 : 275 : /*! 276 : * \fn DeviceInfoService::firmwareRevisionRead 277 : * 278 : * This signal is emitted when the `Firmware Revision` characteristic has been read successfully. 279 : * 280 : * \see readFirmwareRevisionCharacteristic 281 : * \see firmwareRevision 282 : */ 283 : 284 : /*! 285 : * \fn DeviceInfoService::softwareRevisionRead 286 : * 287 : * This signal is emitted when the `Software Revision` characteristic has been read successfully. 288 : * 289 : * \see readSoftwareRevisionCharacteristic 290 : * \see softwareRevision 291 : */ 292 : 293 : /*! 294 : * \fn DeviceInfoService::serialNumberRead 295 : * 296 : * This signal is emitted when the `Serial Number` characteristic has been read successfully. 297 : * 298 : * \see readSerialNumberCharacteristic 299 : * \see serialNumber 300 : */ 301 : 302 : /*! 303 : * \cond internal 304 : * \class DeviceInfoServicePrivate 305 : * 306 : * The DeviceInfoServicePrivate class provides private implementation for DeviceInfoService. 307 : */ 308 : 309 : /*! 310 : * \internal 311 : * Constructs a new DeviceInfoServicePrivate object with public implementation \a q. 312 : */ 313 460 : DeviceInfoServicePrivate::DeviceInfoServicePrivate( 314 640 : QLowEnergyController * controller, DeviceInfoService * const q) 315 640 : : AbstractPokitServicePrivate(DeviceInfoService::serviceUuid, controller, q) 316 : { 317 : 318 460 : } 319 : 320 : /*! 321 : * Implements AbstractPokitServicePrivate::characteristicRead to parse \a value, then emit a 322 : * specialised signal, for each supported \a characteristic. 323 : */ 324 19 : void DeviceInfoServicePrivate::characteristicRead(const QLowEnergyCharacteristic &characteristic, 325 : const QByteArray &value) 326 : { 327 19 : AbstractPokitServicePrivate::characteristicRead(characteristic, value); 328 : 329 : Q_Q(DeviceInfoService); 330 19 : if (characteristic.uuid() == DeviceInfoService::CharacteristicUuids::manufacturerName) { 331 0 : const QString name = QString::fromUtf8(value); 332 0 : qCDebug(lc).noquote() << tr(R"(Manufacturer name: "%1")").arg(name); 333 0 : emit q->manufacturerRead(name); 334 : return; 335 0 : } 336 : 337 19 : if (characteristic.uuid() == DeviceInfoService::CharacteristicUuids::modelNumber) { 338 0 : const QString model = QString::fromUtf8(value); 339 0 : qCDebug(lc).noquote() << tr(R"(Model number: "%1")").arg(model); 340 0 : emit q->modelNumberRead(model); 341 : return; 342 0 : } 343 : 344 19 : if (characteristic.uuid() == DeviceInfoService::CharacteristicUuids::hardwareRevision) { 345 0 : const QString revision = QString::fromUtf8(value); 346 0 : qCDebug(lc).noquote() << tr(R"(Hardware revision: "%1")").arg(revision); 347 0 : emit q->hardwareRevisionRead(revision); 348 : return; 349 0 : } 350 : 351 19 : if (characteristic.uuid() == DeviceInfoService::CharacteristicUuids::firmwareRevision) { 352 0 : const QString revision = QString::fromUtf8(value); 353 0 : qCDebug(lc).noquote() << tr(R"(Firmware revision: "%1")").arg(revision); 354 0 : emit q->firmwareRevisionRead(revision); 355 : return; 356 0 : } 357 : 358 19 : if (characteristic.uuid() == DeviceInfoService::CharacteristicUuids::softwareRevision) { 359 0 : const QString revision = QString::fromUtf8(value); 360 0 : qCDebug(lc).noquote() << tr(R"(Software revision: "%1")").arg(revision); 361 0 : emit q->softwareRevisionRead(revision); 362 : return; 363 0 : } 364 : 365 19 : if (characteristic.uuid() == DeviceInfoService::CharacteristicUuids::serialNumber) { 366 0 : const QString serialNumber = QString::fromUtf8(value); 367 0 : qCDebug(lc).noquote() << tr(R"(Serial number: "%1")").arg(serialNumber); 368 0 : emit q->serialNumberRead(serialNumber); 369 : return; 370 0 : } 371 : 372 59 : qCWarning(lc).noquote() << tr("Unknown characteristic read for Device Info service") 373 25 : << serviceUuid << characteristic.name() << characteristic.uuid(); 374 : } 375 : 376 : /// \endcond