2021-03-22 14:11:42 +01:00
|
|
|
#include "BleFingerprint.h"
|
2021-09-30 18:48:50 +02:00
|
|
|
#include "BleFingerprintCollection.h"
|
|
|
|
#include "rssi.h"
|
2021-10-05 13:54:41 +02:00
|
|
|
#include "strings.h"
|
2021-04-07 02:13:13 +02:00
|
|
|
#include "util.h"
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
class ClientCallbacks : public BLEClientCallbacks
|
2022-02-13 18:58:17 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
bool onConnParamsUpdateRequest(NimBLEClient *pClient, const ble_gap_upd_params *params)
|
2022-02-13 18:58:17 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
static ClientCallbacks clientCB;
|
2022-02-13 18:58:17 +01:00
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
bool BleFingerprint::shouldHide(const String& s)
|
2022-02-13 18:58:17 +01:00
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
if (BleFingerprintCollection::include.length() > 0 && !prefixExists(BleFingerprintCollection::include, s)) return true;
|
|
|
|
return (BleFingerprintCollection::exclude.length() > 0 && prefixExists(BleFingerprintCollection::exclude, s));
|
2022-02-13 18:58:17 +01:00
|
|
|
}
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
bool BleFingerprint::setId(const String& newId, short newIdType, const String& newName)
|
2022-02-13 18:58:17 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
if (newIdType < idType && idType > 0) return false;
|
2022-02-13 18:58:17 +01:00
|
|
|
|
|
|
|
hidden = shouldHide(newId);
|
2022-03-14 02:54:50 +01:00
|
|
|
ignore = newIdType < 0;
|
2022-02-13 18:58:17 +01:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
if (!allowQuery && !ignore)
|
2022-02-13 18:58:17 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
if (!BleFingerprintCollection::query.isEmpty() && prefixExists(BleFingerprintCollection::query, newId))
|
2022-02-13 18:58:17 +01:00
|
|
|
{
|
|
|
|
allowQuery = true;
|
|
|
|
qryAttempts = 0;
|
|
|
|
if (rssi < -60)
|
|
|
|
{
|
|
|
|
qryDelayMillis = 5000;
|
|
|
|
lastQryMillis = millis();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
countable = !ignore && !BleFingerprintCollection::countIds.isEmpty() && prefixExists(BleFingerprintCollection::countIds, newId);
|
2022-02-13 18:58:17 +01:00
|
|
|
id = newId;
|
|
|
|
idType = newIdType;
|
2022-03-06 17:48:04 +01:00
|
|
|
if (!newName.isEmpty()) name = newName;
|
|
|
|
return true;
|
2022-02-13 18:58:17 +01:00
|
|
|
}
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
int BleFingerprint::get1mRssi() const
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-30 18:48:50 +02:00
|
|
|
if (calRssi != NO_RSSI) return calRssi;
|
|
|
|
if (mdRssi != NO_RSSI) return mdRssi;
|
|
|
|
if (asRssi != NO_RSSI) return asRssi;
|
2022-03-06 17:48:04 +01:00
|
|
|
return BleFingerprintCollection::refRssi + DEFAULT_TX;
|
2021-09-30 18:48:50 +02:00
|
|
|
}
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
BleFingerprint::BleFingerprint(const BleFingerprintCollection *parent, BLEAdvertisedDevice *advertisedDevice, float fcmin, float beta, float dcutoff) : oneEuro{OneEuroFilter<float, unsigned long>(1, fcmin, beta, dcutoff)}
|
2021-09-30 18:48:50 +02:00
|
|
|
{
|
|
|
|
firstSeenMillis = millis();
|
2022-02-20 14:06:49 +01:00
|
|
|
address = NimBLEAddress(advertisedDevice->getAddress());
|
2021-04-07 02:13:13 +02:00
|
|
|
newest = recent = oldest = rssi = advertisedDevice->getRSSI();
|
2022-02-20 22:03:03 +01:00
|
|
|
seenCount = 1;
|
2022-03-14 02:54:50 +01:00
|
|
|
|
|
|
|
auto mac = getMac();
|
|
|
|
if (!BleFingerprintCollection::knownMacs.isEmpty() && prefixExists(BleFingerprintCollection::knownMacs, mac))
|
|
|
|
setId("known:" + mac, ID_TYPE_KNOWN_MAC);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (advertisedDevice->getAddressType())
|
|
|
|
{
|
|
|
|
case BLE_ADDR_PUBLIC:
|
|
|
|
case BLE_ADDR_PUBLIC_ID:
|
|
|
|
setId(mac, ID_TYPE_PUBLIC_MAC);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
setId(mac, ID_TYPE_MAC);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-09-15 18:11:32 +02:00
|
|
|
}
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
void BleFingerprint::fingerprint(NimBLEAdvertisedDevice *advertisedDevice)
|
2021-09-15 18:11:32 +02:00
|
|
|
{
|
2021-03-22 14:11:42 +01:00
|
|
|
if (advertisedDevice->haveName())
|
2022-03-06 17:48:04 +01:00
|
|
|
setId(String("name:") + kebabify(advertisedDevice->getName()).c_str(), ID_TYPE_NAME, String(advertisedDevice->getName().c_str()));
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2022-02-13 18:58:17 +01:00
|
|
|
if (advertisedDevice->getAdvType() > 0)
|
|
|
|
connectable = true;
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
size_t serviceAdvCount = advertisedDevice->getServiceUUIDCount();
|
|
|
|
size_t serviceDataCount = advertisedDevice->getServiceDataCount();
|
2022-03-14 02:54:50 +01:00
|
|
|
bool haveTxPower = advertisedDevice->haveTXPower();
|
|
|
|
int8_t txPower = advertisedDevice->getTXPower();
|
2022-03-06 17:48:04 +01:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
if (serviceAdvCount > 0) fingerprintServiceAdvertisements(advertisedDevice, serviceAdvCount, haveTxPower, txPower);
|
|
|
|
if (serviceDataCount > 0) fingerprintServiceData(advertisedDevice, serviceDataCount, haveTxPower, txPower);
|
|
|
|
if (advertisedDevice->haveManufacturerData()) fingerprintManufactureData(advertisedDevice, haveTxPower, txPower);
|
2022-03-06 17:48:04 +01:00
|
|
|
}
|
2021-09-30 18:48:50 +02:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
void BleFingerprint::fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount, bool haveTxPower, int8_t txPower)
|
2022-03-06 17:48:04 +01:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < serviceAdvCount; i++)
|
|
|
|
{
|
|
|
|
auto uuid = advertisedDevice->getServiceUUID(i);
|
2021-09-30 18:48:50 +02:00
|
|
|
#ifdef VERBOSE
|
2022-03-06 17:48:04 +01:00
|
|
|
Serial.printf("Verbose | %-58sAD: %s\n", getId().c_str(), advertisedDevice->getServiceUUID(i).toString().c_str());
|
2021-09-30 18:48:50 +02:00
|
|
|
#endif
|
2022-03-06 17:48:04 +01:00
|
|
|
if (uuid == roomAssistantService)
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
asRssi = BleFingerprintCollection::refRssi + RM_ASST_TX;
|
|
|
|
if (!rmAsst)
|
|
|
|
{
|
|
|
|
rmAsst = true;
|
|
|
|
if (didQuery)
|
|
|
|
{
|
|
|
|
qryDelayMillis = 0;
|
|
|
|
qryAttempts = 0;
|
|
|
|
didQuery = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (uuid == tileUUID)
|
|
|
|
{
|
|
|
|
asRssi = BleFingerprintCollection::refRssi + TILE_TX;
|
2021-10-11 04:49:56 +02:00
|
|
|
setId("tile:" + getMac(), ID_TYPE_TILE);
|
2022-03-06 17:48:04 +01:00
|
|
|
return;
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (uuid == sonosUUID)
|
2021-09-16 01:19:50 +02:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2021-10-11 04:49:56 +02:00
|
|
|
setId("sonos:" + getMac(), ID_TYPE_SONOS);
|
2022-03-06 17:48:04 +01:00
|
|
|
return;
|
2021-09-16 01:19:50 +02:00
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (uuid == itagUUID)
|
2021-09-30 18:48:50 +02:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = BleFingerprintCollection::refRssi + (haveTxPower ? txPower : ITAG_TX);
|
2021-10-11 04:49:56 +02:00
|
|
|
setId("itag:" + getMac(), ID_TYPE_ITAG);
|
2022-03-06 17:48:04 +01:00
|
|
|
return;
|
2021-10-05 13:54:41 +02:00
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (uuid == trackrUUID)
|
2021-11-11 13:32:12 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2021-11-11 13:32:12 +01:00
|
|
|
setId("trackr:" + getMac(), ID_TYPE_TRACKR);
|
2022-03-06 17:48:04 +01:00
|
|
|
return;
|
2021-11-11 13:32:12 +01:00
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (uuid == vanmoofUUID)
|
2022-02-23 04:10:19 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-02-23 04:10:19 +01:00
|
|
|
setId("vanmoof:" + getMac(), ID_TYPE_VANMOOF);
|
2022-03-06 17:48:04 +01:00
|
|
|
return;
|
2022-02-23 04:10:19 +01:00
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (uuid == (meaterService))
|
2021-11-11 03:55:10 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2021-11-11 03:55:10 +01:00
|
|
|
setId("meater:" + getMac(), ID_TYPE_MEATER);
|
2022-03-06 17:48:04 +01:00
|
|
|
return;
|
2021-11-11 03:55:10 +01:00
|
|
|
}
|
2022-05-14 17:57:42 +02:00
|
|
|
else if (uuid == nutUUID)
|
|
|
|
{
|
|
|
|
asRssi = BleFingerprintCollection::refRssi + (haveTxPower ? txPower : NUT_TX);
|
|
|
|
setId("nut:" + getMac(), ID_TYPE_NUT);
|
|
|
|
return;
|
|
|
|
}
|
2021-11-11 03:55:10 +01:00
|
|
|
}
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
String fingerprint = "ad:";
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-03-06 17:48:04 +01:00
|
|
|
for (int i = 0; i < serviceAdvCount; i++)
|
|
|
|
{
|
|
|
|
std::string sid = advertisedDevice->getServiceUUID(i).toString();
|
|
|
|
fingerprint = fingerprint + sid.c_str();
|
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
if (haveTxPower) fingerprint = fingerprint + String(-txPower);
|
2022-03-06 17:48:04 +01:00
|
|
|
setId(fingerprint, ID_TYPE_AD);
|
|
|
|
}
|
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
void BleFingerprint::fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount, bool haveTxPower, int8_t txPower)
|
2022-03-06 17:48:04 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
|
|
|
String fingerprint = "sd:";
|
|
|
|
for (int i = 0; i < serviceDataCount; i++)
|
2021-11-11 03:55:10 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
BLEUUID uuid = advertisedDevice->getServiceDataUUID(i);
|
|
|
|
std::string strServiceData = advertisedDevice->getServiceData(i);
|
2021-11-11 03:55:10 +01:00
|
|
|
#ifdef VERBOSE
|
2022-03-14 02:54:50 +01:00
|
|
|
Serial.printf("Verbose | %-58sSD: %s/%s\n", getId().c_str(), uuid.toString().c_str(), hexStr(strServiceData).c_str());
|
2021-11-11 03:55:10 +01:00
|
|
|
#endif
|
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
if (uuid == exposureUUID)
|
|
|
|
{ // found COVID-19 exposure tracker
|
|
|
|
calRssi = BleFingerprintCollection::refRssi + EXPOSURE_TX;
|
|
|
|
setId("exp:" + String(strServiceData.length()), ID_TYPE_EXPOSURE);
|
|
|
|
disc = hexStr(strServiceData).c_str();
|
|
|
|
}
|
2022-03-31 22:59:37 +02:00
|
|
|
else if (uuid == smartTagUUID && strServiceData.length() == 20)
|
2022-03-14 02:54:50 +01:00
|
|
|
{ // found Samsung smart tag
|
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-03-31 22:59:37 +02:00
|
|
|
setId(String("smarttag:") + hexStr(strServiceData).substr(8, 16).c_str(), ID_TYPE_SMARTTAG);
|
2022-03-14 02:54:50 +01:00
|
|
|
}
|
|
|
|
else if (uuid == miThermUUID)
|
|
|
|
{
|
|
|
|
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
|
|
|
if (strServiceData.length() == 15)
|
|
|
|
{ // custom format
|
|
|
|
auto serviceData = strServiceData.c_str();
|
|
|
|
temp = float(*(int16_t *)(serviceData + 6)) / 100.0f;
|
|
|
|
humidity = float(*(uint16_t *)(serviceData + 8)) / 100.0f;
|
|
|
|
mv = *(uint16_t *)(serviceData + 10);
|
|
|
|
battery = serviceData[12];
|
2021-11-11 03:55:10 +01:00
|
|
|
#ifdef VERBOSE
|
2022-03-14 02:54:50 +01:00
|
|
|
Serial.printf("Temp: %.1f°, Humidity: %.1f%%, mV: %hu, Battery: %hhu%%, flg: 0x%02hhx, cout: %hhu\n", temp, humidity, mv, battery, serviceData[14], serviceData[13]);
|
2021-11-11 03:55:10 +01:00
|
|
|
#endif
|
2022-03-14 02:54:50 +01:00
|
|
|
setId("miTherm:" + getMac(), ID_TYPE_MITHERM);
|
|
|
|
}
|
|
|
|
else if (strServiceData.length() == 13)
|
|
|
|
{ // format atc1441
|
|
|
|
auto serviceData = strServiceData.c_str();
|
|
|
|
int16_t x = (serviceData[6] << 8) | serviceData[7];
|
|
|
|
temp = float(x) / 10.0f;
|
|
|
|
humidity = serviceData[8];
|
|
|
|
mv = x = (serviceData[10] << 8) | serviceData[11];
|
|
|
|
battery = serviceData[9];
|
|
|
|
|
2021-11-11 03:55:10 +01:00
|
|
|
#ifdef VERBOSE
|
2022-03-14 02:54:50 +01:00
|
|
|
Serial.printf("Temp: %.1f°, Humidity: %.1f%%, mV: %hu, Battery: %hhu%%, cout: %hhu\n", temp, humidity, mv, battery, serviceData[12]);
|
2021-11-11 03:55:10 +01:00
|
|
|
#endif
|
2022-03-14 02:54:50 +01:00
|
|
|
setId("miTherm:" + getMac(), ID_TYPE_MITHERM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (uuid == eddystoneUUID && strServiceData.length() > 0)
|
|
|
|
{
|
|
|
|
if (strServiceData[0] == EDDYSTONE_URL_FRAME_TYPE && strServiceData.length() <= 18)
|
|
|
|
{
|
|
|
|
BLEEddystoneURL oBeacon = BLEEddystoneURL();
|
|
|
|
oBeacon.setData(strServiceData);
|
|
|
|
calRssi = EDDYSTONE_ADD_1M + oBeacon.getPower();
|
|
|
|
}
|
|
|
|
else if (strServiceData[0] == EDDYSTONE_TLM_FRAME_TYPE)
|
|
|
|
{
|
|
|
|
BLEEddystoneTLM oBeacon = BLEEddystoneTLM();
|
|
|
|
oBeacon.setData(strServiceData);
|
|
|
|
temp = oBeacon.getTemp();
|
|
|
|
mv = oBeacon.getVolt();
|
2021-09-15 18:11:32 +02:00
|
|
|
#ifdef VERBOSE
|
2022-03-14 02:54:50 +01:00
|
|
|
Serial.println(oBeacon.toString().c_str());
|
2021-09-15 18:11:32 +02:00
|
|
|
#endif
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
else if (strServiceData[0] == 0x00)
|
|
|
|
{
|
|
|
|
auto serviceData = strServiceData.c_str();
|
|
|
|
int8_t rss0m = *(int8_t *)(serviceData + 1);
|
|
|
|
calRssi = EDDYSTONE_ADD_1M + rss0m;
|
|
|
|
setId(Sprintf("eddy:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
|
|
strServiceData[2], strServiceData[3], strServiceData[4], strServiceData[5], strServiceData[6],
|
|
|
|
strServiceData[6], strServiceData[7], strServiceData[8], strServiceData[9], strServiceData[10],
|
|
|
|
strServiceData[11], strServiceData[12], strServiceData[13], strServiceData[14], strServiceData[15],
|
|
|
|
strServiceData[16], strServiceData[17]),
|
|
|
|
ID_TYPE_EBEACON);
|
|
|
|
}
|
2021-03-24 18:16:49 +01:00
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
fingerprint = fingerprint + uuid.toString().c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (haveTxPower)
|
|
|
|
fingerprint = fingerprint + String(-txPower);
|
|
|
|
setId(fingerprint, ID_TYPE_SD);
|
2022-03-06 17:48:04 +01:00
|
|
|
}
|
2021-09-18 19:05:07 +02:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice, bool haveTxPower, int8_t txPower)
|
2022-03-06 17:48:04 +01:00
|
|
|
{
|
|
|
|
std::string strManufacturerData = advertisedDevice->getManufacturerData();
|
2021-09-25 16:08:30 +02:00
|
|
|
#ifdef VERBOSE
|
2022-03-06 17:48:04 +01:00
|
|
|
Serial.printf("Verbose | %-58sMD: %s\n", getId().c_str(), hexStr(strManufacturerData).c_str());
|
2021-09-25 16:08:30 +02:00
|
|
|
#endif
|
2022-03-06 17:48:04 +01:00
|
|
|
if (strManufacturerData.length() >= 2)
|
|
|
|
{
|
|
|
|
String manuf = Sprintf("%02x%02x", strManufacturerData[1], strManufacturerData[0]);
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
if (manuf == "004c") // Apple
|
|
|
|
{
|
|
|
|
if (strManufacturerData.length() == 25 && strManufacturerData[2] == 0x02 && strManufacturerData[3] == 0x15)
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
BLEBeacon oBeacon = BLEBeacon();
|
|
|
|
oBeacon.setData(strManufacturerData);
|
2022-03-14 02:54:50 +01:00
|
|
|
setId(Sprintf("iBeacon:%s-%u-%u", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), ID_TYPE_IBEACON);
|
2022-03-06 17:48:04 +01:00
|
|
|
calRssi = oBeacon.getSignalPower();
|
|
|
|
}
|
|
|
|
else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10)
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
String pid = Sprintf("apple:%02x%02x:%zu", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
|
|
|
|
if (haveTxPower) pid += -txPower;
|
|
|
|
setId(pid, ID_TYPE_APPLE_NEARBY);
|
2022-03-06 17:48:04 +01:00
|
|
|
disc = hexStr(strManufacturerData.substr(4)).c_str();
|
|
|
|
mdRssi = BleFingerprintCollection::refRssi + APPLE_TX;
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
else if (strManufacturerData.length() >= 4)
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
String pid = Sprintf("apple:%02x%02x:%zu", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
|
|
|
|
if (haveTxPower) pid += -txPower;
|
|
|
|
setId(pid, ID_TYPE_MISC_APPLE);
|
2022-03-06 17:48:04 +01:00
|
|
|
mdRssi = BleFingerprintCollection::refRssi + APPLE_TX;
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (manuf == "05a7") // Sonos
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-03-06 17:48:04 +01:00
|
|
|
setId("sonos:" + getMac(), ID_TYPE_SONOS);
|
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
else if (manuf == "0087") // Garmin
|
|
|
|
{
|
|
|
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
|
|
|
setId("garmin:" + getMac(), ID_TYPE_GARMIN);
|
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
else if (manuf == "0157") // Mi-fit
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-03-06 17:48:04 +01:00
|
|
|
setId("mifit:" + getMac(), ID_TYPE_MIFIT);
|
|
|
|
}
|
|
|
|
else if (manuf == "0006" && strManufacturerData.length() == 29) // microsoft
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-03-06 17:48:04 +01:00
|
|
|
setId(Sprintf("msft:cdp:%02x%02x", strManufacturerData[3], strManufacturerData[5]), ID_TYPE_MSFT);
|
|
|
|
disc = Sprintf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
|
|
strManufacturerData[6], strManufacturerData[7], strManufacturerData[8], strManufacturerData[9], strManufacturerData[10],
|
|
|
|
strManufacturerData[11], strManufacturerData[12], strManufacturerData[13], strManufacturerData[14], strManufacturerData[15],
|
|
|
|
strManufacturerData[16], strManufacturerData[17], strManufacturerData[18], strManufacturerData[19], strManufacturerData[20],
|
|
|
|
strManufacturerData[21], strManufacturerData[22], strManufacturerData[23], strManufacturerData[24], strManufacturerData[25]);
|
|
|
|
}
|
|
|
|
else if (manuf == "0075") // samsung
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
2022-03-06 17:48:04 +01:00
|
|
|
setId("samsung:" + getMac(), ID_TYPE_MISC);
|
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
else if (manuf == "beac" && strManufacturerData.length() == 26)
|
2022-03-06 17:48:04 +01:00
|
|
|
{
|
|
|
|
BLEBeacon oBeacon = BLEBeacon();
|
|
|
|
oBeacon.setData(strManufacturerData.substr(0, 25));
|
2022-03-14 02:54:50 +01:00
|
|
|
setId(Sprintf("altBeacon:%s-%u-%u", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), ID_TYPE_ABEACON);
|
2022-03-06 17:48:04 +01:00
|
|
|
calRssi = oBeacon.getSignalPower();
|
|
|
|
}
|
|
|
|
else if (manuf != "0000")
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
|
|
|
|
String fingerprint = Sprintf("md:%s:%zu", manuf.c_str(), strManufacturerData.length());
|
|
|
|
if (haveTxPower) fingerprint = fingerprint + String(-txPower);
|
2022-03-06 17:48:04 +01:00
|
|
|
setId(fingerprint, ID_TYPE_MD);
|
|
|
|
}
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
bool BleFingerprint::filter()
|
|
|
|
{
|
2022-02-20 22:03:03 +01:00
|
|
|
Reading<float, unsigned long> inter1, inter2;
|
|
|
|
inter1.timestamp = millis();
|
2021-09-30 18:48:50 +02:00
|
|
|
inter1.value = raw;
|
2021-04-07 02:13:13 +02:00
|
|
|
|
2021-09-30 18:48:50 +02:00
|
|
|
return oneEuro.push(&inter1, &inter2) && diffFilter.push(&inter2, &output);
|
2021-04-07 02:13:13 +02:00
|
|
|
}
|
|
|
|
|
2021-09-30 18:48:50 +02:00
|
|
|
bool BleFingerprint::seen(BLEAdvertisedDevice *advertisedDevice)
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-30 18:48:50 +02:00
|
|
|
lastSeenMillis = millis();
|
2022-02-20 22:03:03 +01:00
|
|
|
reported = false;
|
|
|
|
|
2021-10-05 13:54:41 +02:00
|
|
|
seenCount++;
|
2021-09-30 18:48:50 +02:00
|
|
|
|
|
|
|
if (ignore) return false;
|
2021-03-29 20:50:23 +02:00
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
oldest = recent;
|
|
|
|
recent = newest;
|
|
|
|
newest = advertisedDevice->getRSSI();
|
|
|
|
rssi = median_of_3(oldest, recent, newest);
|
|
|
|
|
2021-09-15 18:11:32 +02:00
|
|
|
fingerprint(advertisedDevice);
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2021-09-30 18:48:50 +02:00
|
|
|
if (ignore) return false;
|
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
float ratio = (get1mRssi() - rssi) / (10.0f * BleFingerprintCollection::absorption);
|
2021-03-30 00:12:59 +02:00
|
|
|
raw = pow(10, ratio);
|
2022-02-20 22:03:03 +01:00
|
|
|
if (filter()) hasValue = true;
|
2021-03-24 14:10:33 +01:00
|
|
|
|
2022-02-20 22:03:03 +01:00
|
|
|
if (!close && newest > CLOSE_RSSI)
|
2021-03-22 23:56:29 +01:00
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
GUI::close(this);
|
2022-02-20 22:03:03 +01:00
|
|
|
close = true;
|
|
|
|
}
|
|
|
|
else if (close && newest < LEFT_RSSI)
|
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
GUI::left(this);
|
2022-02-20 22:03:03 +01:00
|
|
|
close = false;
|
|
|
|
}
|
2021-09-30 18:48:50 +02:00
|
|
|
|
2022-02-20 22:03:03 +01:00
|
|
|
if (!added)
|
|
|
|
{
|
|
|
|
added = true;
|
|
|
|
return true;
|
2021-03-22 23:56:29 +01:00
|
|
|
}
|
2021-09-30 18:48:50 +02:00
|
|
|
|
|
|
|
return false;
|
2021-03-29 20:50:23 +02:00
|
|
|
}
|
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
void BleFingerprint::setInitial(int initalRssi, float initalDistance)
|
2021-03-30 00:12:59 +02:00
|
|
|
{
|
2021-04-07 02:13:13 +02:00
|
|
|
newest = recent = oldest = rssi = initalRssi;
|
|
|
|
raw = initalDistance;
|
|
|
|
hasValue = filter() || filter();
|
2021-03-30 00:12:59 +02:00
|
|
|
}
|
|
|
|
|
2021-10-12 01:57:30 +02:00
|
|
|
bool BleFingerprint::report(JsonDocument *doc)
|
2021-03-29 20:50:23 +02:00
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
if (ignore || idType == 0 || hidden)
|
2021-03-29 20:50:23 +02:00
|
|
|
return false;
|
|
|
|
|
2021-09-30 18:48:50 +02:00
|
|
|
if (reported || !hasValue)
|
2021-03-29 20:50:23 +02:00
|
|
|
return false;
|
2021-03-24 14:10:33 +01:00
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
auto maxDistance = BleFingerprintCollection::maxDistance;
|
2021-10-05 13:54:41 +02:00
|
|
|
if (maxDistance > 0 && output.value.position > maxDistance)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto now = millis();
|
2022-03-06 17:48:04 +01:00
|
|
|
if ((abs(output.value.position - lastReported) < BleFingerprintCollection::skipDistance) && (lastReportedMillis > 0) && (now - lastReportedMillis < BleFingerprintCollection::skipMs))
|
2021-10-05 13:54:41 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
lastReportedMillis = now;
|
|
|
|
lastReported = output.value.position;
|
|
|
|
reported = true;
|
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
(*doc)[F("id")] = id;
|
2021-10-05 13:54:41 +02:00
|
|
|
if (!name.isEmpty()) (*doc)[F("name")] = name;
|
2021-10-11 04:49:56 +02:00
|
|
|
if (!disc.isEmpty()) (*doc)[F("disc")] = disc;
|
2021-11-11 03:55:10 +01:00
|
|
|
if (idType) (*doc)[F("idType")] = idType;
|
2021-03-31 19:36:20 +02:00
|
|
|
|
2021-10-05 13:54:41 +02:00
|
|
|
(*doc)[F("rssi@1m")] = get1mRssi();
|
|
|
|
(*doc)[F("rssi")] = rssi;
|
2021-04-02 13:56:27 +02:00
|
|
|
|
2021-10-05 13:54:41 +02:00
|
|
|
(*doc)[F("raw")] = round(raw * 100.0f) / 100.0f;
|
|
|
|
(*doc)[F("distance")] = round(output.value.position * 100.0f) / 100.0f;
|
2022-03-06 17:48:04 +01:00
|
|
|
(*doc)[F("speed")] = round(output.value.speed * 1e5f) / 100.0f;
|
2022-02-20 22:03:03 +01:00
|
|
|
(*doc)[F("mac")] = SMacf(address);
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
(*doc)[F("interval")] = (now - firstSeenMillis) / seenCount;
|
|
|
|
|
2021-11-11 03:55:10 +01:00
|
|
|
if (mv) (*doc)[F("mV")] = mv;
|
2022-03-14 02:54:50 +01:00
|
|
|
if (battery != 0xFF) (*doc)[F("batt")] = battery;
|
|
|
|
if (temp) (*doc)[F("temp")] = round(temp*10)/10;
|
|
|
|
if (humidity) (*doc)[F("rh")] = round(humidity*10)/10;
|
2021-03-30 00:12:59 +02:00
|
|
|
|
2021-10-05 13:54:41 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BleFingerprint::query()
|
|
|
|
{
|
2022-02-14 13:50:30 +01:00
|
|
|
if (!(allowQuery || rmAsst) || didQuery) return false;
|
2021-10-12 17:23:48 +02:00
|
|
|
if (rssi < -90) return false;
|
2021-10-05 13:54:41 +02:00
|
|
|
auto now = millis();
|
2022-02-14 13:50:30 +01:00
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
if (now - lastSeenMillis > 10) return false;
|
2022-02-14 13:50:30 +01:00
|
|
|
|
2021-10-12 17:23:48 +02:00
|
|
|
if (now - lastQryMillis < qryDelayMillis) return false;
|
2021-10-05 13:54:41 +02:00
|
|
|
didQuery = true;
|
|
|
|
lastQryMillis = now;
|
2021-10-12 01:57:30 +02:00
|
|
|
|
2021-10-12 17:23:48 +02:00
|
|
|
bool success = false;
|
|
|
|
|
2022-03-14 02:54:50 +01:00
|
|
|
Serial.printf("%u Query | MAC: %s, ID: %-60s %lums\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), now - lastSeenMillis);
|
2022-02-13 18:58:17 +01:00
|
|
|
|
2021-10-12 01:57:30 +02:00
|
|
|
NimBLEClient *pClient = NimBLEDevice::getClientListSize() ? NimBLEDevice::getClientByPeerAddress(address) : nullptr;
|
|
|
|
if (!pClient) pClient = NimBLEDevice::getDisconnectedClient();
|
2021-10-05 13:54:41 +02:00
|
|
|
if (!pClient) pClient = NimBLEDevice::createClient();
|
2022-03-14 02:54:50 +01:00
|
|
|
pClient->setClientCallbacks(&clientCB, false);
|
|
|
|
pClient->setConnectionParams(12, 12, 0, 200);
|
2022-02-14 13:50:30 +01:00
|
|
|
pClient->setConnectTimeout(5);
|
2022-03-14 02:54:50 +01:00
|
|
|
NimBLEDevice::getScan()->stop();
|
2021-10-05 13:54:41 +02:00
|
|
|
if (pClient->connect(address))
|
|
|
|
{
|
2022-03-14 02:54:50 +01:00
|
|
|
delay(100);
|
2022-02-14 19:04:41 +01:00
|
|
|
bool iphone = true;
|
|
|
|
if (allowQuery)
|
2021-10-05 13:54:41 +02:00
|
|
|
{
|
2022-02-15 06:14:32 +01:00
|
|
|
std::string sMdl = pClient->getValue(deviceInformationService, modelChar);
|
|
|
|
std::string sName = pClient->getValue(genericAccessService, nameChar);
|
2022-02-14 19:04:41 +01:00
|
|
|
iphone = sMdl.find("iPhone") == 0;
|
2022-03-06 17:48:04 +01:00
|
|
|
if (!sName.empty() && sMdl.find(sName) == std::string::npos && sName != "Apple Watch")
|
2021-10-12 17:23:48 +02:00
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
if (setId(String("name:") + kebabify(sName).c_str(), ID_TYPE_QUERY_NAME, String(sName.c_str())))
|
2022-03-14 02:54:50 +01:00
|
|
|
{
|
|
|
|
Serial.printf("\u001b[38;5;104m%u Name | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), sName.c_str());
|
|
|
|
}
|
2022-02-13 18:58:17 +01:00
|
|
|
success = true;
|
2021-10-12 17:23:48 +02:00
|
|
|
}
|
2022-03-06 17:48:04 +01:00
|
|
|
|
|
|
|
if (!sMdl.empty())
|
2021-10-05 13:54:41 +02:00
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
if (setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_QUERY_MODEL, String(sMdl.c_str())))
|
2022-03-14 02:54:50 +01:00
|
|
|
{
|
|
|
|
Serial.printf("\u001b[38;5;136m%u Model | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), sMdl.c_str());
|
|
|
|
}
|
2022-02-13 18:58:17 +01:00
|
|
|
success = true;
|
2021-10-05 13:54:41 +02:00
|
|
|
}
|
|
|
|
}
|
2022-02-14 19:04:41 +01:00
|
|
|
|
2022-03-06 17:48:04 +01:00
|
|
|
if (rmAsst || iphone) // For some reason we often don't get room assistant's service advertisement
|
2022-02-14 19:04:41 +01:00
|
|
|
{
|
2022-02-15 06:14:32 +01:00
|
|
|
std::string sRmAst = pClient->getValue(roomAssistantService, rootAssistantCharacteristic);
|
2022-02-14 19:04:41 +01:00
|
|
|
if (!sRmAst.empty())
|
|
|
|
{
|
2022-03-06 17:48:04 +01:00
|
|
|
if (setId(String("roomAssistant:") + kebabify(sRmAst).c_str(), ID_TYPE_RM_ASST))
|
2022-03-14 02:54:50 +01:00
|
|
|
{
|
|
|
|
Serial.printf("\u001b[38;5;129m%u RmAst | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), sRmAst.c_str());
|
|
|
|
}
|
2022-02-14 19:04:41 +01:00
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
}
|
2021-10-05 13:54:41 +02:00
|
|
|
}
|
2021-10-12 17:23:48 +02:00
|
|
|
|
2022-02-14 14:32:03 +01:00
|
|
|
NimBLEDevice::deleteClient(pClient);
|
|
|
|
|
2021-10-12 17:23:48 +02:00
|
|
|
if (success) return true;
|
|
|
|
|
2022-02-13 18:58:17 +01:00
|
|
|
qryAttempts++;
|
2022-03-14 02:54:50 +01:00
|
|
|
Serial.printf("%u QryErr| MAC: %s, ID: %-60s rssi %d, try %d, retry after %dms\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, qryAttempts, qryDelayMillis);
|
2021-10-12 17:23:48 +02:00
|
|
|
|
2022-02-10 18:18:23 +01:00
|
|
|
if (qryDelayMillis < 30000)
|
2022-02-13 18:58:17 +01:00
|
|
|
qryDelayMillis += (1000 * qryAttempts * qryAttempts);
|
2021-10-05 13:54:41 +02:00
|
|
|
else
|
2022-02-10 18:18:23 +01:00
|
|
|
qryDelayMillis = 30000;
|
2021-10-12 17:23:48 +02:00
|
|
|
didQuery = false;
|
|
|
|
|
2021-10-05 13:54:41 +02:00
|
|
|
return true;
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
2022-03-14 02:54:50 +01:00
|
|
|
|
|
|
|
bool BleFingerprint::shouldCount()
|
|
|
|
{
|
|
|
|
bool prevCounting = counting;
|
|
|
|
if (ignore || !countable)
|
|
|
|
counting = false;
|
|
|
|
else if (getMsSinceFirstSeen() <= BleFingerprintCollection::countMs || getMsSinceLastSeen() > BleFingerprintCollection::countMs)
|
|
|
|
counting = false;
|
|
|
|
else if (counting && output.value.position > BleFingerprintCollection::countExit)
|
|
|
|
counting = false;
|
|
|
|
else if (!counting && output.value.position <= BleFingerprintCollection::countEnter)
|
|
|
|
counting = true;
|
|
|
|
|
|
|
|
if (prevCounting != counting)
|
|
|
|
{
|
|
|
|
counting ? GUI::plusOne(this) : GUI::minusOne(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
return counting;
|
|
|
|
}
|