[BT] Device tracker presence detection (#1548)

Add device tracker detection and automatic creation following HA discovery convention
So as to trigger the away state, add an offline status to the identified BLE trackers
This commit is contained in:
Florian authored and GitHub committed 2023-03-20 07:31:00 -05:00
1 parent 7bae999435
commit 85650a6204
5 files changed
+107 -31

No files matched your search

+63 -3
View File
@@ -98,6 +98,7 @@ void BTConfig_init() {
BTConfig.pubAdvData = pubBLEAdvData;
BTConfig.pubBeaconUuidForTopic = useBeaconUuidForTopic;
BTConfig.ignoreWBlist = false;
BTConfig.presenceAwayTimer = PresenceAwayTimer;
}
unsigned long timeBetweenConnect = 0;
@@ -124,6 +125,7 @@ void stateBTMeasures(bool start) {
jo["pubadvdata"] = BTConfig.pubAdvData;
jo["pubBeaconUuidForTopic"] = BTConfig.pubBeaconUuidForTopic;
jo["ignoreWBlist"] = BTConfig.ignoreWBlist;
jo["presenceawaytimer"] = BTConfig.presenceAwayTimer;
jo["btqblck"] = btQueueBlocked;
jo["btqsum"] = btQueueLengthSum;
jo["btqsnd"] = btQueueLengthCount;
@@ -143,6 +145,16 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) {
Config_update(BTdata, "bleconnect", BTConfig.bleConnect);
// Identify AdaptiveScan deactivation to pass to continuous mode or activation to come back to default settings
if (startup == false) {
if (BTdata.containsKey("hasspresence") && BTdata["hasspresence"] == false && BTConfig.presenceEnable == true) {
BTdata["adaptivescan"] = true;
# ifdef ZmqttDiscovery
// Remove discovered entities
eraseTopic("number", (char*)getUniqueId("presenceawaytimer", "").c_str());
# endif
} else if (BTdata.containsKey("hasspresence") && BTdata["hasspresence"] == true && BTConfig.presenceEnable == false) {
BTdata["adaptivescan"] = false;
}
if (BTdata.containsKey("adaptivescan") && BTdata["adaptivescan"] == false && BTConfig.adaptiveScan == true) {
BTdata["interval"] = MinTimeBtwScan;
BTdata["intervalacts"] = MinTimeBtwScan;
@@ -159,9 +171,12 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) {
}
}
Config_update(BTdata, "adaptivescan", BTConfig.adaptiveScan);
// Home Assistant presence message
Config_update(BTdata, "hasspresence", BTConfig.presenceEnable);
# ifdef ZmqttDiscovery
// Create discovery entities
btScanParametersDiscovery();
btPresenceParametersDiscovery();
# endif
// Time before before active scan
// Scan interval set
@@ -177,12 +192,12 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) {
Config_update(BTdata, "onlysensors", BTConfig.pubOnlySensors);
// publish devices which randomly change their MAC addresses
Config_update(BTdata, "randommacs", BTConfig.pubRandomMACs);
// Home Assistant presence message
Config_update(BTdata, "hasspresence", BTConfig.presenceEnable);
// Home Assistant presence message topic
Config_update(BTdata, "presenceTopic", BTConfig.presenceTopic);
// Home Assistant presence message use iBeacon UUID
Config_update(BTdata, "presenceUseBeaconUuid", BTConfig.presenceUseBeaconUuid);
// Timer to trigger a device state as offline if not seen
Config_update(BTdata, "presenceawaytimer", BTConfig.presenceAwayTimer);
// MinRSSI set
Config_update(BTdata, "minrssi", BTConfig.minRssi);
// Send undecoded device data
@@ -230,6 +245,7 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) {
jo["pubadvdata"] = BTConfig.pubAdvData;
jo["pubBeaconUuidForTopic"] = BTConfig.pubBeaconUuidForTopic;
jo["ignoreWBlist"] = BTConfig.ignoreWBlist;
jo["presenceawaytimer"] = BTConfig.presenceAwayTimer;
// Save config into NVS (non-volatile storage)
String conf = "";
serializeJson(jsonBuffer, conf);
@@ -408,11 +424,12 @@ void createOrUpdateDevice(const char* mac, uint8_t flags, int model, int mac_typ
device->connect = flags & device_flags_connect;
device->macType = mac_type;
device->sensorModel_id = model;
device->lastUpdate = millis();
devices.push_back(device);
newDevices++;
} else {
Log.trace(F("update %s" CR), mac);
device->lastUpdate = millis();
device->macType = mac_type;
if (flags & device_flags_isDisc) {
@@ -439,6 +456,30 @@ void createOrUpdateDevice(const char* mac, uint8_t flags, int model, int mac_typ
xSemaphoreGive(semaphoreCreateOrUpdateDevice);
}
void updateDevicesStatus() {
for (vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
unsigned long now = millis();
if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::NUT ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::MIBAND ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::TAGIT ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::TILE ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::ITAG ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::RUUVITAG_RAWV1 ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::RUUVITAG_RAWV2) { // We apply the offline status only for tracking device, can be extended further to all the devices
if ((p->lastUpdate != 0) && (p->lastUpdate < (now - BTConfig.presenceAwayTimer) && (now > BTConfig.presenceAwayTimer)) &&
(BTConfig.ignoreWBlist || ((!oneWhite || isWhite(p)) && !isBlack(p)))) { // Only if WBlist is disabled OR ((no white MAC OR this MAC is white) AND not a black listed MAC)) {
JsonObject BLEdata = getBTJsonObject();
BLEdata["id"] = p->macAdr;
BLEdata["state"] = "offline";
pubBT(BLEdata);
// We set the lastUpdate to 0 to avoid replublishing the offline state
p->lastUpdate = 0;
}
}
}
}
void dumpDevices() {
for (vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
@@ -449,6 +490,7 @@ void dumpDevices() {
Log.trace(F("isBlkL %d" CR), p->isBlkL);
Log.trace(F("connect %d" CR), p->connect);
Log.trace(F("sensorModel_id %d" CR), p->sensorModel_id);
Log.trace(F("LastUpdate %u" CR), p->lastUpdate);
}
}
@@ -646,6 +688,8 @@ void procBLETask(void* pvParameters) {
} else {
Log.trace(F("Filtered MAC device" CR));
}
if (BTConfig.presenceEnable)
updateDevicesStatus();
}
delete (advertisedDevice);
}
@@ -869,6 +913,7 @@ void setupBT() {
Log.notice(F("Active BLE scan interval: %d" CR), BTConfig.intervalActiveScan);
Log.notice(F("minrssi: %d" CR), -abs(BTConfig.minRssi));
Log.notice(F("Low Power Mode: %d" CR), lowpowermode);
Log.notice(F("Presence Away Timer: %d" CR), BTConfig.presenceAwayTimer);
atomic_init(&forceBTScan, 0); // in theory, we don't need this
atomic_init(&jsonBTBufferQueueNext, 0); // in theory, we don't need this
@@ -983,6 +1028,21 @@ void launchBTDiscovery(bool overrideDiscovery) {
model.c_str(), brand.c_str(), model_id.c_str(), macWOdots.c_str(), false,
stateClassMeasurement);
}
if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::NUT ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::MIBAND ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::TAGIT ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::TILE ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::ITAG ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::RUUVITAG_RAWV1 ||
p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::RUUVITAG_RAWV2) {
createDiscovery("device_tracker",
discovery_topic.c_str(), entity_name.c_str(), unique_id.c_str(),
will_Topic, "occupancy", "{% if value_json.rssi -%}home{%- else -%}not_home{%- endif %}",
"", "", "",
0, "", "", false, "",
model.c_str(), brand.c_str(), model_id.c_str(), macWOdots.c_str(), false,
stateClassNone);
}
}
}
} else {