// 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 . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "bluetoothgattdescriptor.h" #include QString BluetoothGattDescriptor::name() const { bool ok = false; quint16 typeId = m_uuid.toUInt16(&ok); if (ok) { QBluetoothUuid::DescriptorType type = static_cast(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 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 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; }