11#include " blemanager.h"
2+ #include " enums.h"
23#include < QDebug>
34#include < QTimer>
5+ #include " logger.h"
6+ #include < QMap>
7+
8+ AirpodsTrayApp::Enums::AirPodsModel getModelName (quint16 modelId)
9+ {
10+ using namespace AirpodsTrayApp ::Enums;
11+ static const QMap<quint16, AirPodsModel> modelMap = {
12+ {0x0220 , AirPodsModel::AirPods1},
13+ {0x0F20 , AirPodsModel::AirPods2},
14+ {0x1320 , AirPodsModel::AirPods3},
15+ {0x1920 , AirPodsModel::AirPods4},
16+ {0x1B20 , AirPodsModel::AirPods4ANC},
17+ {0x0A20 , AirPodsModel::AirPodsMaxLightning},
18+ {0x1F20 , AirPodsModel::AirPodsMaxUSBC},
19+ {0x0E20 , AirPodsModel::AirPodsPro},
20+ {0x1420 , AirPodsModel::AirPodsPro2Lightning},
21+ {0x2420 , AirPodsModel::AirPodsPro2USBC}
22+ };
23+
24+ return modelMap.value (modelId, AirPodsModel::Unknown);
25+ }
26+
27+ QString getColorName (quint8 colorId)
28+ {
29+ switch (colorId)
30+ {
31+ case 0x00 :
32+ return " White" ;
33+ case 0x01 :
34+ return " Black" ;
35+ case 0x02 :
36+ return " Red" ;
37+ case 0x03 :
38+ return " Blue" ;
39+ case 0x04 :
40+ return " Pink" ;
41+ case 0x05 :
42+ return " Gray" ;
43+ case 0x06 :
44+ return " Silver" ;
45+ case 0x07 :
46+ return " Gold" ;
47+ case 0x08 :
48+ return " Rose Gold" ;
49+ case 0x09 :
50+ return " Space Gray" ;
51+ case 0x0A :
52+ return " Dark Blue" ;
53+ case 0x0B :
54+ return " Light Blue" ;
55+ case 0x0C :
56+ return " Yellow" ;
57+ default :
58+ return " Unknown" ;
59+ }
60+ }
61+
62+ QString getConnectionStateName (BleInfo::ConnectionState state)
63+ {
64+ using ConnectionState = BleInfo::ConnectionState;
65+ switch (state)
66+ {
67+ case ConnectionState::DISCONNECTED:
68+ return QString (" Disconnected" );
69+ case ConnectionState::IDLE:
70+ return QString (" Idle" );
71+ case ConnectionState::MUSIC:
72+ return QString (" Playing Music" );
73+ case ConnectionState::CALL:
74+ return QString (" On Call" );
75+ case ConnectionState::RINGING:
76+ return QString (" Ringing" );
77+ case ConnectionState::HANGING_UP:
78+ return QString (" Hanging Up" );
79+ case ConnectionState::UNKNOWN:
80+ default :
81+ return QString (" Unknown" );
82+ }
83+ }
484
585BleManager::BleManager (QObject *parent) : QObject(parent)
686{
@@ -13,38 +93,25 @@ BleManager::BleManager(QObject *parent) : QObject(parent)
1393 this , &BleManager::onScanFinished);
1494 connect (discoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred,
1595 this , &BleManager::onErrorOccurred);
16-
17- // Set up pruning timer
18- pruneTimer = new QTimer (this );
19- connect (pruneTimer, &QTimer::timeout, this , &BleManager::pruneOldDevices);
20- pruneTimer->start (PRUNE_INTERVAL_MS); // Start timer (runs every 5 seconds)
2196}
2297
2398BleManager::~BleManager ()
2499{
25100 delete discoveryAgent;
26- delete pruneTimer;
27101}
28102
29103void BleManager::startScan ()
30104{
31- qDebug () << " Starting BLE scan..." ;
32- devices.clear ();
105+ LOG_DEBUG (" Starting BLE scan..." );
33106 discoveryAgent->start (QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
34- pruneTimer->start (PRUNE_INTERVAL_MS); // Ensure timer is running
35107}
36108
37109void BleManager::stopScan ()
38110{
39- qDebug () << " Stopping BLE scan..." ;
111+ LOG_DEBUG ( " Stopping BLE scan..." ) ;
40112 discoveryAgent->stop ();
41113}
42114
43- const QMap<QString, DeviceInfo> &BleManager::getDevices () const
44- {
45- return devices;
46- }
47-
48115void BleManager::onDeviceDiscovered (const QBluetoothDeviceInfo &info)
49116{
50117 // Check for Apple's manufacturer ID (0x004C)
@@ -55,10 +122,11 @@ void BleManager::onDeviceDiscovered(const QBluetoothDeviceInfo &info)
55122 if (data.size () >= 10 && data[0 ] == 0x07 )
56123 {
57124 QString address = info.address ().toString ();
58- DeviceInfo deviceInfo;
125+ BleInfo deviceInfo;
59126 deviceInfo.name = info.name ().isEmpty () ? " AirPods" : info.name ();
60127 deviceInfo.address = address;
61- deviceInfo.rawData = data;
128+ deviceInfo.rawData = data.left (data.size () - 16 );
129+ deviceInfo.encryptedPayload = data.mid (data.size () - 16 );
62130
63131 // data[1] is the length of the data, so we can skip it
64132
@@ -68,8 +136,9 @@ void BleManager::onDeviceDiscovered(const QBluetoothDeviceInfo &info)
68136 return ; // Skip pairing mode devices (the values are differently structured)
69137 }
70138
139+
71140 // Parse device model (big-endian: high byte at data[3], low byte at data[4])
72- deviceInfo.deviceModel = static_cast <quint16>(data[4 ]) | (static_cast <quint8>(data[3 ]) << 8 );
141+ deviceInfo.modelName = getModelName ( static_cast <quint16>(data[4 ]) | (static_cast <quint8>(data[3 ]) << 8 ) );
73142
74143 // Status byte for primary pod and other flags
75144 quint8 status = static_cast <quint8>(data[5 ]);
@@ -83,16 +152,18 @@ void BleManager::onDeviceDiscovered(const QBluetoothDeviceInfo &info)
83152
84153 // Lid open counter and device color
85154 quint8 lidIndicator = static_cast <quint8>(data[8 ]);
86- deviceInfo.deviceColor = static_cast < quint8> (data[9 ]);
155+ deviceInfo.color = getColorName (( quint8) (data[9 ]) );
87156
88- deviceInfo.connectionState = static_cast <DeviceInfo ::ConnectionState>(data[10 ]);
157+ deviceInfo.connectionState = static_cast <BleInfo ::ConnectionState>(data[10 ]);
89158
90159 // Next: Encrypted Payload: 16 bytes
91160
92161 // Determine primary pod (bit 5 of status) and value flipping
93162 bool primaryLeft = (status & 0x20 ) != 0 ; // Bit 5: 1 = left primary, 0 = right primary
94163 bool areValuesFlipped = !primaryLeft; // Flipped when right pod is primary
95164
165+ deviceInfo.primaryLeft = primaryLeft; // Store primary pod information
166+
96167 // Parse battery levels
97168 int leftNibble = areValuesFlipped ? (podsBatteryByte >> 4 ) & 0x0F : podsBatteryByte & 0x0F ;
98169 int rightNibble = areValuesFlipped ? podsBatteryByte & 0x0F : (podsBatteryByte >> 4 ) & 0x0F ;
@@ -117,34 +188,30 @@ void BleManager::onDeviceDiscovered(const QBluetoothDeviceInfo &info)
117188 deviceInfo.isLeftPodInEar = xorFactor ? (status & 0x08 ) != 0 : (status & 0x02 ) != 0 ; // Bit 3 or 1
118189 deviceInfo.isRightPodInEar = xorFactor ? (status & 0x02 ) != 0 : (status & 0x08 ) != 0 ; // Bit 1 or 3
119190
191+ // Determine primary and secondary in-ear status
192+ deviceInfo.isPrimaryInEar = primaryLeft ? deviceInfo.isLeftPodInEar : deviceInfo.isRightPodInEar ;
193+ deviceInfo.isSecondaryInEar = primaryLeft ? deviceInfo.isRightPodInEar : deviceInfo.isLeftPodInEar ;
194+
120195 // Microphone status
121196 deviceInfo.isLeftPodMicrophone = primaryLeft ^ deviceInfo.isThisPodInTheCase ;
122197 deviceInfo.isRightPodMicrophone = !primaryLeft ^ deviceInfo.isThisPodInTheCase ;
123198
124199 deviceInfo.lidOpenCounter = lidIndicator & 0x07 ; // Extract bits 0-2 (count)
125200 quint8 lidState = static_cast <quint8>((lidIndicator >> 3 ) & 0x01 ); // Extract bit 3 (lid state)
126201 if (deviceInfo.isThisPodInTheCase ) {
127- deviceInfo.lidState = static_cast <DeviceInfo ::LidState>(lidState);
202+ deviceInfo.lidState = static_cast <BleInfo ::LidState>(lidState);
128203 }
129204
130205 // Update timestamp
131206 deviceInfo.lastSeen = QDateTime::currentDateTime ();
132207
133- // Store device info in the map
134- devices[address] = deviceInfo;
135-
136- // Debug output
137- qDebug () << " Found device:" << deviceInfo.name
138- << " Left:" << (deviceInfo.leftPodBattery >= 0 ? QString (" %1%" ).arg (deviceInfo.leftPodBattery ) : " N/A" )
139- << " Right:" << (deviceInfo.rightPodBattery >= 0 ? QString (" %1%" ).arg (deviceInfo.rightPodBattery ) : " N/A" )
140- << " Case:" << (deviceInfo.caseBattery >= 0 ? QString (" %1%" ).arg (deviceInfo.caseBattery ) : " N/A" );
208+ emit deviceFound (deviceInfo); // Emit signal for device found
141209 }
142210 }
143211}
144212
145213void BleManager::onScanFinished ()
146214{
147- qDebug () << " Scan finished." ;
148215 if (discoveryAgent->isActive ())
149216 {
150217 discoveryAgent->start (QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
@@ -153,24 +220,6 @@ void BleManager::onScanFinished()
153220
154221void BleManager::onErrorOccurred (QBluetoothDeviceDiscoveryAgent::Error error)
155222{
156- qDebug () << " Error occurred:" << error;
223+ LOG_ERROR ( " BLE scan error occurred:" << error) ;
157224 stopScan ();
158225}
159-
160- void BleManager::pruneOldDevices ()
161- {
162- QDateTime now = QDateTime::currentDateTime ();
163- auto it = devices.begin ();
164- while (it != devices.end ())
165- {
166- if (it.value ().lastSeen .msecsTo (now) > DEVICE_TIMEOUT_MS)
167- {
168- qDebug () << " Removing old device:" << it.value ().name << " at" << it.key ();
169- it = devices.erase (it); // Remove device if not seen recently
170- }
171- else
172- {
173- ++it;
174- }
175- }
176- }
0 commit comments