Skip to content

Commit eb1b633

Browse files
authored
[Linux] Let users edit Phone bluetooth MAC via GUI (#195)
initial commit
1 parent dde5d1e commit eb1b633

2 files changed

Lines changed: 98 additions & 0 deletions

File tree

linux/Main.qml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,28 @@ ApplicationWindow {
154154
visible: airPodsTrayApp.airpodsConnected
155155
text: "Conversational Awareness"
156156
checked: airPodsTrayApp.deviceInfo.conversationalAwareness
157+
// Disable when no phone MAC set or using default placeholder
158+
enabled: !(PHONE_MAC_ADDRESS === "" || PHONE_MAC_ADDRESS === "00:00:00:00:00:00")
157159
onCheckedChanged: airPodsTrayApp.setConversationalAwareness(checked)
158160
}
161+
162+
// Instruction when Conversational Awareness is disabled due to missing phone MAC
163+
Row {
164+
anchors.horizontalCenter: parent.horizontalCenter
165+
spacing: 8
166+
visible: airPodsTrayApp.airpodsConnected && (PHONE_MAC_ADDRESS === "" || PHONE_MAC_ADDRESS === "00:00:00:00:00:00")
167+
168+
Label {
169+
text: "Set your phone's MAC in Settings to use this feature"
170+
font.pixelSize: 12
171+
color: "gray"
172+
}
173+
174+
Button {
175+
text: "Open Settings"
176+
onClicked: stackView.push(settingsPage)
177+
}
178+
}
159179
}
160180

161181
RoundButton {
@@ -269,6 +289,23 @@ ApplicationWindow {
269289
}
270290
}
271291

292+
Row {
293+
spacing: 10
294+
visible: airPodsTrayApp.airpodsConnected
295+
296+
TextField {
297+
id: newPhoneMacField
298+
placeholderText: (PHONE_MAC_ADDRESS !== "" ? PHONE_MAC_ADDRESS : "00:00:00:00:00:00")
299+
maximumLength: 32
300+
}
301+
302+
Button {
303+
text: "Change Phone MAC"
304+
onClicked: airPodsTrayApp.setPhoneMac(newPhoneMacField.text)
305+
}
306+
}
307+
308+
272309
Button {
273310
text: "Show Magic Cloud Keys QR"
274311
onClicked: keysQrDialog.show()

linux/main.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <QThread>
1212
#include <QTimer>
1313
#include <QProcess>
14+
#include <QRegularExpression>
1415

1516
#include "airpods_packets.h"
1617
#include "logger.h"
@@ -40,6 +41,7 @@ class AirPodsTrayApp : public QObject {
4041
Q_PROPERTY(int retryAttempts READ retryAttempts WRITE setRetryAttempts NOTIFY retryAttemptsChanged)
4142
Q_PROPERTY(bool hideOnStart READ hideOnStart CONSTANT)
4243
Q_PROPERTY(DeviceInfo *deviceInfo READ deviceInfo CONSTANT)
44+
Q_PROPERTY(QString phoneMacStatus READ phoneMacStatus NOTIFY phoneMacStatusChanged)
4345

4446
public:
4547
AirPodsTrayApp(bool debugMode, bool hideOnStart, QQmlApplicationEngine *parent = nullptr)
@@ -119,6 +121,7 @@ class AirPodsTrayApp : public QObject {
119121
int retryAttempts() const { return m_retryAttempts; }
120122
bool hideOnStart() const { return m_hideOnStart; }
121123
DeviceInfo *deviceInfo() const { return m_deviceInfo; }
124+
QString phoneMacStatus() const { return m_phoneMacStatus; }
122125

123126
private:
124127
bool debugMode;
@@ -310,6 +313,51 @@ public slots:
310313
emit crossDeviceEnabledChanged(enabled);
311314
}
312315

316+
void setPhoneMac(const QString &mac)
317+
{
318+
if (mac.isEmpty()) {
319+
LOG_WARN("Empty MAC provided, ignoring");
320+
m_phoneMacStatus = QStringLiteral("No MAC provided (ignoring)");
321+
emit phoneMacStatusChanged();
322+
return;
323+
}
324+
325+
// Basic MAC address validation (accepts formats like AA:BB:CC:DD:EE:FF, AABBCCDDEEFF, AA-BB-CC-DD-EE-FF)
326+
QRegularExpression re("^([0-9A-Fa-f]{2}([-:]?)){5}[0-9A-Fa-f]{2}$");
327+
if (!re.match(mac).hasMatch()) {
328+
LOG_ERROR("Invalid MAC address format: " << mac);
329+
m_phoneMacStatus = QStringLiteral("Invalid MAC: ") + mac;
330+
emit phoneMacStatusChanged();
331+
return;
332+
}
333+
334+
// Set environment variable for the running process
335+
qputenv("PHONE_MAC_ADDRESS", mac.toUtf8());
336+
LOG_INFO("PHONE_MAC_ADDRESS environment variable set to: " << mac);
337+
338+
m_phoneMacStatus = QStringLiteral("Updated MAC: ") + mac;
339+
emit phoneMacStatusChanged();
340+
341+
// Update QML context property so UI placeholders reflect the new value
342+
if (parent) {
343+
parent->rootContext()->setContextProperty("PHONE_MAC_ADDRESS", mac);
344+
}
345+
346+
// If a phone socket exists, restart connection using the new MAC
347+
if (phoneSocket && phoneSocket->isOpen()) {
348+
phoneSocket->close();
349+
phoneSocket->deleteLater();
350+
phoneSocket = nullptr;
351+
}
352+
connectToPhone();
353+
}
354+
355+
void updatePhoneMacStatus(const QString &status)
356+
{
357+
m_phoneMacStatus = status;
358+
emit phoneMacStatusChanged();
359+
}
360+
313361
bool writePacketToSocket(const QByteArray &packet, const QString &logMessage)
314362
{
315363
if (socket && socket->isOpen())
@@ -658,6 +706,7 @@ private slots:
658706
}
659707
QBluetoothAddress phoneAddress("00:00:00:00:00:00"); // Default address, will be overwritten if PHONE_MAC_ADDRESS is set
660708
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
709+
661710
if (!env.value("PHONE_MAC_ADDRESS").isEmpty())
662711
{
663712
phoneAddress = QBluetoothAddress(env.value("PHONE_MAC_ADDRESS"));
@@ -854,6 +903,7 @@ private slots:
854903
void notificationsEnabledChanged(bool enabled);
855904
void retryAttemptsChanged(int attempts);
856905
void oneBudANCModeChanged(bool enabled);
906+
void phoneMacStatusChanged();
857907

858908
private:
859909
QBluetoothSocket *socket = nullptr;
@@ -870,6 +920,7 @@ private slots:
870920
DeviceInfo *m_deviceInfo;
871921
BleManager *m_bleManager;
872922
SystemSleepMonitor *m_systemSleepMonitor = nullptr;
923+
QString m_phoneMacStatus;
873924
};
874925

875926
int main(int argc, char *argv[]) {
@@ -917,6 +968,16 @@ int main(int argc, char *argv[]) {
917968
qmlRegisterType<DeviceInfo>("me.kavishdevar.DeviceInfo", 1, 0, "DeviceInfo");
918969
AirPodsTrayApp *trayApp = new AirPodsTrayApp(debugMode, hideOnStart, &engine);
919970
engine.rootContext()->setContextProperty("airPodsTrayApp", trayApp);
971+
972+
// Expose PHONE_MAC_ADDRESS environment variable to QML for placeholder in settings
973+
{
974+
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
975+
QString phoneMacEnv = env.value("PHONE_MAC_ADDRESS", "");
976+
engine.rootContext()->setContextProperty("PHONE_MAC_ADDRESS", phoneMacEnv);
977+
// Initialize the visible status in the GUI
978+
trayApp->updatePhoneMacStatus(phoneMacEnv.isEmpty() ? QStringLiteral("No phone MAC set") : phoneMacEnv);
979+
}
980+
920981
engine.addImageProvider("qrcode", new QRCodeImageProvider());
921982
trayApp->loadMainModule();
922983

0 commit comments

Comments
 (0)