From e4048a2ba628cf509cd3bae12d4a57ac9a3aec7e Mon Sep 17 00:00:00 2001 From: jamescarter2001 Date: Sun, 4 Jan 2026 13:29:16 +0000 Subject: [PATCH 1/2] fix connected device detection when using latest wireplumber version --- linux/main.cpp | 11 ++---- linux/media/mediacontroller.cpp | 6 +-- linux/media/pulseaudiocontroller.cpp | 55 +++++++++++++++++++++++++++- linux/media/pulseaudiocontroller.h | 2 + 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/linux/main.cpp b/linux/main.cpp index 94d341ea3..64317d2c4 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -105,8 +105,7 @@ class AirPodsTrayApp : public QObject { // On startup after reboot, activate A2DP profile for already connected AirPods QTimer::singleShot(2000, this, [this, address]() { - QString formattedAddress = address.toString().replace(":", "_"); - mediaController->setConnectedDeviceMacAddress(formattedAddress); + mediaController->setConnectedDeviceMacAddress(address.toString()); mediaController->activateA2dpProfile(); LOG_INFO("A2DP profile activation attempted for AirPods found on startup"); }); @@ -427,7 +426,7 @@ public slots: if (areAirpodsConnected() && m_deviceInfo && !m_deviceInfo->bluetoothAddress().isEmpty()) { LOG_INFO("AirPods already connected after wake-up, re-activating A2DP profile"); - mediaController->setConnectedDeviceMacAddress(m_deviceInfo->bluetoothAddress().replace(":", "_")); + mediaController->setConnectedDeviceMacAddress(m_deviceInfo->bluetoothAddress()); // Always activate A2DP profile after system wake since the profile might have been lost QTimer::singleShot(1000, this, [this]() @@ -494,9 +493,7 @@ private slots: { if (!address.isEmpty()) { - QString formattedAddress = address; - formattedAddress = formattedAddress.replace(":", "_"); - mediaController->setConnectedDeviceMacAddress(formattedAddress); + mediaController->setConnectedDeviceMacAddress(address); mediaController->activateA2dpProfile(); LOG_INFO("A2DP profile activation attempted for newly connected device"); } @@ -738,7 +735,7 @@ private slots: { parseMetadata(data); initiateMagicPairing(); - mediaController->setConnectedDeviceMacAddress(m_deviceInfo->bluetoothAddress().replace(":", "_")); + mediaController->setConnectedDeviceMacAddress(m_deviceInfo->bluetoothAddress()); if (m_deviceInfo->getEarDetection()->oneOrMorePodsInEar()) // AirPods get added as output device only after this { mediaController->activateA2dpProfile(); diff --git a/linux/media/mediacontroller.cpp b/linux/media/mediacontroller.cpp index 078129c5a..06091bd55 100644 --- a/linux/media/mediacontroller.cpp +++ b/linux/media/mediacontroller.cpp @@ -95,9 +95,9 @@ void MediaController::followMediaChanges() { } bool MediaController::isActiveOutputDeviceAirPods() { - QString defaultSink = m_pulseAudio->getDefaultSink(); - LOG_DEBUG("Default sink: " << defaultSink); - return defaultSink.contains(connectedDeviceMacAddress); + QString defaultSinkMacAddress = m_pulseAudio->getDefaultSinkMacAddress(); + LOG_DEBUG("Default sink MAC address: " << defaultSinkMacAddress); + return defaultSinkMacAddress == connectedDeviceMacAddress; } void MediaController::handleConversationalAwareness(const QByteArray &data) { diff --git a/linux/media/pulseaudiocontroller.cpp b/linux/media/pulseaudiocontroller.cpp index d1535d0ee..e74383bd4 100644 --- a/linux/media/pulseaudiocontroller.cpp +++ b/linux/media/pulseaudiocontroller.cpp @@ -105,11 +105,60 @@ QString PulseAudioController::getDefaultSink() waitForOperation(op); pa_operation_unref(op); } + pa_threaded_mainloop_unlock(m_mainloop); return data.sinkName; } +QString PulseAudioController::getDefaultSinkMacAddress() { + return this->getMacAddressBySinkName(this->getDefaultSink()); +} + +QString PulseAudioController::getMacAddressBySinkName(const QString &sinkName) +{ + if (!m_initialized) return QString(); + + struct CallbackData { + QString sinkMacAddress; + pa_threaded_mainloop *mainloop; + } data; + data.mainloop = m_mainloop; + + auto callback = [](pa_context *c, const pa_sink_info *info, int eol, void *userdata) + { + CallbackData *d = static_cast(userdata); + if (eol > 0) + { + pa_threaded_mainloop_signal(d->mainloop, 0); + return; + } + + const char *addr = pa_proplist_gets( + info->proplist, + "device.string" + ); + + if (addr) + { + d->sinkMacAddress = QString::fromUtf8(addr); + pa_threaded_mainloop_signal(d->mainloop, 0); + } + }; + + pa_threaded_mainloop_lock(m_mainloop); + pa_operation *op = pa_context_get_sink_info_by_name(m_context, sinkName.toLocal8Bit(), callback, &data); + if (op) + { + waitForOperation(op); + pa_operation_unref(op); + } + + pa_threaded_mainloop_unlock(m_mainloop); + + return data.sinkMacAddress; +} + int PulseAudioController::getSinkVolume(const QString &sinkName) { if (!m_initialized) return -1; @@ -215,8 +264,10 @@ QString PulseAudioController::getCardNameForDevice(const QString &macAddress) } if (info) { - QString name = QString::fromUtf8(info->name); - if (name.startsWith("bluez") && name.contains(d->targetMac)) + const QString name = QString::fromUtf8(info->name); + const QString macAddress = pa_proplist_gets(info->proplist, "device.string"); + + if (d->targetMac == macAddress) { d->cardName = name; pa_threaded_mainloop_signal(d->mainloop, 0); diff --git a/linux/media/pulseaudiocontroller.h b/linux/media/pulseaudiocontroller.h index 9ee1b2793..807e07467 100644 --- a/linux/media/pulseaudiocontroller.h +++ b/linux/media/pulseaudiocontroller.h @@ -15,6 +15,7 @@ class PulseAudioController : public QObject bool initialize(); QString getDefaultSink(); + QString getDefaultSinkMacAddress(); int getSinkVolume(const QString &sinkName); bool setSinkVolume(const QString &sinkName, int volumePercent); bool setCardProfile(const QString &cardName, const QString &profileName); @@ -32,6 +33,7 @@ class PulseAudioController : public QObject static void serverInfoCallback(pa_context *c, const pa_server_info *info, void *userdata); bool waitForOperation(pa_operation *op); + QString getMacAddressBySinkName(const QString &sinkName); }; #endif // PULSEAUDIOCONTROLLER_H From 69bb94b963940b1f5d4ec946d13eab85be08956d Mon Sep 17 00:00:00 2001 From: jamescarter2001 Date: Sun, 4 Jan 2026 13:49:49 +0000 Subject: [PATCH 2/2] apply CodeRabbit feedback --- linux/media/pulseaudiocontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux/media/pulseaudiocontroller.cpp b/linux/media/pulseaudiocontroller.cpp index e74383bd4..299626162 100644 --- a/linux/media/pulseaudiocontroller.cpp +++ b/linux/media/pulseaudiocontroller.cpp @@ -147,7 +147,7 @@ QString PulseAudioController::getMacAddressBySinkName(const QString &sinkName) }; pa_threaded_mainloop_lock(m_mainloop); - pa_operation *op = pa_context_get_sink_info_by_name(m_context, sinkName.toLocal8Bit(), callback, &data); + pa_operation *op = pa_context_get_sink_info_by_name(m_context, sinkName.toUtf8().constData(), callback, &data); if (op) { waitForOperation(op);