#include "device.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../dbus/properties.hpp" #include "adapter.hpp" #include "bluez.hpp" #include "dbus_device.h" namespace qs::bluetooth { namespace { Q_LOGGING_CATEGORY(logDevice, "quickshell.bluetooth.device", QtWarningMsg); } QString BluetoothDeviceState::toString(BluetoothDeviceState::Enum state) { switch (state) { case BluetoothDeviceState::Disconnected: return QStringLiteral("Disconnected"); case BluetoothDeviceState::Connected: return QStringLiteral("Connected"); case BluetoothDeviceState::Disconnecting: return QStringLiteral("Disconnecting"); case BluetoothDeviceState::Connecting: return QStringLiteral("Connecting"); default: return QStringLiteral("Unknown"); } } BluetoothDevice::BluetoothDevice(const QString& path, QObject* parent): QObject(parent) { this->mInterface = new DBusBluezDeviceInterface("org.bluez", path, QDBusConnection::systemBus(), this); if (!this->mInterface->isValid()) { qCWarning(logDevice) << "Could not create DBus interface for device at" << path; delete this->mInterface; this->mInterface = nullptr; return; } this->properties.setInterface(this->mInterface); } BluetoothAdapter* BluetoothDevice::adapter() const { return Bluez::instance()->adapter(this->bAdapterPath.value().path()); } void BluetoothDevice::setConnected(bool connected) { if (connected == this->bConnected) return; if (connected) { this->connect(); } else { this->disconnect(); } } void BluetoothDevice::setTrusted(bool trusted) { if (trusted == this->bTrusted) return; this->bTrusted = trusted; this->pTrusted.write(); } void BluetoothDevice::setBlocked(bool blocked) { if (blocked == this->bBlocked) return; this->bBlocked = blocked; this->pBlocked.write(); } void BluetoothDevice::setName(const QString& name) { if (name == this->bName) return; this->bName = name; this->pName.write(); } void BluetoothDevice::setWakeAllowed(bool wakeAllowed) { if (wakeAllowed == this->bWakeAllowed) return; this->bWakeAllowed = wakeAllowed; this->pWakeAllowed.write(); } void BluetoothDevice::connect() { if (this->bConnected) { qCCritical(logDevice) << "Device" << this << "is already connected"; return; } if (this->bState == BluetoothDeviceState::Connecting) { qCCritical(logDevice) << "Device" << this << "is already connecting"; return; } qCDebug(logDevice) << "Connecting to device" << this; this->bState = BluetoothDeviceState::Connecting; auto reply = this->mInterface->Connect(); auto* watcher = new QDBusPendingCallWatcher(reply, this); QObject::connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher* watcher) { const QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCWarning(logDevice).nospace() << "Failed to connect to device " << this << ": " << reply.error().message(); this->bState = this->bConnected ? BluetoothDeviceState::Connected : BluetoothDeviceState::Disconnected; } else { qCDebug(logDevice) << "Successfully connected to to device" << this; } delete watcher; } ); } void BluetoothDevice::disconnect() { if (!this->bConnected) { qCCritical(logDevice) << "Device" << this << "is already disconnected"; return; } if (this->bState == BluetoothDeviceState::Disconnecting) { qCCritical(logDevice) << "Device" << this << "is already disconnecting"; return; } qCDebug(logDevice) << "Disconnecting from device" << this; this->bState = BluetoothDeviceState::Disconnecting; auto reply = this->mInterface->Disconnect(); auto* watcher = new QDBusPendingCallWatcher(reply, this); QObject::connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher* watcher) { const QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCWarning(logDevice).nospace() << "Failed to disconnect from device " << this << ": " << reply.error().message(); this->bState = this->bConnected ? BluetoothDeviceState::Connected : BluetoothDeviceState::Disconnected; } else { qCDebug(logDevice) << "Successfully disconnected from from device" << this; } delete watcher; } ); } void BluetoothDevice::pair() { if (this->bPaired) { qCCritical(logDevice) << "Device" << this << "is already paired"; return; } if (this->bPairing) { qCCritical(logDevice) << "Device" << this << "is already pairing"; return; } qCDebug(logDevice) << "Pairing with device" << this; this->bPairing = true; auto reply = this->mInterface->Pair(); auto* watcher = new QDBusPendingCallWatcher(reply, this); QObject::connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher* watcher) { const QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCWarning(logDevice).nospace() << "Failed to pair with device " << this << ": " << reply.error().message(); } else { qCDebug(logDevice) << "Successfully initiated pairing with device" << this; } this->bPairing = false; delete watcher; } ); } void BluetoothDevice::cancelPair() { if (!this->bPairing) { qCCritical(logDevice) << "Device" << this << "is not currently pairing"; return; } qCDebug(logDevice) << "Cancelling pairing with device" << this; auto reply = this->mInterface->CancelPairing(); auto* watcher = new QDBusPendingCallWatcher(reply, this); QObject::connect( watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher* watcher) { const QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCWarning(logDevice) << "Failed to cancel pairing with device" << this << ":" << reply.error().message(); } else { qCDebug(logDevice) << "Successfully cancelled pairing with device" << this; } this->bPairing = false; delete watcher; } ); } void BluetoothDevice::forget() { if (!this->mInterface || !this->mInterface->isValid()) { qCCritical(logDevice) << "Cannot forget - device interface is invalid"; return; } if (auto* adapter = Bluez::instance()->adapter(this->bAdapterPath.value().path())) { qCDebug(logDevice) << "Forgetting device" << this << "via adapter" << adapter; adapter->removeDevice(this->path()); } else { qCCritical(logDevice) << "Could not find adapter for path" << this->bAdapterPath.value().path() << "to forget from"; } } void BluetoothDevice::addInterface(const QString& interface, const QVariantMap& properties) { if (interface == "org.bluez.Device1") { this->properties.updatePropertySet(properties, false); qCDebug(logDevice) << "Updated Device properties for" << this; } else if (interface == "org.bluez.Battery1") { if (!this->mBatteryInterface) { this->mBatteryInterface = new QDBusInterface( "org.bluez", this->path(), "org.bluez.Battery1", QDBusConnection::systemBus(), this ); if (!this->mBatteryInterface->isValid()) { qCWarning(logDevice) << "Could not create Battery interface for device at" << this; delete this->mBatteryInterface; this->mBatteryInterface = nullptr; return; } } this->batteryProperties.setInterface(this->mBatteryInterface); this->batteryProperties.updatePropertySet(properties, false); emit this->batteryAvailableChanged(); qCDebug(logDevice) << "Updated Battery properties for" << this; } } void BluetoothDevice::removeInterface(const QString& interface) { if (interface == "org.bluez.Battery1" && this->mBatteryInterface) { this->batteryProperties.setInterface(nullptr); delete this->mBatteryInterface; this->mBatteryInterface = nullptr; this->bBattery = 0; emit this->batteryAvailableChanged(); qCDebug(logDevice) << "Battery interface removed from device" << this; } } void BluetoothDevice::onConnectedChanged() { this->bState = this->bConnected ? BluetoothDeviceState::Connected : BluetoothDeviceState::Disconnected; emit this->connectedChanged(); } } // namespace qs::bluetooth namespace qs::dbus { using namespace qs::bluetooth; DBusResult DBusDataTransform::fromWire(quint8 percentage) { return DBusResult(percentage * 0.01); } } // namespace qs::dbus QDebug operator<<(QDebug debug, const qs::bluetooth::BluetoothDevice* device) { auto saver = QDebugStateSaver(debug); if (device) { debug.nospace() << "BluetoothDevice(" << static_cast(device) << ", path=" << device->path() << ")"; } else { debug << "BluetoothDevice(nullptr)"; } return debug; }