Add device model to apple fingerprints add support room assistant app (#69)
* Add Active Scan and Query switches * Rework stats * Cleanup less often
This commit is contained in:
parent
82a181252f
commit
28443789b6
|
@ -9,3 +9,4 @@ Settings_*.h
|
|||
.pio
|
||||
.vscode
|
||||
.scripts
|
||||
.DS_Store
|
||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -5,13 +5,25 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.0.0]
|
||||
|
||||
- Add `Query` switch to mqtt and webapp setup (defaults to off so as to not break existing fingerprints)
|
||||
- Add `Active Scan` switch to mqtt and webapp setup
|
||||
- Add device model to apple fingerprints
|
||||
- Add support [room assistant app](https://apps.apple.com/us/app/room-assistant/id1538642237?itsct=apps_box_link&itscg=30200) @DTTerastar (#69)
|
||||
- Stats reworked
|
||||
|
||||
## [1.7.3]
|
||||
|
||||
- Skip distance was being ignored because of an accidental cast to int @DTTerastar (#75)
|
||||
|
||||
## [1.7.2]
|
||||
|
||||
Fix MQTT last will and testament not consistently sending offline
|
||||
- Fix MQTT last will and testament not consistently sending offline
|
||||
|
||||
## [1.7.1]
|
||||
|
||||
Fix more places slugified room name is needed
|
||||
- Fix more places slugified room name is needed
|
||||
|
||||
## [1.7.0]
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "BleFingerprint.h"
|
||||
#include "BleFingerprintCollection.h"
|
||||
#include "rssi.h"
|
||||
#include "strings.h"
|
||||
#include "util.h"
|
||||
|
||||
#define Sprintf(f, ...) ( \
|
||||
|
@ -18,24 +19,6 @@
|
|||
Sprintf("%02x%02x%02x%02x%02x%02x", nativeAddress[5], nativeAddress[4], nativeAddress[3], nativeAddress[2], nativeAddress[1], nativeAddress[0]); \
|
||||
})
|
||||
|
||||
static std::string hexStr(const char *data, int len)
|
||||
{
|
||||
constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
std::string s(len * 2, ' ');
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
s[2 * i] = hexmap[(data[i] & 0xF0) >> 4];
|
||||
s[2 * i + 1] = hexmap[data[i] & 0x0F];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::string hexStr(std::string s)
|
||||
{
|
||||
return hexStr(s.c_str(), s.length());
|
||||
}
|
||||
|
||||
String BleFingerprint::getMac() { return SMacf(address); }
|
||||
|
||||
int BleFingerprint::get1mRssi()
|
||||
|
@ -72,23 +55,29 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice)
|
|||
if (advertisedDevice->isAdvertisingService(tileUUID))
|
||||
{
|
||||
calRssi = _parent->getRefRssi() + TILE_TX;
|
||||
pid = "tile:" + getMac();
|
||||
if (!pidOverriden) pid = "tile:" + getMac();
|
||||
}
|
||||
else if (advertisedDevice->isAdvertisingService(exposureUUID))
|
||||
{ // found covid exposure tracker
|
||||
std::string strServiceData = advertisedDevice->getServiceData(exposureUUID);
|
||||
calRssi = _parent->getRefRssi() + EXPOSURE_TX;
|
||||
pid = "exp:" + String(strServiceData.length());
|
||||
if (!pidOverriden) pid = "exp:" + String(strServiceData.length());
|
||||
name = hexStr(strServiceData).c_str();
|
||||
}
|
||||
else if (advertisedDevice->isAdvertisingService(sonosUUID))
|
||||
{
|
||||
asRssi = advertisedDevice->haveTXPower() ? _parent->getRefRssi() + advertisedDevice->getTXPower() : NO_RSSI;
|
||||
pid = "sonos:" + getMac();
|
||||
if (!pidOverriden) pid = "sonos:" + getMac();
|
||||
}
|
||||
else if (advertisedDevice->isAdvertisingService(itagUUID))
|
||||
{
|
||||
asRssi = _parent->getRefRssi() + (advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() : ITAG_TX);
|
||||
pid = "itag:" + getMac();
|
||||
if (!pidOverriden) pid = "itag:" + getMac();
|
||||
}
|
||||
else if (advertisedDevice->isAdvertisingService(roomAssistantService))
|
||||
{
|
||||
asRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
||||
shouldQuery = true;
|
||||
}
|
||||
else if (advertisedDevice->isAdvertisingService(eddystoneUUID))
|
||||
{
|
||||
|
@ -132,7 +121,7 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice)
|
|||
#ifdef VERBOSE
|
||||
Serial.printf("Verbose | %-58sMD: %s\n", getId().c_str(), hexStr(strManufacturerData).c_str());
|
||||
#endif
|
||||
if (strManufacturerData.length() > 2)
|
||||
if (strManufacturerData.length() >= 2)
|
||||
{
|
||||
String manuf = Sprintf("%02x%02x", strManufacturerData[1], strManufacturerData[0]);
|
||||
|
||||
|
@ -142,23 +131,36 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice)
|
|||
{
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setData(strManufacturerData);
|
||||
pid = Sprintf("iBeacon:%s-%d-%d", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()));
|
||||
if (!pidOverriden) pid = Sprintf("iBeacon:%s-%d-%d", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()));
|
||||
calRssi = oBeacon.getSignalPower();
|
||||
}
|
||||
else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10)
|
||||
{
|
||||
shouldQuery = true;
|
||||
ignore = false;
|
||||
if (!pidOverriden)
|
||||
{
|
||||
if (advertisedDevice->haveTXPower())
|
||||
pid = Sprintf("apple:%02x%02x:%d%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length(), -advertisedDevice->getTXPower());
|
||||
else
|
||||
pid = Sprintf("apple:%02x%02x:%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
|
||||
}
|
||||
mdRssi = _parent->getRefRssi() + APPLE_TX;
|
||||
}
|
||||
else
|
||||
{
|
||||
ignore = strManufacturerData[2] != 0x10;
|
||||
if (advertisedDevice->haveTXPower())
|
||||
pid = Sprintf("apple:%02x%02x:%d%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length(), -advertisedDevice->getTXPower());
|
||||
sid = Sprintf("apple:%02x%02x:%d%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length(), -advertisedDevice->getTXPower());
|
||||
else
|
||||
pid = Sprintf("apple:%02x%02x:%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
|
||||
sid = Sprintf("apple:%02x%02x:%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
|
||||
mdRssi = _parent->getRefRssi() + APPLE_TX;
|
||||
ignore = true;
|
||||
}
|
||||
}
|
||||
else if (manuf == "05a7") //Sonos
|
||||
{
|
||||
mdRssi = advertisedDevice->haveTXPower() ? _parent->getRefRssi() + advertisedDevice->getTXPower() : NO_RSSI;
|
||||
pid = "sonos:" + getMac();
|
||||
if (!pidOverriden) pid = "sonos:" + getMac();
|
||||
}
|
||||
else if (manuf == "0157") //Mi-fit
|
||||
{
|
||||
|
@ -168,17 +170,18 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice)
|
|||
else if (manuf == "0006" && strManufacturerData.length() == 29) //microsoft
|
||||
{
|
||||
mdRssi = advertisedDevice->haveTXPower() ? _parent->getRefRssi() + advertisedDevice->getTXPower() : NO_RSSI;
|
||||
pid = Sprintf("microsoft:%02x%02x%02x%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], strManufacturerData[26], strManufacturerData[27], strManufacturerData[28]);
|
||||
pid = "md:microsoft:29";
|
||||
name = Sprintf("%02x%02x%02x%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], strManufacturerData[26], strManufacturerData[27], strManufacturerData[28]);
|
||||
ignore = true;
|
||||
}
|
||||
else if (manuf == "0075") //samsung
|
||||
{
|
||||
mdRssi = advertisedDevice->haveTXPower() ? _parent->getRefRssi() + advertisedDevice->getTXPower() : NO_RSSI;
|
||||
pid = "samsung:" + getMac();
|
||||
if (!pidOverriden) pid = "samsung:" + getMac();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -204,6 +207,7 @@ bool BleFingerprint::filter()
|
|||
bool BleFingerprint::seen(BLEAdvertisedDevice *advertisedDevice)
|
||||
{
|
||||
lastSeenMillis = millis();
|
||||
seenCount++;
|
||||
|
||||
if (ignore) return false;
|
||||
|
||||
|
@ -258,30 +262,90 @@ bool BleFingerprint::report(JsonDocument *doc, float maxDistance)
|
|||
if (reported || !hasValue)
|
||||
return false;
|
||||
|
||||
if (maxDistance > 0 && output.value.position > maxDistance)
|
||||
return false;
|
||||
if (maxDistance > 0 && output.value.position > maxDistance)
|
||||
return false;
|
||||
|
||||
auto now = millis();
|
||||
if (abs(output.value.position - lastReported) < _parent->getSkipDistance() && lastReportedMillis > 0 && now - lastReportedMillis < _parent->getSkipMs())
|
||||
return false;
|
||||
auto now = millis();
|
||||
if ((abs(output.value.position - lastReported) < _parent->getSkipDistance()) && (lastReportedMillis > 0) && (now - lastReportedMillis < _parent->getSkipMs()))
|
||||
return false;
|
||||
|
||||
lastReportedMillis = now;
|
||||
lastReported = output.value.position;
|
||||
reported = true;
|
||||
//Serial.printf("%f > %f || %d > %d\n", abs(output.value.position - lastReported), _parent->getSkipDistance(), now - lastReportedMillis, _parent->getSkipMs());
|
||||
|
||||
(*doc)[F("id")] = getId();
|
||||
if (!name.isEmpty()) (*doc)[F("name")] = name;
|
||||
lastReportedMillis = now;
|
||||
lastReported = output.value.position;
|
||||
reported = true;
|
||||
|
||||
(*doc)[F("rssi@1m")] = get1mRssi();
|
||||
(*doc)[F("rssi")] = rssi;
|
||||
(*doc)[F("id")] = getId();
|
||||
if (!name.isEmpty()) (*doc)[F("name")] = name;
|
||||
|
||||
(*doc)[F("mac")] = SMacf(address);
|
||||
(*doc)[F("raw")] = round(raw * 100.0f) / 100.0f;
|
||||
(*doc)[F("distance")] = round(output.value.position * 100.0f) / 100.0f;
|
||||
(*doc)[F("speed")] = round(output.value.speed * 1e7f) / 10.0f;
|
||||
(*doc)[F("rssi@1m")] = get1mRssi();
|
||||
(*doc)[F("rssi")] = rssi;
|
||||
|
||||
if (volts) (*doc)[F("volts")] = volts;
|
||||
if (temp) (*doc)[F("temp")] = temp;
|
||||
(*doc)[F("mac")] = SMacf(address);
|
||||
(*doc)[F("raw")] = round(raw * 100.0f) / 100.0f;
|
||||
(*doc)[F("distance")] = round(output.value.position * 100.0f) / 100.0f;
|
||||
(*doc)[F("speed")] = round(output.value.speed * 1e7f) / 10.0f;
|
||||
|
||||
return true;
|
||||
if (volts) (*doc)[F("volts")] = volts;
|
||||
if (temp) (*doc)[F("temp")] = temp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleFingerprint::query()
|
||||
{
|
||||
if (!shouldQuery || didQuery) return false;
|
||||
auto now = millis();
|
||||
if (now - lastQryMillis < 500) return false;
|
||||
didQuery = true;
|
||||
lastQryMillis = now;
|
||||
auto pClient = NimBLEDevice::getDisconnectedClient();
|
||||
if (!pClient) pClient = NimBLEDevice::createClient();
|
||||
pClient->setConnectTimeout(5);
|
||||
if (pClient->connect(address))
|
||||
{
|
||||
auto sName = pClient->getValue(genericAccessService, nameChar);
|
||||
if (!sName.empty())
|
||||
{
|
||||
Serial.printf("%d Name | MAC: %s, ID: %-50s%s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sName.c_str());
|
||||
if (name.length() == 0) name = sName.c_str();
|
||||
}
|
||||
|
||||
auto sRmAst = pClient->getValue(roomAssistantService, rootAssistantCharacteristic);
|
||||
if (!sRmAst.empty())
|
||||
{
|
||||
Serial.printf("%d RmAst | MAC: %s, ID: %-50s%s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sRmAst.c_str());
|
||||
if (!pidOverriden) pid = String("roomAssistant:") + kebabify(sRmAst).c_str();
|
||||
pidOverriden = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto sMdl = pClient->getValue(deviceInformationService, modelChar);
|
||||
if (!sMdl.empty())
|
||||
{
|
||||
Serial.printf("%d Model | MAC: %s, ID: %-50s%s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sMdl.c_str());
|
||||
if (!pidOverriden) pid = pid + String("-") + kebabify(sMdl).c_str();
|
||||
pidOverriden = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name.length() > 0 && !pidOverriden) pid = pid + String("-") + kebabify(name);
|
||||
pidOverriden = true;
|
||||
}
|
||||
}
|
||||
|
||||
// auto sFwRevChar = pClient->getValue(deviceInformationService, fwRevChar);
|
||||
// Serial.printf("%d FwRev | MAC: %s, ID: %-50s%s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sFwRevChar.c_str());
|
||||
|
||||
// auto sHwRevChar = pClient->getValue(deviceInformationService, hwRevChar);
|
||||
// Serial.printf("%d HwRev | MAC: %s, ID: %-50s%s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sHwRevChar.c_str());
|
||||
|
||||
pClient->disconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
qryAttempts++;
|
||||
if (qryAttempts < 10) didQuery = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
|
||||
bool seen(BLEAdvertisedDevice *advertisedDevice);
|
||||
bool report(JsonDocument *doc, float maxDistance);
|
||||
bool query();
|
||||
|
||||
String getId()
|
||||
{
|
||||
|
@ -42,20 +43,26 @@ public:
|
|||
NimBLEAddress getAddress() { return address; }
|
||||
long getAge() { return millis() - lastSeenMillis; };
|
||||
bool getIgnore() { return ignore; };
|
||||
int getSeenCount()
|
||||
{
|
||||
auto sc = seenCount;
|
||||
seenCount = 0;
|
||||
return sc;
|
||||
}
|
||||
|
||||
private:
|
||||
void fingerprint(BLEAdvertisedDevice *advertisedDevice);
|
||||
|
||||
BleFingerprintCollection *_parent;
|
||||
bool hasValue = false, close = false, reported = false, macPublic = false, ignore = false;
|
||||
bool hasValue = false, close = false, reported = false, macPublic = false, ignore = false, shouldQuery = false, didQuery = false, pidOverriden = false;
|
||||
NimBLEAddress address;
|
||||
String pid, sid, name, url;
|
||||
int rssi = -100, calRssi = NO_RSSI, mdRssi = NO_RSSI, asRssi = NO_RSSI;
|
||||
int newest = -100;
|
||||
int recent = -100;
|
||||
int oldest = -100;
|
||||
int newest = -100, recent = -100, oldest = -100;
|
||||
int qryAttempts = 0, seenCount = 1;
|
||||
|
||||
float raw = 0, lastReported = 0, temp = 0;
|
||||
unsigned long firstSeenMillis, lastSeenMillis = 0, lastReportedMillis = 0;
|
||||
unsigned long firstSeenMillis, lastSeenMillis = 0, lastReportedMillis = 0, lastQryMillis = 0;
|
||||
uint16_t volts = 0;
|
||||
|
||||
Reading<Differential<float>> output;
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
void BleFingerprintCollection::cleanupOldFingerprints()
|
||||
{
|
||||
auto now = millis();
|
||||
if (now - lastCleanup < 5000) return;
|
||||
lastCleanup = now;
|
||||
auto it = fingerprints.begin();
|
||||
while (it != fingerprints.end())
|
||||
{
|
||||
|
@ -52,13 +55,13 @@ BleFingerprint *BleFingerprintCollection::getFingerprint(BLEAdvertisedDevice *ad
|
|||
return f;
|
||||
}
|
||||
|
||||
std::list<BleFingerprint *> BleFingerprintCollection::getSeen()
|
||||
std::list<BleFingerprint *> BleFingerprintCollection::getCopy()
|
||||
{
|
||||
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
||||
log_e("Couldn't take semaphore!");
|
||||
cleanupOldFingerprints();
|
||||
std::list<BleFingerprint *> seen(fingerprints);
|
||||
std::list<BleFingerprint *> copy(fingerprints);
|
||||
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
||||
log_e("Couldn't give semaphore!");
|
||||
return seen;
|
||||
return copy;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ public:
|
|||
}
|
||||
BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice);
|
||||
void cleanupOldFingerprints();
|
||||
int getTotalAdverts() { return _totalSeen; }
|
||||
std::list<BleFingerprint *> getSeen();
|
||||
std::list<BleFingerprint *> getCopy();
|
||||
void setDisable(bool disable) { _disable = disable; }
|
||||
void setParams(int rssiRef, int forgetMs, float skipDistance, int skipMs, float maxDistance)
|
||||
{
|
||||
|
@ -35,10 +34,10 @@ public:
|
|||
|
||||
private:
|
||||
bool _disable = false;
|
||||
int _totalSeen = 0;
|
||||
|
||||
float _maxDistance, _skipDistance;
|
||||
int _refRssi, _forgetMs, _skipMs;
|
||||
unsigned long lastCleanup = 0;
|
||||
|
||||
SemaphoreHandle_t fingerprintSemaphore;
|
||||
std::list<BleFingerprint *> fingerprints;
|
||||
|
@ -46,7 +45,6 @@ private:
|
|||
|
||||
void onResult(BLEAdvertisedDevice *advertisedDevice)
|
||||
{
|
||||
_totalSeen++;
|
||||
if (_disable) return;
|
||||
|
||||
Display.seenStart();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#ifndef STRINGS_h
|
||||
#define STRINGS_h
|
||||
#include <regex>
|
||||
#include <strings.h>
|
||||
|
||||
std::string lowertrim(std::string str, char c)
|
||||
{
|
||||
|
@ -57,4 +56,21 @@ String kebabify(String text)
|
|||
std::string s = std::string(text.c_str());
|
||||
return kebabify(s).c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string hexStr(const char *data, int len)
|
||||
{
|
||||
constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
std::string s(len * 2, ' ');
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
s[2 * i] = hexmap[(data[i] & 0xF0) >> 4];
|
||||
s[2 * i + 1] = hexmap[data[i] & 0x0F];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string hexStr(std::string s)
|
||||
{
|
||||
return hexStr(s.c_str(), s.length());
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef STRINGS_h
|
||||
#define STRINGS_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
std::string slugify(std::string text);
|
||||
String slugify(String text);
|
||||
std::string kebabify(std::string text);
|
||||
String kebabify(String text);
|
||||
std::string hexStr(const char *data, int len);
|
||||
std::string hexStr(std::string s);
|
||||
#endif
|
|
@ -10,6 +10,18 @@ static BLEUUID exposureUUID((uint16_t)0xFD6F);
|
|||
static BLEUUID sonosUUID((uint16_t)0xFE07);
|
||||
static BLEUUID itagUUID((uint16_t)0xffe0);
|
||||
|
||||
static BLEUUID roomAssistantService(uint32_t(0x5403c8a7), uint16_t(0x5c96), uint16_t(0x47e9), uint64_t(0x9ab859e373d875a7));
|
||||
static BLEUUID rootAssistantCharacteristic(0x21c46f33, 0xe813, 0x4407, 0x86012ad281030052);
|
||||
|
||||
static BLEUUID genericAccessService(uint16_t(0x1800));
|
||||
static BLEUUID deviceInformationService(uint16_t(0x180A));
|
||||
|
||||
static BLEUUID nameChar(uint16_t(0x2A00));
|
||||
static BLEUUID manufChar(uint16_t(0x2A29));
|
||||
static BLEUUID modelChar(uint16_t(0x2A24));
|
||||
static BLEUUID hwRevChar(uint16_t(0x2A27));
|
||||
static BLEUUID fwRevChar(uint16_t(0x2A26));
|
||||
|
||||
static int median_of_3(int a, int b, int c)
|
||||
{
|
||||
int the_max = max(max(a, b), c);
|
||||
|
|
|
@ -22,6 +22,8 @@ lib_deps_external =
|
|||
bbx10/DNSServer@^1.1.0
|
||||
adafruit/Adafruit Unified Sensor @ ^1.1.4
|
||||
beegee-tokyo/DHT sensor library for ESPx @ ^1.18
|
||||
build_flags =
|
||||
-D CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
|
||||
|
||||
[env:esp32]
|
||||
platform = espressif32
|
||||
|
|
88
src/main.cpp
88
src/main.cpp
|
@ -1,10 +1,9 @@
|
|||
#include <main.h>
|
||||
|
||||
bool sendTelemetry(int totalSeen = -1, int totalReported = -1, int totalAdverts = -1)
|
||||
bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int totalFpReported)
|
||||
{
|
||||
if (!online)
|
||||
{
|
||||
if (sendOnline() && sendDiscoveryConnectivity() && sendDiscoveryMaxDistance() && sendDiscoveryMotion() && sendDiscoveryHumidity() && sendDiscoveryTemperature())
|
||||
if (sendOnline())
|
||||
{
|
||||
online = true;
|
||||
reconnectTries = 0;
|
||||
|
@ -15,6 +14,18 @@ bool sendTelemetry(int totalSeen = -1, int totalReported = -1, int totalAdverts
|
|||
}
|
||||
}
|
||||
|
||||
if (discovery && !sentDiscovery)
|
||||
{
|
||||
if (sendDiscoveryConnectivity() && sendNumberDiscovery("Max Distance") && sendSwitchDiscovery("Active Scan") && sendSwitchDiscovery("Query") && sendDiscoveryMotion() && sendDiscoveryHumidity() && sendDiscoveryTemperature())
|
||||
{
|
||||
sentDiscovery = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_e("Error sending discovery");
|
||||
}
|
||||
}
|
||||
|
||||
auto now = millis();
|
||||
|
||||
if (now - lastTeleMillis < 15000)
|
||||
|
@ -33,13 +44,15 @@ bool sendTelemetry(int totalSeen = -1, int totalReported = -1, int totalAdverts
|
|||
#ifdef VERSION
|
||||
tele["ver"] = String(VERSION);
|
||||
#endif
|
||||
|
||||
if (totalSeen > 0)
|
||||
tele["seen"] = totalSeen;
|
||||
if (totalReported > 0)
|
||||
tele["reported"] = totalReported;
|
||||
if (totalAdverts > 0)
|
||||
tele["adverts"] = totalAdverts;
|
||||
tele["adverts"] = totalSeen;
|
||||
if (totalFpSeen > 0)
|
||||
tele["seen"] = totalFpSeen;
|
||||
if (totalFpQueried > 0)
|
||||
tele["queried"] = totalFpQueried;
|
||||
if (totalFpReported > 0)
|
||||
tele["reported"] = totalFpReported;
|
||||
|
||||
if (teleFails > 0)
|
||||
tele["teleFails"] = teleFails;
|
||||
if (reconnectTries > 0)
|
||||
|
@ -108,7 +121,8 @@ void connectToWifi()
|
|||
autoUpdate = WiFiSettings.checkbox("auto_update", DEFAULT_AUTO_UPDATE, "Automatically Update");
|
||||
otaUpdate = WiFiSettings.checkbox("ota_update", DEFAULT_OTA_UPDATE, "Arduino OTA Update");
|
||||
discovery = WiFiSettings.checkbox("discovery", true, "Home Assistant Discovery");
|
||||
activeScan = WiFiSettings.checkbox("active_scan", true, "Active scanning (uses more battery but more results)");
|
||||
activeScan = WiFiSettings.checkbox("active_scan", false, "Active scanning (uses more battery but more results)");
|
||||
allowQuery = WiFiSettings.checkbox("query", false, "Query devices for characteristics (helps apple fingerprints uniqueness)");
|
||||
publishTele = WiFiSettings.checkbox("pub_tele", true, "Send to telemetry topic");
|
||||
publishRooms = WiFiSettings.checkbox("pub_rooms", true, "Send to rooms topic");
|
||||
publishDevices = WiFiSettings.checkbox("pub_devices", true, "Send to devices topic");
|
||||
|
@ -198,6 +212,19 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties
|
|||
{
|
||||
maxDistance = pay.toFloat();
|
||||
spurt("/max_dist", pay);
|
||||
online = false;
|
||||
}
|
||||
else if (top == roomsTopic + "/active_scan/set")
|
||||
{
|
||||
activeScan = pay == "ON";
|
||||
spurt("/active_scan", String(activeScan));
|
||||
online = false;
|
||||
}
|
||||
else if (top == roomsTopic + "/query/set")
|
||||
{
|
||||
allowQuery = pay == "ON";
|
||||
spurt("/query", String(allowQuery));
|
||||
online = false;
|
||||
}
|
||||
|
||||
fingerprints.setParams(refRssi, forgetMs, skipDistance, skipMs, maxDistance);
|
||||
|
@ -273,6 +300,7 @@ void scanForDevices(void *parameter)
|
|||
{
|
||||
fingerprints.setParams(refRssi, forgetMs, skipDistance, skipMs, maxDistance);
|
||||
BLEDevice::init("");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
auto pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setInterval(BLE_SCAN_INTERVAL);
|
||||
pBLEScan->setWindow(BLE_SCAN_WINDOW);
|
||||
|
@ -282,25 +310,45 @@ void scanForDevices(void *parameter)
|
|||
if (!pBLEScan->start(0, nullptr, false))
|
||||
log_e("Error starting continuous ble scan");
|
||||
|
||||
int totalSeen = 0;
|
||||
int totalFpSeen = 0;
|
||||
int totalFpQueried = 0;
|
||||
int totalFpReported = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
delay(1000);
|
||||
while (updateInProgress || !mqttClient.connected())
|
||||
delay(1000);
|
||||
|
||||
if (updateInProgress || !mqttClient.connected())
|
||||
continue;
|
||||
sendTelemetry(totalSeen, totalFpSeen, totalFpQueried, totalFpReported);
|
||||
|
||||
int totalSeen = 0;
|
||||
int totalReported = 0;
|
||||
auto seen = fingerprints.getCopy();
|
||||
|
||||
auto seen = fingerprints.getSeen();
|
||||
if (allowQuery)
|
||||
{
|
||||
for (auto it = seen.begin(); it != seen.end(); ++it)
|
||||
{
|
||||
auto f = (*it);
|
||||
if (f->query())
|
||||
totalFpQueried++;
|
||||
}
|
||||
|
||||
if (!pBLEScan->start(0, nullptr, false))
|
||||
log_e("Error re-starting continuous ble scan");
|
||||
}
|
||||
|
||||
for (auto it = seen.begin(); it != seen.end(); ++it)
|
||||
{
|
||||
totalSeen++;
|
||||
if (reportDevice(*it))
|
||||
totalReported++;
|
||||
auto f = (*it);
|
||||
auto seen = f->getSeenCount();
|
||||
if (seen)
|
||||
{
|
||||
totalSeen += seen;
|
||||
totalFpSeen++;
|
||||
}
|
||||
if (reportDevice(f))
|
||||
totalFpReported++;
|
||||
}
|
||||
sendTelemetry(totalSeen, totalReported, fingerprints.getTotalAdverts());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
53
src/main.h
53
src/main.h
|
@ -33,7 +33,8 @@ String localIp;
|
|||
unsigned long lastTeleMillis;
|
||||
int reconnectTries = 0;
|
||||
int teleFails = 0;
|
||||
bool online; // Have we successfully sent status=online
|
||||
bool online = false; // Have we successfully sent status=online
|
||||
bool sentDiscovery = false; // Have we successfully sent discovery
|
||||
String offline = "offline";
|
||||
String mqttHost;
|
||||
int mqttPort;
|
||||
|
@ -47,7 +48,7 @@ String roomsTopic;
|
|||
String subTopic;
|
||||
bool autoUpdate, otaUpdate;
|
||||
bool discovery;
|
||||
bool activeScan;
|
||||
bool activeScan, allowQuery;
|
||||
bool publishTele;
|
||||
bool publishRooms;
|
||||
bool publishDevices;
|
||||
|
@ -286,7 +287,7 @@ void spiffsInit()
|
|||
|
||||
bool sendOnline()
|
||||
{
|
||||
return mqttClient.publish(statusTopic.c_str(), 0, 1, "online") && mqttClient.publish((roomsTopic + "/max_distance").c_str(), 0, 1, String(maxDistance).c_str());
|
||||
return mqttClient.publish(statusTopic.c_str(), 0, 1, "online") && mqttClient.publish((roomsTopic + "/max_distance").c_str(), 0, 1, String(maxDistance).c_str()) && mqttClient.publish((roomsTopic + "/query").c_str(), 0, 1, String(allowQuery ? "ON" : "OFF").c_str()) && mqttClient.publish((roomsTopic + "/active_scan").c_str(), 0, 1, String(activeScan ? "ON" : "OFF").c_str());
|
||||
}
|
||||
|
||||
void commonDiscovery(JsonDocument *doc)
|
||||
|
@ -308,8 +309,6 @@ void commonDiscovery(JsonDocument *doc)
|
|||
|
||||
bool sendDiscoveryConnectivity()
|
||||
{
|
||||
if (!discovery) return true;
|
||||
|
||||
DynamicJsonDocument doc(1200);
|
||||
commonDiscovery(&doc);
|
||||
doc["~"] = roomsTopic;
|
||||
|
@ -337,7 +336,6 @@ bool sendDiscoveryConnectivity()
|
|||
|
||||
bool sendDiscoveryMotion()
|
||||
{
|
||||
if (!discovery) return true;
|
||||
if (!pirPin && !radarPin) return true;
|
||||
|
||||
DynamicJsonDocument doc(1200);
|
||||
|
@ -364,7 +362,6 @@ bool sendDiscoveryMotion()
|
|||
|
||||
bool sendDiscoveryTemperature()
|
||||
{
|
||||
if (!discovery) return true;
|
||||
if (!dht11Pin && !dht22Pin) return true;
|
||||
|
||||
DynamicJsonDocument doc(1200);
|
||||
|
@ -393,7 +390,6 @@ bool sendDiscoveryTemperature()
|
|||
|
||||
bool sendDiscoveryHumidity()
|
||||
{
|
||||
if (!discovery) return true;
|
||||
if (!dht11Pin && !dht22Pin) return true;
|
||||
|
||||
DynamicJsonDocument doc(1200);
|
||||
|
@ -419,23 +415,50 @@ bool sendDiscoveryHumidity()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool sendDiscoveryMaxDistance()
|
||||
bool sendSwitchDiscovery(String name)
|
||||
{
|
||||
if (!discovery) return true;
|
||||
auto slug = slugify(name);
|
||||
|
||||
DynamicJsonDocument doc(1200);
|
||||
commonDiscovery(&doc);
|
||||
doc["~"] = roomsTopic;
|
||||
doc["name"] = "ESPresense " + room + " Max Distance";
|
||||
doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_max_distance", ESP.getEfuseMac() >> 24);
|
||||
doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str());
|
||||
doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_%s", ESP.getEfuseMac() >> 24, slug.c_str());
|
||||
doc["avty_t"] = "~/status";
|
||||
doc["stat_t"] = "~/max_distance";
|
||||
doc["cmd_t"] = "~/max_distance/set";
|
||||
doc["stat_t"] = "~/" + slug;
|
||||
doc["cmd_t"] = "~/" + slug + "/set";
|
||||
|
||||
char buffer[1200];
|
||||
serializeJson(doc, buffer);
|
||||
String discoveryTopic = "homeassistant/switch/espresense_" + ESPMAC + "/" + slug + "/config";
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (mqttClient.publish(discoveryTopic.c_str(), 0, true, buffer))
|
||||
return true;
|
||||
delay(50);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sendNumberDiscovery(String name)
|
||||
{
|
||||
auto slug = slugify(name);
|
||||
|
||||
DynamicJsonDocument doc(1200);
|
||||
commonDiscovery(&doc);
|
||||
doc["~"] = roomsTopic;
|
||||
doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str());
|
||||
doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_%s", ESP.getEfuseMac() >> 24, slug.c_str());
|
||||
doc["avty_t"] = "~/status";
|
||||
doc["stat_t"] = "~/" + slug;
|
||||
doc["cmd_t"] = "~/" + slug + "/set";
|
||||
doc["step"] = "0.01";
|
||||
|
||||
char buffer[1200];
|
||||
serializeJson(doc, buffer);
|
||||
String discoveryTopic = "homeassistant/number/espresense_" + ESPMAC + "/max_distance/config";
|
||||
String discoveryTopic = "homeassistant/number/espresense_" + ESPMAC + "/" + slug + "/config";
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue