nymea-plugins/nuki/bluez/bluetoothgattdescriptor.cpp

229 lines
8.4 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-plugins.
*
* nymea-plugins is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nymea-plugins is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nymea-plugins. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "bluetoothgattdescriptor.h"
#include <QDBusReply>
QString BluetoothGattDescriptor::name() const
{
bool ok = false;
quint16 typeId = m_uuid.toUInt16(&ok);
if (ok) {
QBluetoothUuid::DescriptorType type = static_cast<QBluetoothUuid::DescriptorType>(typeId);
const QString name = QBluetoothUuid::descriptorToString(type);
if (!name.isEmpty())
return name;
}
return QString("Unknown Descriptor");
}
QBluetoothUuid BluetoothGattDescriptor::uuid() const
{
return m_uuid;
}
QByteArray BluetoothGattDescriptor::value() const
{
return m_value;
}
BluetoothGattDescriptor::Properties BluetoothGattDescriptor::properties() const
{
return m_properties;
}
BluetoothGattDescriptor::BluetoothGattDescriptor(const QDBusObjectPath &path, const QVariantMap &properties, QObject *parent) :
QObject(parent),
m_path(path)
{
m_descriptorInterface = new QDBusInterface(orgBluez, m_path.path(), orgBluezGattDescriptor1, QDBusConnection::systemBus(), this);
if (!m_descriptorInterface->isValid()) {
qCWarning(dcBluez()) << "Invalid DBus descriptor interface for" << m_path.path();
return;
}
QDBusConnection::systemBus().connect(orgBluez, m_path.path(), "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
processProperties(properties);
QDBusPendingCall readingCall = m_descriptorInterface->asyncCall("GetAll", QVariantMap());
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(readingCall, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [=](){
qCDebug(dcBluez()) << "Get descriptor properties finished";
});
}
void BluetoothGattDescriptor::processProperties(const QVariantMap &properties)
{
qCDebug(dcBluez()) << "Descriptor properties" << properties;
foreach (const QString &propertyName, properties.keys()) {
if (propertyName == "UUID") {
m_uuid = QBluetoothUuid(properties.value(propertyName).toString());
} else if (propertyName == "Value") {
setValueInternally(properties.value(propertyName).toByteArray());
} else if (propertyName == "Flags") {
m_properties = parsePropertyFlags(properties.value(propertyName).toStringList());
}
}
}
void BluetoothGattDescriptor::setValueInternally(const QByteArray &value)
{
if (m_value != value) {
m_value = value;
emit valueChanged(m_value);
}
}
BluetoothGattDescriptor::Properties BluetoothGattDescriptor::parsePropertyFlags(const QStringList &descriptorProperties)
{
Properties properties;
foreach (const QString &propertyString, descriptorProperties) {
if (propertyString == "read") {
properties |= Read;
} else if (propertyString == "write") {
properties |= Write;
} else if (propertyString == "encrypt-read") {
properties |= EncryptRead;
} else if (propertyString == "encrypt-write") {
properties |= EncryptWrite;
} else if (propertyString == "encrypt-authenticated-read") {
properties |= EncryptAuthenticatedRead;
} else if (propertyString == "encrypt-authenticated-write") {
properties |= EncryptAuthenticatedWrite;
} else if (propertyString == "secure-read") {
properties |= SecureRead;
} else if (propertyString == "secure-write") {
properties |= SecureWrite;
}
}
return properties;
}
void BluetoothGattDescriptor::onPropertiesChanged(const QString &interface, const QVariantMap &changedProperties, const QStringList &invalidatedProperties)
{
if (interface != orgBluezGattDescriptor1)
return;
qCDebug(dcBluez()) << "BluetoothDescriptor:" << m_uuid << "properties changed" << interface << changedProperties << invalidatedProperties;
processProperties(changedProperties);
}
void BluetoothGattDescriptor::onReadingFinished(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<QByteArray> reply = *call;
if (reply.isError()) {
qCWarning(dcBluez()) << "Could not read descriptor" << m_uuid.toString() << reply.error().name() << reply.error().message();
} else {
QByteArray value = reply.argumentAt<0>();
qCDebug(dcBluez()) << "Async descriptor reading finished for" << m_uuid.toString() << value;
setValueInternally(value);
emit readingFinished(value);
}
call->deleteLater();
}
void BluetoothGattDescriptor::onWritingFinished(QDBusPendingCallWatcher *call)
{
QByteArray value = m_asyncWrites.take(call);
QDBusPendingReply<void> reply = *call;
if (reply.isError()) {
qCWarning(dcBluez()) << "Could not write descriptor" << m_uuid.toString() << reply.error().name() << reply.error().message();
} else {
qCDebug(dcBluez()) << "Async descriptor writing finished for" << m_uuid.toString() << value;
emit writingFinished(value);
}
call->deleteLater();
}
bool BluetoothGattDescriptor::readValue()
{
if (!m_descriptorInterface->isValid()) {
qCWarning(dcBluez()) << "Invalid DBus characteristic interface for" << m_path.path();
return false;
}
QDBusPendingCall readingCall = m_descriptorInterface->asyncCall("ReadValue", QVariantMap());
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(readingCall, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, &BluetoothGattDescriptor::onReadingFinished);
return true;
}
bool BluetoothGattDescriptor::writeValue(const QByteArray &value)
{
if (!m_descriptorInterface->isValid()) {
qCWarning(dcBluez()) << "Invalid DBus characteristic interface for" << m_path.path();
return false;
}
QDBusPendingCall writingCall = m_descriptorInterface->asyncCall("WriteValue", value, QVariantMap());
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(writingCall, this);
m_asyncWrites.insert(watcher, value);
connect(watcher, &QDBusPendingCallWatcher::finished, this, &BluetoothGattDescriptor::onWritingFinished);
return true;
}
QDebug operator<<(QDebug debug, BluetoothGattDescriptor *descriptor)
{
debug.noquote().nospace() << "GattDescriptor(" << descriptor->name();
debug.noquote().nospace() << ", " << descriptor->uuid().toString();
debug.noquote().nospace() << ", Properties: ";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::Read))
debug.noquote().nospace() << " R ";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::Write))
debug.noquote().nospace() << " W";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::EncryptRead))
debug.noquote().nospace() << " ER";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::EncryptWrite))
debug.noquote().nospace() << " EW";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::EncryptAuthenticatedRead))
debug.noquote().nospace() << " EAR";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::EncryptAuthenticatedWrite))
debug.noquote().nospace() << " EAW";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::SecureRead))
debug.noquote().nospace() << " SR";
if (descriptor->properties().testFlag(BluetoothGattDescriptor::SecureWrite))
debug.noquote().nospace() << " SW";
debug.noquote().nospace() << ", value: " << descriptor->value();
debug.noquote().nospace() << ") ";
return debug;
}