├── CNAME ├── LICENSE.md ├── README.md ├── _config.yml ├── bleAdvReader.py ├── hc2.png └── main.py /CNAME: -------------------------------------------------------------------------------- 1 | bleadvreader.hc2.fr -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A BLE tool library to decode some advertising data in object mode (used on ESP32 and [Pycom](http://www.pycom.io) modules) 2 | 3 | ![HC²](hc2.png "HC²") 4 | 5 | #### Very easy to integrate and very light with one file only : 6 | - `"bleAdvReader.py"` 7 | 8 | #### BLEAdvReader features : 9 | - Access to all elements data in BLE advertising packets 10 | - Checking data format errors in lenght and structure 11 | - Getting objects (class) to read some specialized data 12 | - Works on data at different levels 13 | - Reading UUIDs in the good format (16bits, 32bits, 128bits) 14 | - Access to manufacturer data for custom ID of companies 15 | - Decoding and reading **Apple iBeacon** format 16 | - Decoding and reading **Google EddyStone** format : 17 | - EddyStone UID 18 | - EddyStone URL *(Beacon URI)* 19 | - EddyStone TML Unencrypted 20 | - EddyStone TML Encrypted 21 | - EddyStone EID 22 | - Estimating device proximity with 3 equations : 23 | - LogTX (path loss exponent variable) 24 | - OldBconTX (on old iPhone/Android) 25 | - NewBconTX (on recent iPhone/Android) 26 | 27 | ### Example of using *BLEAdvReader* easily : 28 | ```python 29 | # Getting "advertisingData" before via the BLE 30 | 31 | from bleAdvReader import BLEAdvReader 32 | 33 | advReader = BLEAdvReader(advertisingData) 34 | 35 | # Gets the service data part in the advertising packet, 36 | svcData = advReader.GetDataByDataType(BLEAdvReader.DATA_TYPE_SVC_DATA) 37 | 38 | # List all decoded and structured objects (class), 39 | for advElement in advReader.GetAllElements() : 40 | print(advElement) 41 | # Finds an iBeacon with classes instances comparison, 42 | if isinstance(advElement, BLEAdvReader.AppleIBeacon) : 43 | print('This is an iBeacon with UUID %s' % advElement.StrUUID) 44 | 45 | # Gets the same iBeacon more directly, 46 | iBeaconElement = advReader.GetElementByClass(BLEAdvReader.AppleIBeacon) 47 | if iBeaconElement : 48 | print('iBeacon found!') 49 | ``` 50 | 51 | ### Using *BLEAdvReader* main class : 52 | 53 | | Name | Function | 54 | | - | - | 55 | | Constructor | `advReader = BLEAdvReader(advertisingData)` | 56 | | GetDataByDataType | `data = advReader.GetDataByDataType(dataType)` | 57 | | GetAllElements | `advElements = advReader.GetAllElements()` | 58 | | GetElementByClass | `advElement = advReader.GetElementByClass(elementType)` | 59 | 60 | | Element type (class) | 61 | | - | 62 | | BLEAdvReader.Flags | 63 | | BLEAdvReader.AdoptedService16bits | 64 | | BLEAdvReader.AdoptedService32bits | 65 | | BLEAdvReader.AdoptedService128bits | 66 | | BLEAdvReader.ShortName | 67 | | BLEAdvReader.CompleteName | 68 | | BLEAdvReader.TXPowerLevel | 69 | | BLEAdvReader.ServiceData | 70 | | BLEAdvReader.ManufacturerData | 71 | | BLEAdvReader.AppleService | 72 | | BLEAdvReader.AppleIBeacon | 73 | | BLEAdvReader.EddyStoneUID | 74 | | BLEAdvReader.EddyStoneURL | 75 | | BLEAdvReader.EddyStoneTLMUnencrypted | 76 | | BLEAdvReader.EddyStoneTLMEncrypted | 77 | | BLEAdvReader.EddyStoneEID | 78 | 79 | | Data type | 80 | | - | 81 | | BLEAdvReader.DATA_TYPE_FLAGS | 82 | | BLEAdvReader.DATA_TYPE_INCOMP_16BITS_UUIDS | 83 | | BLEAdvReader.DATA_TYPE_COMP_16BITS_UUIDS | 84 | | BLEAdvReader.DATA_TYPE_INCOMP_32BITS_UUIDS | 85 | | BLEAdvReader.DATA_TYPE_COMP_32BITS_UUIDS | 86 | | BLEAdvReader.DATA_TYPE_INCOMP_128BITS_UUIDS | 87 | | BLEAdvReader.DATA_TYPE_COMP_128BITS_UUIDS | 88 | | BLEAdvReader.DATA_TYPE_SHORT_NAME | 89 | | BLEAdvReader.DATA_TYPE_COMPLETE_NAME | 90 | | BLEAdvReader.DATA_TYPE_TX_POWER_LEVEL | 91 | | BLEAdvReader.DATA_TYPE_DEVICE_CLASS | 92 | | BLEAdvReader.DATA_TYPE_SMP_PAIR_HASH_C | 93 | | BLEAdvReader.DATA_TYPE_SMP_PAIR_HASH_C192 | 94 | | BLEAdvReader.DATA_TYPE_SMP_PAIR_RAND_R | 95 | | BLEAdvReader.DATA_TYPE_SMP_PAIR_RAND_R192 | 96 | | BLEAdvReader.DATA_TYPE_DEVICE_ID | 97 | | BLEAdvReader.DATA_TYPE_SECU_MNGR_TK_VAL | 98 | | BLEAdvReader.DATA_TYPE_SECU_MNGR_OOB_FLAGS | 99 | | BLEAdvReader.DATA_TYPE_SLAVE_CONN_INT_RNG | 100 | | BLEAdvReader.DATA_TYPE_16BITS_SVC_SOL_UUIDS | 101 | | BLEAdvReader.DATA_TYPE_128BITS_SVC_SOL_UUIDS | 102 | | BLEAdvReader.DATA_TYPE_SVC_DATA | 103 | | BLEAdvReader.DATA_TYPE_SVC_DATA_16BITS_UUID | 104 | | BLEAdvReader.DATA_TYPE_PUB_TARGET_ADDR | 105 | | BLEAdvReader.DATA_TYPE_RAND_TARGET_ADDR | 106 | | BLEAdvReader.DATA_TYPE_APPEARANCE | 107 | | BLEAdvReader.DATA_TYPE_ADV_INT | 108 | | BLEAdvReader.DATA_TYPE_LE_BLT_DEVICE_ADDR | 109 | | BLEAdvReader.DATA_TYPE_LE_ROLE | 110 | | BLEAdvReader.DATA_TYPE_SMP_PAIR_HASH_C256 | 111 | | BLEAdvReader.DATA_TYPE_SMP_PAIR_RAND_R256 | 112 | | BLEAdvReader.DATA_TYPE_32BITS_SVC_SOL_UUIDS | 113 | | BLEAdvReader.DATA_TYPE_SVC_DATA_32BITS_UUID | 114 | | BLEAdvReader.DATA_TYPE_SVC_DATA_128BITS_UUID | 115 | | BLEAdvReader.DATA_TYPE_LE_SECU_CONN_RAND_VAL | 116 | | BLEAdvReader.DATA_TYPE_URI | 117 | | BLEAdvReader.DATA_TYPE_INDOOR_POS | 118 | | BLEAdvReader.DATA_TYPE_TRANS_DISCOV_DATA | 119 | | BLEAdvReader.DATA_TYPE_LE_SUPPORT_FEAT | 120 | | BLEAdvReader.DATA_TYPE_CHAN_MAP_UPD_INDIC | 121 | | BLEAdvReader.DATA_TYPE_PB_ADV | 122 | | BLEAdvReader.DATA_TYPE_MESH_MSG | 123 | | BLEAdvReader.DATA_TYPE_MESH_BEACON | 124 | | BLEAdvReader.DATA_TYPE_3D_INFO_DATA | 125 | | BLEAdvReader.DATA_TYPE_MANUFACTURER_DATA | 126 | 127 | ### Using *BLEAdvReader.Flags* class : 128 | 129 | | Name | Property | Type | 130 | | - | - | - | 131 | | LE_LIMITED_DISC_MODE | `flags.LE_LIMITED_DISC_MODE` | bool | 132 | | LE_GENERAL_DISC_MODE | `flags.LE_GENERAL_DISC_MODE` | bool | 133 | | BR_EDR_NOT_SUPPORTED | `flags.BR_EDR_NOT_SUPPORTED` | bool | 134 | | LE_BR_EDR_CONTROLLER | `flags.LE_BR_EDR_CONTROLLER` | bool | 135 | | LE_BR_EDR_HOST | `flags.LE_BR_EDR_HOST` | bool | 136 | | LE_ONLY_LIMITED_DISC_MODE | `flags.LE_ONLY_LIMITED_DISC_MODE` | bool | 137 | | LE_ONLY_GENERAL_DISC_MODE | `flags.LE_ONLY_GENERAL_DISC_MODE` | bool | 138 | 139 | ### Using *BLEAdvReader.AdoptedService16bits* class : 140 | 141 | | Name | Property | Type | 142 | | - | - | - | 143 | | UUID | `adoptedSvc.UUID` | int | 144 | | StrUUID | `adoptedSvc.StrUUID` | string | 145 | 146 | ### Using *BLEAdvReader.AdoptedService32bits* class : 147 | 148 | | Name | Property | Type | 149 | | - | - | - | 150 | | UUID | `adoptedSvc.UUID` | int | 151 | | StrUUID | `adoptedSvc.StrUUID` | string | 152 | 153 | ### Using *BLEAdvReader.AdoptedService128bits* class : 154 | 155 | | Name | Property | Type | 156 | | - | - | - | 157 | | UUID | `adoptedSvc.UUID` | bytes | 158 | | StrUUID | `adoptedSvc.StrUUID` | string | 159 | 160 | ### Using *BLEAdvReader.ShortName* class : 161 | Directly usable as string 162 | 163 | ### Using *BLEAdvReader.CompleteName* class : 164 | Directly usable as string 165 | 166 | ### Using *BLEAdvReader.TXPowerLevel* class : 167 | 168 | | Name | Property | Type | 169 | | - | - | - | 170 | | DBM | `txPower.DBM` | int | 171 | 172 | | Name | Function | 173 | | - | - | 174 | | GetProximityByLogTX | `meters = txPowerLvl.GetProximityByLogTX(rssi, n_PathLossExp=2)` | 175 | | GetProximityByOldBconTX | `meters = txPowerLvl.GetProximityByOldBconTX(rssi)` | 176 | | GetProximityByNewBconTX | `meters = txPowerLvl.GetProximityByNewBconTX(rssi)` | 177 | 178 | ### Using *BLEAdvReader.ServiceData* class : 179 | 180 | | Name | Property | Type | 181 | | - | - | - | 182 | | UUID | `svcData.UUID` | int | 183 | | StrUUID | `svcData.StrUUID` | string | 184 | | Data | `svcData.Data` | bytes | 185 | 186 | ### Using *BLEAdvReader.ManufacturerData* class : 187 | 188 | | Name | Property | Type | 189 | | - | - | - | 190 | | CompanyID | `mfacturerData.CompanyID` | int | 191 | | StrCompanyID | `mfacturerData.StrCompanyID` | string | 192 | | Data | `mfacturerData.Data` | bytes | 193 | 194 | ### Using *BLEAdvReader.AppleService* class : 195 | 196 | | Name | Property | Type | 197 | | - | - | - | 198 | | TypeName | `appleSvc.TypeName` | string | 199 | | Data | `appleSvc.Data` | bytes | 200 | 201 | | TypeName value | 202 | | - | 203 | | Empty *(unknown type)* | 204 | | `"AirDrop"` | 205 | | `"AirPods"` | 206 | | `"AirPlay Destination"` | 207 | | `"AirPlay Source"` | 208 | | `"HandOff"` | 209 | | `"Nearby"` | 210 | 211 | ### Using *BLEAdvReader.AppleIBeacon* class : 212 | 213 | | Name | Property | Type | 214 | | - | - | - | 215 | | UUID | `iBeacon.UUID` | bytes | 216 | | StrUUID | `iBeacon.StrUUID` | string | 217 | | Major | `iBeacon.Major` | int | 218 | | Minor | `iBeacon.Minor` | int | 219 | | TxPower | `iBeacon.TxPower` | int | 220 | 221 | | Name | Function | 222 | | - | - | 223 | | GetProximityByLogTX | `meters = iBeacon.GetProximityByLogTX(rssi, n_PathLossExp=2)` | 224 | | GetProximityByOldBconTX | `meters = iBeacon.GetProximityByOldBconTX(rssi)` | 225 | | GetProximityByNewBconTX | `meters = iBeacon.GetProximityByNewBconTX(rssi)` | 226 | 227 | ### Using *BLEAdvReader.EddyStoneUID* class : 228 | 229 | | Name | Property | Type | 230 | | - | - | - | 231 | | TxPower | `beaconEddyStone.TxPower` | int | 232 | | Namespace | `beaconEddyStone.Namespace` | bytes | 233 | | Instance | `beaconEddyStone.Instance` | bytes | 234 | 235 | | Name | Function | 236 | | - | - | 237 | | GetProximityByLogTX | `meters = beaconEddyStone.GetProximityByLogTX(rssi, n_PathLossExp=2)` | 238 | | GetProximityByOldBconTX | `meters = beaconEddyStone.GetProximityByOldBconTX(rssi)` | 239 | | GetProximityByNewBconTX | `meters = beaconEddyStone.GetProximityByNewBconTX(rssi)` | 240 | 241 | ### Using *BLEAdvReader.EddyStoneURL* class : 242 | 243 | | Name | Property | Type | 244 | | - | - | - | 245 | | TxPower | `beaconEddyStone.TxPower` | int | 246 | | URL | `beaconEddyStone.URL` | string | 247 | 248 | | Name | Function | 249 | | - | - | 250 | | GetProximityByLogTX | `meters = beaconEddyStone.GetProximityByLogTX(rssi, n_PathLossExp=2)` | 251 | | GetProximityByOldBconTX | `meters = beaconEddyStone.GetProximityByOldBconTX(rssi)` | 252 | | GetProximityByNewBconTX | `meters = beaconEddyStone.GetProximityByNewBconTX(rssi)` | 253 | 254 | ### Using *BLEAdvReader.EddyStoneTLMUnencrypted* class : 255 | 256 | | Name | Property | Type | 257 | | - | - | - | 258 | | VBatt | `beaconEddyStone.VBatt` | int | 259 | | Temp | `beaconEddyStone.Temp` | int | 260 | | AdvCnt | `beaconEddyStone.AdvCnt` | int | 261 | | SecCnt | `beaconEddyStone.SecCnt` | int | 262 | 263 | ### Using *BLEAdvReader.EddyStoneTLMEncrypted* class : 264 | 265 | | Name | Property | Type | 266 | | - | - | - | 267 | | ETLM | `beaconEddyStone.ETLM` | bytes | 268 | | SALT | `beaconEddyStone.SALT` | int | 269 | | MIC | `beaconEddyStone.MIC` | int | 270 | 271 | ### Using *BLEAdvReader.EddyStoneEID* class : 272 | 273 | | Name | Property | Type | 274 | | - | - | - | 275 | | TxPower | `beaconEddyStone.TxPower` | int | 276 | | EncryptedID | `beaconEddyStone.EncryptedID` | bytes | 277 | 278 | | Name | Function | 279 | | - | - | 280 | | GetProximityByLogTX | `meters = beaconEddyStone.GetProximityByLogTX(rssi, n_PathLossExp=2)` | 281 | | GetProximityByOldBconTX | `meters = beaconEddyStone.GetProximityByOldBconTX(rssi)` | 282 | | GetProximityByNewBconTX | `meters = beaconEddyStone.GetProximityByNewBconTX(rssi)` | 283 | 284 | ### Using *BLEAdvReader.ProximityHelper* "static" class : 285 | 286 | | Name | Function | 287 | | - | - | 288 | | LogTX | `meters = BLEAdvReader.ProximityHelper.LogTX(rssi, rssiTX, n_PathLossExp=2)` | 289 | | OldBconTX | `meters = BLEAdvReader.ProximityHelper.OldBconTX(rssi, rssiTX)` | 290 | | NewBconTX | `meters = BLEAdvReader.ProximityHelper.NewBconTX(rssi, rssiTX)` | 291 | 292 | 293 | 294 | ### By JC`zic for [HC²](https://www.hc2.fr) ;') 295 | 296 | *Keep it simple, stupid* :+1: 297 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight -------------------------------------------------------------------------------- /bleAdvReader.py: -------------------------------------------------------------------------------- 1 | """ 2 | The MIT License (MIT) 3 | Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) 4 | """ 5 | 6 | 7 | 8 | from ustruct import pack, unpack 9 | from ubinascii import hexlify 10 | 11 | class BLEAdvReader : 12 | 13 | # ============================================================================ 14 | # ===( Constants )============================================================ 15 | # ============================================================================ 16 | 17 | DATA_TYPE_FLAGS = 0x01 18 | DATA_TYPE_INCOMP_16BITS_UUIDS = 0x02 19 | DATA_TYPE_COMP_16BITS_UUIDS = 0x03 20 | DATA_TYPE_INCOMP_32BITS_UUIDS = 0x04 21 | DATA_TYPE_COMP_32BITS_UUIDS = 0x05 22 | DATA_TYPE_INCOMP_128BITS_UUIDS = 0x06 23 | DATA_TYPE_COMP_128BITS_UUIDS = 0x07 24 | DATA_TYPE_SHORT_NAME = 0x08 25 | DATA_TYPE_COMPLETE_NAME = 0x09 26 | DATA_TYPE_TX_POWER_LEVEL = 0x0A 27 | DATA_TYPE_DEVICE_CLASS = 0x0B 28 | DATA_TYPE_SMP_PAIR_HASH_C = 0x0C 29 | DATA_TYPE_SMP_PAIR_HASH_C192 = 0x0D 30 | DATA_TYPE_SMP_PAIR_RAND_R = 0x0E 31 | DATA_TYPE_SMP_PAIR_RAND_R192 = 0x0F 32 | DATA_TYPE_DEVICE_ID = 0x10 33 | DATA_TYPE_SECU_MNGR_TK_VAL = 0x11 34 | DATA_TYPE_SECU_MNGR_OOB_FLAGS = 0x12 35 | DATA_TYPE_SLAVE_CONN_INT_RNG = 0x13 36 | DATA_TYPE_16BITS_SVC_SOL_UUIDS = 0x14 37 | DATA_TYPE_128BITS_SVC_SOL_UUIDS = 0x15 38 | DATA_TYPE_SVC_DATA = 0x16 39 | DATA_TYPE_SVC_DATA_16BITS_UUID = 0x17 40 | DATA_TYPE_PUB_TARGET_ADDR = 0x18 41 | DATA_TYPE_RAND_TARGET_ADDR = 0x19 42 | DATA_TYPE_APPEARANCE = 0x1A 43 | DATA_TYPE_ADV_INT = 0x1B 44 | DATA_TYPE_LE_BLT_DEVICE_ADDR = 0x1C 45 | DATA_TYPE_LE_ROLE = 0x1D 46 | DATA_TYPE_SMP_PAIR_HASH_C256 = 0x1E 47 | DATA_TYPE_SMP_PAIR_RAND_R256 = 0x1F 48 | DATA_TYPE_32BITS_SVC_SOL_UUIDS = 0x20 49 | DATA_TYPE_SVC_DATA_32BITS_UUID = 0x21 50 | DATA_TYPE_SVC_DATA_128BITS_UUID = 0x22 51 | DATA_TYPE_LE_SECU_CONN_RAND_VAL = 0x23 52 | DATA_TYPE_URI = 0x24 53 | DATA_TYPE_INDOOR_POS = 0x25 54 | DATA_TYPE_TRANS_DISCOV_DATA = 0x26 55 | DATA_TYPE_LE_SUPPORT_FEAT = 0x27 56 | DATA_TYPE_CHAN_MAP_UPD_INDIC = 0x28 57 | DATA_TYPE_PB_ADV = 0x29 58 | DATA_TYPE_MESH_MSG = 0x2A 59 | DATA_TYPE_MESH_BEACON = 0x2B 60 | DATA_TYPE_3D_INFO_DATA = 0x3D 61 | DATA_TYPE_MANUFACTURER_DATA = 0xFF 62 | 63 | MEMBER_UUID_GOOGLE_EDDYSTONE = 0xFEAA 64 | 65 | COMPANY_ID_APPLE = 0x004C 66 | 67 | APPLE_TYPE_IBEACON = 0x02 68 | APPLE_TYPE_AIRDROP = 0x05 69 | APPLE_TYPE_AIRPODS = 0x07 70 | APPLE_TYPE_AIRPLAY_DEST = 0x09 71 | APPLE_TYPE_AIRPLAY_SRC = 0x0A 72 | APPLE_TYPE_HANDOFF = 0x0C 73 | APPLE_TYPE_NEARBY = 0x10 74 | 75 | # ============================================================================ 76 | # ===( Class InvalidAdvData )================================================= 77 | # ============================================================================ 78 | 79 | class InvalidAdvData(Exception) : 80 | pass 81 | 82 | # ============================================================================ 83 | # ===( Constructor )========================================================== 84 | # ============================================================================ 85 | 86 | def __init__(self, advertisingData) : 87 | self._advData = dict() 88 | self._advObj = [ ] 89 | self._advDataProcess(advertisingData) 90 | self._advDataElementsProcess() 91 | self._advKnownElementsProcess() 92 | 93 | # ============================================================================ 94 | # ===( Functions )============================================================ 95 | # ============================================================================ 96 | 97 | @staticmethod 98 | def _hex(data) : 99 | if data : 100 | return hexlify(data).decode().upper() 101 | return '' 102 | 103 | # ---------------------------------------------------------------------------- 104 | 105 | @staticmethod 106 | def _twosComp(val, bits) : 107 | if val < 2**bits : 108 | return val - int((val << 1) & 2**bits) 109 | raise ValueError('Value %s out of range of %s-bit value.' % (val, bits)) 110 | 111 | # ---------------------------------------------------------------------------- 112 | 113 | @staticmethod 114 | def _accum88(data16b) : 115 | if isinstance(data16b, bytes) and len(data16b) == 2 : 116 | return BLEAdvReader._twosComp(data16b[0], 8) + \ 117 | BLEAdvReader._twosComp(data16b[1], 16) / 256 118 | raise ValueError('%s is not a 16 bits data value.' % data16b) 119 | 120 | # ---------------------------------------------------------------------------- 121 | 122 | @staticmethod 123 | def _128bitsUUID(uuidBytes) : 124 | if uuidBytes and len(uuidBytes) == 16 : 125 | s = hexlify(uuidBytes).decode() 126 | return s[:8] + '-' + s[8:12] + '-' + s[12:16] + '-' + s[16:20] + '-' + s[20:] 127 | return '' 128 | 129 | # ---------------------------------------------------------------------------- 130 | 131 | @staticmethod 132 | def _decodeURIBeacon(data) : 133 | schemes = { 134 | 0x00 : 'http://www.', 0x01 : 'https://www.', 135 | 0x02 : 'http://', 0x03 : 'https://' 136 | } 137 | expansions = { 138 | 0x00 : '.com/', 0x01 : '.org/', 0x02 : '.edu/', 0x03 : '.net/', 139 | 0x04 : '.info/', 0x05 : '.biz/', 0x06 : '.gov/', 0x07 : '.com', 140 | 0x08 : '.org', 0x09 : '.edu', 0x0A : '.net', 141 | 0x0B : '.info', 0x0C : '.biz', 0x0D : '.gov' 142 | } 143 | try : 144 | url = schemes[data[0]] 145 | for b in data[1:] : 146 | url += expansions[b] if b in expansions else chr(b) 147 | return url 148 | except : 149 | return '' 150 | 151 | # ---------------------------------------------------------------------------- 152 | 153 | def _advDataProcess(self, advData) : 154 | if advData : 155 | advDataLen = len(advData) 156 | idx = 0 157 | while idx < advDataLen : 158 | dataLen = advData[idx] 159 | idx += 1 160 | if dataLen > 0 : 161 | idxEnd = idx + dataLen 162 | if idxEnd <= advDataLen : 163 | dataType = advData[idx] 164 | data = advData[idx+1:idxEnd] 165 | self._advData[dataType] = data 166 | else : 167 | raise self.InvalidAdvData('Data element invalid size') 168 | idx = idxEnd 169 | 170 | # ---------------------------------------------------------------------------- 171 | 172 | def _advDataElementsProcess(self) : 173 | if not self._advData : 174 | raise self.InvalidAdvData('No advertising data element') 175 | for dataType in self._advData : 176 | data = self._advData[dataType] 177 | advObj = None 178 | if dataType == self.DATA_TYPE_FLAGS : 179 | try : 180 | advObj = self.Flags(ord(data)) 181 | except : 182 | raise self.InvalidAdvData('Invalid flags data element') 183 | elif dataType == self.DATA_TYPE_COMP_16BITS_UUIDS : 184 | try : 185 | advObj = self.AdoptedService16bits(unpack('H', data[16:18])[0], 265 | unpack('>H', data[18:20])[0], 266 | data[20] - 256 ) 267 | elif appleType == self.APPLE_TYPE_AIRDROP : 268 | return self.AppleService('AirDrop', data) 269 | elif appleType == self.APPLE_TYPE_AIRPODS : 270 | return self.AppleService('AirPods', data) 271 | elif appleType == self.APPLE_TYPE_AIRPLAY_DEST : 272 | return self.AppleService('AirPlay Destination', data) 273 | elif appleType == self.APPLE_TYPE_AIRPLAY_SRC : 274 | return self.AppleService('AirPlay Source', data) 275 | elif appleType == self.APPLE_TYPE_HANDOFF : 276 | return self.AppleService('HandOff', data) 277 | elif appleType == self.APPLE_TYPE_NEARBY : 278 | return self.AppleService('Nearby', data) 279 | return self.AppleService() 280 | 281 | # ---------------------------------------------------------------------------- 282 | 283 | def _getAdvObjForGoogleEddyStoneData(self, data) : 284 | frameType = data[0] 285 | if frameType == 0x00 : 286 | txPower = unpack('H', data[2:4])[0] 298 | temp = BLEAdvReader._accum88(data[4:6]) 299 | advCnt = unpack('>I', data[6:10])[0] 300 | secCnt = unpack('>I', data[10:14])[0] 301 | return self.EddyStoneTLMUnencrypted(vbatt, temp, advCnt, secCnt) 302 | elif version == 0x01 : 303 | etlm = data[2:14] 304 | salt = unpack('>H', data[14:16])[0] 305 | mic = unpack('>H', data[16:18])[0] 306 | return self.EddyStoneTLMEncrypted(etlm, salt, mic) 307 | elif frameType == 0x30 : 308 | txPower = unpack('