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