Dokit
Internal development documentation
Loading...
Searching...
No Matches
genericaccessservice.cpp
Go to the documentation of this file.
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 GenericAccessService and GenericAccessServicePrivate classes.
7 */
8
11
12#include <QtEndian>
13
14/*!
15 * \class GenericAccessService
16 *
17 * The GenericAccessService class accesses the `Generic Access` service of Pokit devices.
18 *
19 * \cond internal
20 * \pokitApi Pokit API 1.00 (and 0.02) claims support for the `Generic Access` (`0x1800`) service,
21 * however the neither the Pokit Meter, nor the Pokit Pro report any support for this service, but
22 * both report support for an undocumented `Generic Attribute` (`0x1801`) service instead.
23 * \endcond
24 */
25
26/*!
27 * Constructs a new Pokit service with \a parent.
28 */
30 : AbstractPokitService(new GenericAccessServicePrivate(controller, this), parent)
31{
32
33}
34
35/*!
36 * \cond internal
37 * Constructs a new Pokit service with \a parent, and private implementation \a d.
38 */
45/// \endcond
46
47/*!
48 * Destroys this GenericAccessService object.
49 */
54
56{
57 const bool r1 = readDeviceNameCharacteristic();
58 const bool r2 = readAppearanceCharacteristic();
59 return (r1 && r2);
60}
61
62/*!
63 * Read the `Generic Access` service's `Appearance` characteristic.
64 *
65 * Returns `true` if the read request is succesfully queued, `false` otherwise (ie if the
66 * underlying controller it not yet connected to the Pokit device, or the device's services have
67 * not yet been discovered).
68 *
69 * Emits appearanceRead() if/when the characteristic has been read successfully.
70 */
76
77/*!
78 * Read the `Generic Access` service's `Device Name` characteristic.
79 *
80 * Returns `true` if the read request is succesfully queued, `false` otherwise (ie if the
81 * underlying controller it not yet connected to the Pokit device, or the device's services have
82 * not yet been discovered).
83 *
84 * Emits deviceNameRead() if/when the characteristic has been read successfully.
85 */
91
92/*!
93 * Returns the most recent value of the `Generic Access` services's `Appearance` characteristic.
94 *
95 * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is
96 * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), `0xFFFF`
97 * is returned.
98 *
99 * \note Pokit's Bluetooth API suggests the device's `Appearance` will always be 0 aka "Unknown
100 * Appearance", so this is probably not a very useful characteristic if you already know you are
101 * dealing with a Pokit device.
102 */
104{
105 Q_D(const GenericAccessService);
106 const QLowEnergyCharacteristic characteristic =
107 d->getCharacteristic(CharacteristicUuids::appearance);
108 return (characteristic.isValid()) ? GenericAccessServicePrivate::parseAppearance(characteristic.value())
109 : std::numeric_limits<quint16>::max();
110}
111
112/*!
113 * Returns the most recent value of the `Generic Access` services's `Device Name` characteristic.
114 *
115 * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is
116 * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then a
117 * null QString is returned.
118 */
120{
121 Q_D(const GenericAccessService);
122 const QLowEnergyCharacteristic characteristic =
123 d->getCharacteristic(CharacteristicUuids::deviceName);
124 return (characteristic.isValid()) ? QString::fromUtf8(characteristic.value()) : QString();
125}
126
127/*!
128 * Set's the Pokit device's name to \a name.
129 *
130 * Returns `true` if the write request was successfully queued, `false` otherwise.
131 *
132 * Emits deviceNameWritten() if/when the \a name has been set.
133 */
135{
136 Q_D(const GenericAccessService);
137 const QLowEnergyCharacteristic characteristic =
138 d->getCharacteristic(CharacteristicUuids::deviceName);
139 if (!characteristic.isValid()) {
140 return false;
141 }
142
143 const QByteArray value = name.toUtf8();
144 if (value.length() > 11) {
145 qCWarning(d->lc).noquote() << tr(R"(Device name "%1" is too long (%2 > 11 bytes): 0x%3)")
146 .arg(name).arg(value.length()).arg(QLatin1String(value.toHex()));
147 return false;
148 }
149
150 d->service->writeCharacteristic(characteristic, value);
151 return (d->service->error() != QLowEnergyService::ServiceError::CharacteristicWriteError);
152}
153
154
155/*!
156 * \fn GenericAccessService::appearanceRead
157 *
158 * This signal is emitted when the `Appearance` characteristic has been read successfully.
159 *
160 * \see readAppearanceCharacteristic
161 * \see appearance
162 */
163
164/*!
165 * \fn GenericAccessService::deviceNameRead
166 *
167 * This signal is emitted when the `Device Name` characteristic has been read successfully.
168 *
169 * \see readDeviceName
170 */
171
172/*!
173 * \fn GenericAccessService::deviceNameWritten
174 *
175 * This signal is emitted when the `Device Name` characteristic has been written successfully.
176 *
177 * \see setDeviceName
178 */
179
180/*!
181 * \cond internal
182 * \class GenericAccessServicePrivate
183 *
184 * The GenericAccessServicePrivate class provides private implementation for GenericAccessService.
185 */
186
187/*!
188 * \internal
189 * Constructs a new GenericAccessServicePrivate object with public implementation \a q.
190 */
197
198/*!
199 * Parses the `Appearance` \a value. Returns `0xFFFF` if not valid.
200 */
202{
203 if (!checkSize(QLatin1String("Appearance"), value, 2, 2)) {
204 return std::numeric_limits<quint16>::max();
205 }
206 const quint16 appearance = qFromLittleEndian<quint16>(value.constData());
207 qCDebug(lc).noquote() << tr("Appearance: %1.").arg(appearance);
208 return appearance;
209}
210
211/*!
212 * Implements AbstractPokitServicePrivate::characteristicRead to parse \a value, then emit a
213 * specialised signal, for each supported \a characteristic.
214 */
216 const QByteArray &value)
217{
219
222 Q_EMIT q->appearanceRead(parseAppearance(value));
223 return;
224 }
225
227 const QString deviceName = QString::fromUtf8(value);
228 qCDebug(lc).noquote() << tr(R"(Device name: "%1")").arg(deviceName);
229 Q_EMIT q->deviceNameRead(deviceName);
230 return;
231 }
232
233 qCWarning(lc).noquote() << tr("Unknown characteristic read for Generic Access service")
234 << serviceUuid << characteristic.name() << characteristic.uuid();
235}
236
237/*!
238 * Implements AbstractPokitServicePrivate::characteristicWritten to parse \a newValue, then emit a
239 * specialised signal, for each supported \a characteristic.
240 */
242 const QByteArray &newValue)
243{
245
248 qCWarning(lc).noquote() << tr("Appearance haracteristic is read-only, but somehow written")
249 << serviceUuid << characteristic.name() << characteristic.uuid();
250 return;
251 }
252
254 Q_EMIT q->deviceNameWritten();
255 return;
256 }
257
258 qCWarning(lc).noquote() << tr("Unknown characteristic written for Generic Access service")
259 << serviceUuid << characteristic.name() << characteristic.uuid();
260}
261
262/// \endcond
The AbstractPokitServicePrivate class provides private implementation for AbstractPokitService.
QBluetoothUuid serviceUuid
UUIDs for service.
virtual void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
Handles QLowEnergyService::characteristicRead events.
virtual void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
Handles QLowEnergyService::characteristicWritten events.
static bool checkSize(const QString &label, const QByteArray &data, const int minSize, const int maxSize=-1, const bool failOnMax=false)
Returns false if data is smaller than minSize, otherwise returns failOnMax if data is bigger than max...
The AbstractPokitService class provides a common base for Pokit services classes.
The GenericAccessServicePrivate class provides private implementation for GenericAccessService.
GenericAccessServicePrivate(QLowEnergyController *controller, GenericAccessService *const q)
void characteristicWritten(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) override
Implements AbstractPokitServicePrivate::characteristicWritten to parse newValue, then emit a speciali...
void characteristicRead(const QLowEnergyCharacteristic &characteristic, const QByteArray &value) override
Implements AbstractPokitServicePrivate::characteristicRead to parse value, then emit a specialised si...
static quint16 parseAppearance(const QByteArray &value)
Parses the Appearance value.
The GenericAccessService class accesses the Generic Access service of Pokit devices.
quint16 appearance() const
Returns the most recent value of the Generic Access services's Appearance characteristic.
bool readDeviceNameCharacteristic()
Read the Generic Access service's Device Name characteristic.
bool readAppearanceCharacteristic()
Read the Generic Access service's Appearance characteristic.
GenericAccessService(QLowEnergyController *const pokitDevice, QObject *parent=nullptr)
Constructs a new Pokit service with parent.
bool setDeviceName(const QString &name)
Set's the Pokit device's name to name.
QString deviceName() const
Returns the most recent value of the Generic Access services's Device Name characteristic.
bool readCharacteristics() override
Read all characteristics.
~GenericAccessService() override
Destroys this GenericAccessService object.
Declares the GenericAccessService class.
Declares the GenericAccessServicePrivate class.
const char * constData() const const
int length() const const
QByteArray toHex() const const
bool isValid() const const
QString name() const const
QBluetoothUuid uuid() const const
QByteArray value() const const
Q_EMITQ_EMIT
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromUtf8(const char *str, int size)
QByteArray toUtf8() const const
static const QBluetoothUuid deviceName
UUID of the Generic Access service's Device Name characterstic.
static const QBluetoothUuid appearance
UUID of the Generic Access service's Appearance characterstic.