Dokit
Internal development documentation
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
statuscommand.cpp
1// SPDX-FileCopyrightText: 2022-2025 Paul Colby <git@colby.id.au>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "statuscommand.h"
6
8
9#include <QJsonDocument>
10#include <QJsonObject>
11
12#include <iostream>
13
15
16/*!
17 * \class StatusCommand
18 *
19 * The StatusCommand class implements the `status` CLI command.
20 */
21
22/*!
23 * Construct a new StatusCommand object with \a parent.
24 */
29
35
40
41/*!
42 * \copybrief DeviceCommand::processOptions
43 *
44 * This implementation extends DeviceCommand::processOptions to process additional CLI options
45 * supported (or required) by this command.
46 */
48{
50 if (!errors.isEmpty()) {
51 return errors;
52 }
53
54 return errors;
55}
56
57/*!
58 * \copybrief DeviceCommand::getService
59 *
60 * This override returns a pointer to a StatusService object.
61 */
63{
64 Q_ASSERT(device);
65 if (!service) {
66 service = device->status();
67 Q_ASSERT(service);
68 }
69 return service;
70}
71
72/*!
73 * \copybrief DeviceCommand::serviceDetailsDiscovered
74 *
75 * This override fetches the current device's status, and outputs it in the selected format.
76 */
78{
79 DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
80 const StatusService::DeviceCharacteristics chrs = service->deviceCharacteristics();
81 if (chrs.firmwareVersion.isNull()) {
82 qCWarning(lc).noquote() << tr("Failed to parse device information");
83 QCoreApplication::exit(EXIT_FAILURE);
84 return;
85 }
87}
88
89/*!
90 * Outputs the Pokit device's details, including \a chrs, in the selected format.
91 */
93{
94 const QString deviceName = service->deviceName();
95 const StatusService::Status status = service->status();
96 const std::optional<StatusService::TorchStatus> torchStatus = service->torchStatus();
97 const std::optional<StatusService::ButtonStatus> buttonStatus = service->buttonPress();
98 const QString statusLabel = StatusService::toString(status.deviceStatus);
99 const QString batteryLabel = StatusService::toString(status.batteryStatus);
100 const QString switchLabel = status.switchPosition ? StatusService::toString(*status.switchPosition) : QString();
101 const QString chargingLabel = status.chargingStatus ? StatusService::toString(*status.chargingStatus) : QString();
102 const QString torchLabel = (torchStatus) ? StatusService::toString(*torchStatus) : QString();
103 const QString buttonLabel = (buttonStatus) ? StatusService::toString(*buttonStatus) : QString();
104
105 switch (format) {
107 std::cout << qUtf8Printable(tr("device_name,device_status,firmware_version,maximum_voltage,"
108 "maximum_current,maximum_resistance,maximum_sampling_rate,"
109 "sampling_buffer_size,capability_mask,mac_address,battery_voltage,"
110 "battery_status,torch_status,button_status,switch_position,charging_status\n"));
111 std::cout << qUtf8Printable(QString::fromLatin1("%1,%2,%3,%4,%5,%6,%7,%8,%9,%10,%11,%12,%13,%14,%15,%16\n")
112 .arg(escapeCsvField(deviceName),statusLabel.toLower(),chrs.firmwareVersion.toString())
113 .arg(chrs.maximumVoltage).arg(chrs.maximumCurrent).arg(chrs.maximumResistance)
114 .arg(chrs.maximumSamplingRate).arg(chrs.samplingBufferSize).arg(chrs.capabilityMask)
115 .arg(chrs.macAddress.toString()).arg(status.batteryVoltage)
116 .arg(batteryLabel.toLower(), torchLabel.toLower(), buttonLabel.toLower(), switchLabel.toLower(),
117 chargingLabel.toLower()));
118 break;
119 case OutputFormat::Json: {
120 QJsonObject battery{
121 { u"level"_s, status.batteryVoltage },
122 };
123 if (!batteryLabel.isNull()) {
124 battery.insert(u"status"_s, batteryLabel);
125 }
126 QJsonObject object{
127 { u"deviceName"_s, deviceName },
128 { u"firmwareVersion"_s, QJsonObject{
129 { u"major"_s, chrs.firmwareVersion.majorVersion() },
130 { u"minor"_s, chrs.firmwareVersion.minorVersion() },
131 }},
132 { u"maximumVoltage"_s, chrs.maximumVoltage },
133 { u"maximumCurrent"_s, chrs.maximumCurrent },
134 { u"maximumResistance"_s, chrs.maximumResistance },
135 { u"maximumSamplingRate"_s, chrs.maximumSamplingRate },
136 { u"samplingBufferSize"_s, chrs.samplingBufferSize },
137 { u"capabilityMask"_s, chrs.capabilityMask },
138 { u"macAddress"_s, chrs.macAddress.toString() },
139 { u"deviceStatus"_s, QJsonObject{
140 { u"code"_s, (quint8)status.deviceStatus },
141 { u"label"_s, statusLabel },
142 }},
143 { u"battery"_s, battery },
144 };
145 if (torchStatus) {
146 object.insert(u"torchStatus"_s, QJsonObject{
147 { u"code"_s, (quint8)*torchStatus },
148 { u"label"_s, torchLabel },
149 });
150 }
151 if (buttonStatus) {
152 object.insert(u"buttonStatus"_s, QJsonObject{
153 { u"code"_s, (quint8)*buttonStatus },
154 { u"label"_s, buttonLabel },
155 });
156 }
157 if (status.switchPosition) {
158 object.insert(u"switchStatus"_s, QJsonObject{
159 { u"code"_s, (quint8)*status.switchPosition },
160 { u"label"_s, switchLabel },
161 });
162 }
163 if (status.chargingStatus) {
164 object.insert(u"chargingStatus"_s, QJsonObject{
165 { u"code"_s, (quint8)*status.chargingStatus },
166 { u"label"_s, chargingLabel },
167 });
168 }
169 std::cout << QJsonDocument(object).toJson().toStdString();
170 } break;
172 std::cout << qUtf8Printable(tr("Device name: %1\n").arg(deviceName));
173 std::cout << qUtf8Printable(tr("Firmware version: %1\n").arg(chrs.firmwareVersion.toString()));
174 std::cout << qUtf8Printable(tr("Maximum voltage: %1\n").arg(chrs.maximumVoltage));
175 std::cout << qUtf8Printable(tr("Maximum current: %1\n").arg(chrs.maximumCurrent));
176 std::cout << qUtf8Printable(tr("Maximum resistance: %1\n").arg(chrs.maximumResistance));
177 std::cout << qUtf8Printable(tr("Maximum sampling rate: %1\n").arg(chrs.maximumSamplingRate));
178 std::cout << qUtf8Printable(tr("Sampling buffer size: %1\n").arg(chrs.samplingBufferSize));
179 std::cout << qUtf8Printable(tr("Capability mask: %1\n").arg(chrs.capabilityMask));
180 std::cout << qUtf8Printable(tr("MAC address: %1\n").arg(chrs.macAddress.toString()));
181 std::cout << qUtf8Printable(tr("Device status: %1 (%2)\n").arg(statusLabel)
182 .arg((quint8)status.deviceStatus));
183 std::cout << qUtf8Printable(tr("Battery voltage: %1\n").arg(status.batteryVoltage));
184 std::cout << qUtf8Printable(tr("Battery status: %1 (%2)\n")
185 .arg(batteryLabel.isNull() ? QString::fromLatin1("N/A") : batteryLabel)
186 .arg((quint8)status.batteryStatus));
187 if (status.switchPosition) {
188 std::cout << qUtf8Printable(tr("Switch position: %1 (%2)\n")
189 .arg(switchLabel).arg((quint8)*status.switchPosition));
190 }
191 if (status.chargingStatus) {
192 std::cout << qUtf8Printable(tr("Charging status: %1 (%2)\n")
193 .arg(chargingLabel).arg((quint8)*status.chargingStatus));
194 }
195 break;
196 }
197 if (device) disconnect(); // Will exit the application once disconnected.
198}
virtual QStringList supportedOptions(const QCommandLineParser &parser) const
Returns a list of CLI option names supported by this command.
OutputFormat format
Selected output format.
@ Text
Plain unstructured text.
@ Csv
RFC 4180 compliant CSV text.
@ Json
RFC 8259 compliant JSON text.
virtual QStringList processOptions(const QCommandLineParser &parser)
Processes the relevant options from the command line parser.
static QString escapeCsvField(const QString &field)
Returns an RFC 4180 compliant version of field.
virtual QStringList requiredOptions(const QCommandLineParser &parser) const
Returns a list of CLI option names required by this command.
The AbstractPokitService class provides a common base for Pokit services classes.
PokitDevice * device
Pokit Bluetooth device (if any) this command interacts with.
DeviceCommand(QObject *const parent=nullptr)
Construct a new DeviceCommand object with parent.
virtual void serviceDetailsDiscovered()
Handles service detail discovery events.
void disconnect(int exitCode=EXIT_SUCCESS)
Disconnects the underlying Pokit device, and sets exitCode to be return to the OS once the disconnect...
QStringList supportedOptions(const QCommandLineParser &parser) const override
Returns a list of CLI option names supported by this command.
QStringList requiredOptions(const QCommandLineParser &parser) const override
Returns a list of CLI option names required by this command.
QStringList processOptions(const QCommandLineParser &parser) override
Processes the relevant options from the command line parser.
StatusService * service
Bluetooth service this command interacts with.
StatusCommand(QObject *const parent=nullptr)
Construct a new StatusCommand object with parent.
AbstractPokitService * getService() override
Returns a Pokit service object for the derived command class.
void outputDeviceStatus(const StatusService::DeviceCharacteristics &chrs)
Outputs the Pokit device's details, including chrs, in the selected format.
void serviceDetailsDiscovered() override
Handles service detail discovery events.
static QString toString(const StatusService::DeviceStatus &status)
Returns a string version of the status enum label.
Declares the PokitDevice class.
QString toString() const const
std::string toStdString() const const
void exit(int returnCode)
QByteArray toJson() const const
bool isEmpty() const const
QObject(QObject *parent)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromLatin1(const char *str, int size)
bool isNull() const const
QString toLower() const const
bool isNull() const const
int majorVersion() const const
int minorVersion() const const
QString toString() const const
Declares the DOKIT_USE_STRINGLITERALS macro, and related functions.
#define DOKIT_USE_STRINGLITERALS
Internal macro for using either official Qt string literals (added in Qt 6.4), or our own equivalent ...
Attributes included in the Device Characteristics characteristic.
quint16 maximumCurrent
Device's maximum input current.
quint16 maximumSamplingRate
Device's maximum sampling rate.
quint16 samplingBufferSize
Device's sampling buffer size.
quint16 maximumVoltage
Device's maximum input voltage.
QBluetoothAddress macAddress
Device's MAC address.
quint16 maximumResistance
Device's maximum input resistance.
QVersionNumber firmwareVersion
Device's major and minor firmware version.
Attributes included in the Status characteristic.
float batteryVoltage
Current battery voltage level.
std::optional< ChargingStatus > chargingStatus
Current charging status, if supported by the device.
DeviceStatus deviceStatus
Current Pokit device status.
BatteryStatus batteryStatus
Logical interpretation the battery voltage level.
std::optional< SwitchPosition > switchPosition
Position of the Pokit device's physical mode switch.