[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:
1 parent
7bae999435
commit
85650a6204
5 files changed
+107
-31
No files matched your search
+63
-3
@@ -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 {
|
||||
|
||||
+19
-1
@@ -295,6 +295,8 @@ void createDiscovery(const char* sensor_type,
|
||||
}
|
||||
if (payload_off[0])
|
||||
sensor["pl_off"] = payload_off; //payload_off
|
||||
if (strcmp(sensor_type, "device_tracker") == 0)
|
||||
sensor["source_type "] = "bluetooth_le"; // payload_install for update
|
||||
if (off_delay != 0)
|
||||
sensor["off_delay"] = off_delay; //off_delay
|
||||
if (payload_available[0])
|
||||
@@ -386,8 +388,22 @@ void eraseTopic(const char* sensor_type, const char* unique_id) {
|
||||
}
|
||||
|
||||
# ifdef ZgatewayBT
|
||||
void btPresenceParametersDiscovery() {
|
||||
if (BTConfig.presenceEnable) {
|
||||
createDiscovery("number", //set Type
|
||||
subjectBTtoMQTT, "BT: Presence detection timer", (char*)getUniqueId("presenceawaytimer", "").c_str(), //set state_topic,name,uniqueId
|
||||
will_Topic, "", "{{ value_json.presenceawaytimer/60000 }}", //set availability_topic,device_class,value_template,
|
||||
"{\"presenceawaytimer\":{{value*60000}},\"save\":true}", "", "min", //set,payload_on,payload_off,unit_of_meas,
|
||||
0, //set off_delay
|
||||
Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoBTset, //set,payload_available,payload_not available ,is a gateway entity, command topic
|
||||
"", "", "", "", false, // device name, device manufacturer, device model, device ID, retain,
|
||||
stateClassNone //State Class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void btScanParametersDiscovery() {
|
||||
if (BTConfig.adaptiveScan == false) {
|
||||
if (!BTConfig.adaptiveScan) {
|
||||
createDiscovery("number", //set Type
|
||||
subjectBTtoMQTT, "BT: Interval between scans", (char*)getUniqueId("interval", "").c_str(), //set state_topic,name,uniqueId
|
||||
will_Topic, "", "{{ value_json.interval/1000 }}", //set availability_topic,device_class,value_template,
|
||||
@@ -1105,6 +1121,8 @@ void pubMqttDiscovery() {
|
||||
|
||||
btScanParametersDiscovery();
|
||||
|
||||
btPresenceParametersDiscovery();
|
||||
|
||||
createDiscovery("switch", //set Type
|
||||
subjectBTtoMQTT, "BT: Publish HASS presence", (char*)getUniqueId("hasspresence", "").c_str(), //set state_topic,name,uniqueId
|
||||
will_Topic, "", "{{ value_json.hasspresence }}", //set availability_topic,device_class,value_template,
|
||||
|
||||
@@ -89,6 +89,9 @@ extern int btQueueLengthCount;
|
||||
#ifndef TimeBtwConnect
|
||||
# define TimeBtwConnect 3600000 //define default time between BLE connection attempt (not used for immediate actions); in milliseconds
|
||||
#endif
|
||||
#ifndef PresenceAwayTimer
|
||||
# define PresenceAwayTimer 120000 //define the time between Offline Status update for the sensors
|
||||
#endif
|
||||
|
||||
#ifndef BLEScanDuplicateCacheSize
|
||||
# define BLEScanDuplicateCacheSize 200
|
||||
@@ -160,6 +163,7 @@ struct BTConfig_s {
|
||||
bool pubAdvData; // Publish advertisement data
|
||||
bool pubBeaconUuidForTopic; // Use iBeacon UUID as topic, instead of sender (random) MAC address
|
||||
bool ignoreWBlist; // Disable Whitelist & Blacklist
|
||||
unsigned long presenceAwayTimer; //Timer that trigger a tracker state as offline if not seen
|
||||
};
|
||||
|
||||
// Global struct to store live BT configuration data
|
||||
@@ -209,6 +213,7 @@ struct BLEdevice {
|
||||
bool isBlkL;
|
||||
bool connect;
|
||||
int sensorModel_id;
|
||||
unsigned long lastUpdate;
|
||||
};
|
||||
|
||||
class BLEconectable {
|
||||
|
||||
Reference in new issue
Block a user