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