Skip to content

Commit

Permalink
Initial Apple AirPods / Beats Buds decoder (#569)
Browse files Browse the repository at this point in the history
* Initial Apple AirPods / Beats Buds decoder

Co-authored-by: jum0n <7494877+jum0n@users.noreply.github.com>
  • Loading branch information
DigiH and jum0n authored Nov 2, 2024
1 parent d6fdd20 commit 7c12396
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 6 deletions.
21 changes: 21 additions & 0 deletions docs/devices/AppleAirPods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Apple AirPods (Pro)

|Model Id|[APPLEAIRPODS](https://github.com/theengs/decoder/blob/development/src/devices/APPLEAIRPODS_json.h)|
|-|-|
|Brand|Apple|
|Model|AirPods (Pro)|
|Short Description|Various Apple AirPods (Pro) models|
|Communication|BLE broadcast|
|Frequency|2.4Ghz|
|Power Source|Rechargeable battery|
|Exchanged Data|model version, color, status, (left, right, case) battery level*, (left, right, case) charging state|
|Encrypted|No|
|Device Tracker|&#9989;|

Currently only usefully working with the [Theengs Gateway](https://gateway.theengs.io/use/use.html#details-options) **Identity Address** and **IRK** functionality, to be able to decrypt the randomly changing Bluetooth MAC address to the static identity MAC address.

Instructions on how to get the [Identity Address and IRK for an Apple Watch, iPhone, iPad or AirPods](https://gateway.theengs.io/use/use.html#getting-identity-resolving-key-irk-for-apple-watch-iphone-and-ipad)

\* battery levels are only reported in 10% steps by the BLE broadcasts.

Some model versions and statuses are not decoded correctly yet, due to the various models and generations. In such a case please report your device's MQTT message with the [PUBLISH_ADVDATA option](https://gateway.theengs.io/use/use.html#details-options) set to **true**.
21 changes: 21 additions & 0 deletions docs/devices/BeatsBuds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Beats Solo/Studio Buds

|Model Id|[APPLEAIRPODS](https://github.com/theengs/decoder/blob/development/src/devices/APPLEAIRPODS_json.h)|
|-|-|
|Brand|Beats|
|Model|Solo/Studio Buds|
|Short Description|Various Beats Solo/Studio Buds models|
|Communication|BLE broadcast|
|Frequency|2.4Ghz|
|Power Source|Rechargeable battery|
|Exchanged Data|model version, color, status, (left, right, case) battery level*, (left, right, case) charging state|
|Encrypted|No|
|Device Tracker|&#9989;|

Currently only usefully working with the [Theengs Gateway](https://gateway.theengs.io/use/use.html#details-options) **Identity Address** and **IRK** functionality, to be able to decrypt the randomly changing Bluetooth MAC address to the static identity MAC address.

Instructions on how to get the [Identity Address and IRK for an Apple Watch, iPhone, iPad or Beats Buds](https://gateway.theengs.io/use/use.html#getting-identity-resolving-key-irk-for-apple-watch-iphone-and-ipad)

\* battery levels are only reported in 10% steps by the BLE broadcasts.

Some model versions, colors and statuses are not decoded correctly yet, due to the various models, colors and generations. In such a case please report your device's MQTT message with the [PUBLISH_ADVDATA option](https://gateway.theengs.io/use/use.html#details-options) set to **true**.
9 changes: 6 additions & 3 deletions docs/participate/adding-decoders.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ Each device should also have an encoded **tag** property to, at the minimum, def
</thead>
<tbody>
<tr>
<td rowspan=21>Byte[0]</td>
<td rowspan=21>Device Type > "type":</td>
<td rowspan=22>Byte[0]</td>
<td rowspan=22>Device Type > "type":</td>
<td rowspan=1>0 - Reserved</td>
</tr>
<tr>
Expand Down Expand Up @@ -109,7 +109,10 @@ Each device should also have an encoded **tag** property to, at the minimum, def
<td rowspan=1>17 - BTN - button</td>
</tr>
<tr>
<td rowspan=1>18-253 - Reserved</td>
<td rowspan=1>18 - AUDIO - audio devices</td>
</tr>
<tr>
<td rowspan=1>19-253 - Reserved</td>
</tr>
<tr>
<td rowspan=1>254 - RMAC - known random MAC address devices</td>
Expand Down
3 changes: 3 additions & 0 deletions src/decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,9 @@ int TheengsDecoder::decodeBLEJson(JsonObject& jsondata) {
case 17:
doc["type"] = "BTN"; // Button
break;
case 18:
doc["type"] = "AUDIO"; // Button
break;
case 254:
doc["type"] = "RMAC"; // random MAC address devices
break;
Expand Down
1 change: 1 addition & 0 deletions src/decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class TheengsDecoder {
GTAG,
BOSCHNYON,
JAALEE,
APPLEAIRPODS,
APPLEWATCH,
APPLEDEVICE,
IBEACON,
Expand Down
2 changes: 2 additions & 0 deletions src/devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
#include "devices/BM6_json.h"
#include "devices/TILT_json.h"
#include "devices/JAALEE_json.h"
#include "devices/APPLEAIRPODS_json.h"
#include "devices/APPLEWATCH_json.h"
#include "devices/APPLEDEVICE_json.h"
#include "devices/iBeacon_json.h"
Expand Down Expand Up @@ -227,6 +228,7 @@ const char* _devices[][2] = {
{_tracker_json_GTAG, _tracker_json_props},
{_tracker_json_NYON, _tracker_json_props},
{_JAALEE_json, _JAALEE_json_props},
{_APPLEAIRPODS_json, _APPLEAIRPODS_json_props},
{_APPLEWATCH_json, _APPLEWATCH_json_props},
{_APPLEDEVICE_json, _APPLEDEVICE_json_props},
{_ibeacon_json, _ibeacon_json_props},
Expand Down
116 changes: 116 additions & 0 deletions src/devices/APPLEAIRPODS_json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const char* _APPLEAIRPODS_json = "{\"brand\":\"Apple/Beats\",\"model\":\"AirPods (Pro)/Solo|Studio Buds\",\"model_id\":\"APPLEAIRPODS\",\"tag\":\"1218\",\"condition\":[\"manufacturerdata\",\"index\",8,\"01\",\"&\",\"manufacturerdata\",\"=\",58,\"index\",0,\"4c0007\"],\"properties\":{\"version\":{\"decoder\":[\"static_value\",\"thus far unknown - please report your model version\"]},\"_version\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",10,4],\"lookup\":[\"0220\",\"AirPods 1st gen.\",\"0e20\",\"AirPods Pro 1st gen.\",\"1420\",\"AirPods Pro 2nd gen.\",\"0320\",\"Powerbeats3\",\"0520\",\"BeatsX\",\"0620\",\"Beats Solo3\"]},\"color\":{\"decoder\":[\"static_value\",\"thus far unknown - please report your color\"]},\"_color\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",22,2],\"lookup\":[\"00\",\"white\",\"01\",\"black\",\"02\",\"red\",\"03\",\"blue\",\"04\",\"pink\",\"05\",\"gray\",\"06\",\"silver\",\"07\",\"gold\",\"08\",\"rose gold\",\"09\",\"space gray\",\"0a\",\"dark blue\",\"0b\",\"light blue\",\"0c\",\"yellow\"]},\"status\":{\"decoder\":[\"static_value\",\"thus far unknown - please report your status\"]},\"_status\":{\"decoder\":[\"string_from_hex_data\",\"manufacturerdata\",15,1],\"lookup\":[\"5\",\"in case\",\"1\",\"out of case\",\"3\",\"in ears\",\"b\",\"in ears\"]},\"batt_r\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",16,1],\"post_proc\":[\"*\",10,\"max\",100]},\"batt_l\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",17,1],\"post_proc\":[\"*\",10,\"max\",100]},\"batt_case\":{\"decoder\":[\"value_from_hex_data\",\"manufacturerdata\",19,1],\"post_proc\":[\"*\",10,\"max\",100]},\"charging_r\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",18,1,false,true]},\"charging_l\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",18,0,false,true]},\"charging_case\":{\"decoder\":[\"bit_static_value\",\"manufacturerdata\",18,2,false,true]}}}";
/*R""""(
{
"brand":"Apple/Beats",
"model":"AirPods (Pro)/Solo|Studio Buds",
"model_id":"APPLEAIRPODS",
"tag":"1218",
"condition":["manufacturerdata", "index", 8, "01", "&", "manufacturerdata","=", 58, "index", 0, "4c0007"],
"properties":{
"version":{
"decoder":["static_value", "thus far unknown - please report your model version"]
},
"_version":{
"decoder":["string_from_hex_data", "manufacturerdata", 10, 4],
"lookup":["0220", "AirPods 1st gen.",
"0e20", "AirPods Pro 1st gen.",
"1420", "AirPods Pro 2nd gen.",
"0320", "Powerbeats3",
"0520", "BeatsX",
"0620", "Beats Solo3"]
},
"color":{
"decoder":["static_value", "thus far unknown - please report your color"]
},
"_color":{
"decoder":["string_from_hex_data", "manufacturerdata", 22, 2],
"lookup":["00", "white",
"01", "black",
"02", "red",
"03", "blue",
"04", "pink",
"05", "gray",
"06", "silver",
"07", "gold",
"08", "rose gold",
"09", "space gray",
"0a", "dark blue",
"0b", "light blue",
"0c", "yellow"]
},
"status":{
"decoder":["static_value", "thus far unknown - please report your status"]
},
"_status":{
"decoder":["string_from_hex_data", "manufacturerdata", 15, 1],
"lookup":["5", "in case",
"1", "out of case",
"3", "in ears",
"b", "in ears"]
},
"batt_r":{
"decoder":["value_from_hex_data", "manufacturerdata", 16, 1],
"post_proc":["*", 10, "max", 100]
},
"batt_l":{
"decoder":["value_from_hex_data", "manufacturerdata", 17, 1],
"post_proc":["*", 10, "max", 100]
},
"batt_case":{
"decoder":["value_from_hex_data", "manufacturerdata", 19, 1],
"post_proc":["*", 10, "max", 100]
},
"charging_r":{
"decoder":["bit_static_value", "manufacturerdata", 18, 1, false, true]
},
"charging_l":{
"decoder":["bit_static_value", "manufacturerdata", 18, 0, false, true]
},
"charging_case":{
"decoder":["bit_static_value", "manufacturerdata", 18, 2, false, true]
}
}
})"""";*/

const char* _APPLEAIRPODS_json_props = "{\"properties\":{\"version\":{\"unit\":\"string\",\"name\":\"model version\"},\"color\":{\"unit\":\"string\",\"name\":\"color\"},\"status\":{\"unit\":\"string\",\"name\":\"status\"},\"batt_r\":{\"unit\":\"%\",\"name\":\"batt\"},\"batt_l\":{\"unit\":\"%\",\"name\":\"batt\"},\"batt_case\":{\"unit\":\"%\",\"name\":\"batt\"},\"charging_r\":{\"unit\":\"status\",\"name\":\"battery_charging\"},\"charging_l\":{\"unit\":\"status\",\"name\":\"battery_charging\"},\"charging_case\":{\"unit\":\"status\",\"name\":\"battery_charging\"}}}";
/*R""""(
{
"properties":{
"version":{
"unit":"string",
"name":"model version"
},
"color":{
"unit":"string",
"name":"color"
},
"status":{
"unit":"string",
"name":"status"
},
"batt_r":{
"unit":"%",
"name":"batt"
},
"batt_l":{
"unit":"%",
"name":"batt"
},
"batt_case":{
"unit":"%",
"name":"batt"
},
"charging_r":{
"unit":"status",
"name":"battery_charging"
},
"charging_l":{
"unit":"status",
"name":"battery_charging"
},
"charging_case":{
"unit":"status",
"name":"battery_charging"
}
}
})"""";*/
Loading

0 comments on commit 7c12396

Please sign in to comment.