From 4780e362ae4a376148ca2bbe2fb8c72b4b398459 Mon Sep 17 00:00:00 2001 From: Darrell Date: Sun, 13 Feb 2022 12:58:17 -0500 Subject: [PATCH] Add include exclude query filtering (#255) Co-authored-by: Stefan Knaak * Add Feature: Blacklist/Whitelist * MQTT room=* all rooms will set, added restart * Fixes: #58 #229 #152 --- .github/ISSUE_TEMPLATE/bug_report.md | 22 ++-- lib/BleFingerprint/BleFingerprint.cpp | 111 +++++++++++++----- lib/BleFingerprint/BleFingerprint.h | 12 +- lib/BleFingerprint/BleFingerprintCollection.h | 9 +- lib/BleFingerprint/util.h | 8 +- platformio.ini | 34 +++--- src/defaults.h | 4 + src/main.cpp | 75 +++++++++--- src/main.h | 37 +++++- 9 files changed, 221 insertions(+), 91 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7f4f851..d561621 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,25 +4,29 @@ about: Create a report to help us improve title: '' labels: bug assignees: '' - --- **Describe the bug** -A clear and concise description of what the bug is. -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Hardware Details** -If the bug is related to tracking a specific piece of hardware (e.g. iBeacon, phone, etc), please enter any relevant details about the hardware (such as firmware version, MAC address, etc). +A clear and concise description of what the bug is. If it relates to fingerprinting or presense please include the id you expect/monitor. **Config** -- [ ] Active Scan enabled -- [ ] Query enabled +- Version: +- Active scan enabled: +- Include filter: +- Exclude filter: +- Query filter: + +**Hardware** + +- Firmware flavor: +- Device (be specific): **Logs** + If possible, copy and paste any logs from the serial monitor from the time that you see the bug. **Screenshots** + If applicable, add screenshots to help explain your problem. diff --git a/lib/BleFingerprint/BleFingerprint.cpp b/lib/BleFingerprint/BleFingerprint.cpp index c0c776b..a8755a1 100644 --- a/lib/BleFingerprint/BleFingerprint.cpp +++ b/lib/BleFingerprint/BleFingerprint.cpp @@ -19,6 +19,61 @@ Sprintf("%02x%02x%02x%02x%02x%02x", nativeAddress[5], nativeAddress[4], nativeAddress[3], nativeAddress[2], nativeAddress[1], nativeAddress[0]); \ }) +bool prefixExists(String prefixes, String id) +{ + unsigned int start = 0; + unsigned int space = 0; + + while ((space = prefixes.indexOf(" ", start)) != -1) + { + if (space > start) + { + auto sub = prefixes.substring(start, space); +#ifdef VERBOSE + Serial.printf("Verbose | %-58sSUB\n", sub.c_str()); +#endif + if (sub == "*" || id.indexOf(sub) != -1) return true; + } + start = space + 1; + } + auto sub = prefixes.substring(start); + return (sub == "*" || id.indexOf(sub) != -1); +} + +bool BleFingerprint::shouldHide(String id) +{ + auto include = _parent->getInclude(); + if (include.length() > 0 && !prefixExists(include, id)) return true; + + auto exclude = _parent->getExclude(); + return (exclude.length() > 0 && prefixExists(exclude, id)); +} + +void BleFingerprint::setId(String newId, short int newIdType) +{ + if (newIdType < idType) return; + + hidden = shouldHide(newId); + + if (!allowQuery) + { + auto query = _parent->getQuery(); + if (prefixExists(query, newId)) + { + allowQuery = true; + qryAttempts = 0; + if (rssi < -60) + { + qryDelayMillis = 5000; + lastQryMillis = millis(); + } + } + } + + id = newId; + idType = newIdType; +} + String BleFingerprint::getMac() { return SMacf(address); } int BleFingerprint::get1mRssi() @@ -44,6 +99,9 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice) if (advertisedDevice->haveName()) name = String(advertisedDevice->getName().c_str()); + if (advertisedDevice->getAdvType() > 0) + connectable = true; + if (advertisedDevice->haveServiceUUID()) { @@ -83,10 +141,12 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice) if (!rmAsst) { rmAsst = true; - didQuery = false; - shouldQuery = true; - qryAttempts = 0; - qryDelayMillis = 3; + if (didQuery) + { + qryDelayMillis = 0; + qryAttempts = 0; + didQuery = false; + } } } else @@ -210,7 +270,6 @@ void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice) } else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10) { - shouldQuery = true; ignore = false; { String pid; @@ -338,7 +397,7 @@ void BleFingerprint::setInitial(int initalRssi, float initalDistance) bool BleFingerprint::report(JsonDocument *doc) { - if (ignore || (idType == 0 && !macPublic)) + if (ignore || (idType == 0 && !macPublic) || hidden) return false; if (reported || !hasValue) @@ -380,7 +439,7 @@ bool BleFingerprint::report(JsonDocument *doc) bool BleFingerprint::query() { - if (!shouldQuery || didQuery) return false; + if (!(allowQuery || rmAsst) || !connectable || didQuery) return false; if (rssi < -90) return false; auto now = millis(); @@ -390,58 +449,56 @@ bool BleFingerprint::query() bool success = false; + Serial.printf("%d Query | MAC: %s, ID: %-60s rssi %d\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), rssi); + NimBLEClient *pClient = NimBLEDevice::getClientListSize() ? NimBLEDevice::getClientByPeerAddress(address) : nullptr; if (!pClient) pClient = NimBLEDevice::getDisconnectedClient(); if (!pClient) pClient = NimBLEDevice::createClient(); - pClient->setConnectTimeout(5); + pClient->setConnectTimeout(1); if (pClient->connect(address)) { - auto sRmAst = pClient->getValue(roomAssistantService, rootAssistantCharacteristic); - if (!sRmAst.empty()) + if (rmAsst) { - setId(String("roomAssistant:") + kebabify(sRmAst).c_str(), ID_TYPE_RM_ASST); - Serial.printf("%d RmAst | MAC: %s, ID: %-60s %s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sRmAst.c_str()); - success = true; + auto sRmAst = pClient->getValue(roomAssistantService, rootAssistantCharacteristic); + if (!sRmAst.empty()) + { + setId(String("roomAssistant:") + kebabify(sRmAst).c_str(), ID_TYPE_RM_ASST); + Serial.printf("%d RmAst | MAC: %s, ID: %-60s %s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sRmAst.c_str()); + success = true; + } } - else + else if (allowQuery) { auto sMdl = pClient->getValue(deviceInformationService, modelChar); auto sName = pClient->getValue(genericAccessService, nameChar); if (!sName.empty() && !sMdl.empty() && sMdl.find(sName) == std::string::npos && sName.compare("Apple Watch") != 0) { - setId(String("name:") + kebabify(sName).c_str(), ID_TYPE_APPLE_NAME); Serial.printf("%d Name | MAC: %s, ID: %-60s %s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sName.c_str()); - success = !rmAsst; // Success only if we don't expect to get rootAssistantCharacteristic + setId(String("name:") + kebabify(sName).c_str(), ID_TYPE_APPLE_NAME); + success = true; } else if (!sMdl.empty()) { - setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_APPLE_MODEL); if (name.isEmpty()) name = sMdl.c_str(); Serial.printf("%d Model | MAC: %s, ID: %-60s %s\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sMdl.c_str()); - success = !rmAsst; // Success only if we don't expect to get rootAssistantCharacteristic + setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_APPLE_MODEL); + success = true; } else if (!sName.empty()) { if (name.isEmpty()) name = sName.c_str(); } } - - // 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(); } if (success) return true; + qryAttempts++; Serial.printf("%d QryErr| MAC: %s, ID: %-60s rssi %d, try %d, retry after %dms\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), rssi, qryAttempts, qryDelayMillis); - qryAttempts++; if (qryDelayMillis < 30000) - qryDelayMillis *= qryAttempts; + qryDelayMillis += (1000 * qryAttempts * qryAttempts); else qryDelayMillis = 30000; didQuery = false; diff --git a/lib/BleFingerprint/BleFingerprint.h b/lib/BleFingerprint/BleFingerprint.h index acf0de8..7745539 100644 --- a/lib/BleFingerprint/BleFingerprint.h +++ b/lib/BleFingerprint/BleFingerprint.h @@ -56,12 +56,7 @@ public: return getMac(); } - void setId(String newId, short int newIdType) - { - if (newIdType < idType) return; - id = newId; - idType = newIdType; - } + void setId(String newId, short int newIdType); String getMac(); int get1mRssi(); @@ -85,13 +80,14 @@ public: private: void fingerprint(BLEAdvertisedDevice *advertisedDevice); + bool shouldHide(String newId); BleFingerprintCollection *_parent; - bool hasValue = false, close = false, reported = false, macPublic = false, ignore = false, shouldQuery = false, didQuery = false, rmAsst = false; + bool hasValue = false, close = false, reported = false, macPublic = false, ignore = false, allowQuery = false, didQuery = false, rmAsst = false, hidden = false, connectable = false; NimBLEAddress address; String id, name, disc; short int idType = 0, rssi = -100, calRssi = NO_RSSI, mdRssi = NO_RSSI, asRssi = NO_RSSI, newest = NO_RSSI, recent = NO_RSSI, oldest = NO_RSSI; - int qryAttempts = 0, seenCount = 1, qryDelayMillis = 3; + int qryAttempts = 0, seenCount = 1, qryDelayMillis = 0; float raw = 0, lastReported = 0, temp = 0, humidity = 0; unsigned long firstSeenMillis, lastSeenMillis = 0, lastReportedMillis = 0, lastQryMillis = 0; uint16_t mv = 0; diff --git a/lib/BleFingerprint/BleFingerprintCollection.h b/lib/BleFingerprint/BleFingerprintCollection.h index 6f75471..c6dfa76 100644 --- a/lib/BleFingerprint/BleFingerprintCollection.h +++ b/lib/BleFingerprint/BleFingerprintCollection.h @@ -20,18 +20,24 @@ public: void cleanupOldFingerprints(); std::list getCopy(); void setDisable(bool disable) { _disable = disable; } - void setParams(int rssiRef, int forgetMs, float skipDistance, int skipMs, float maxDistance) + void setParams(int rssiRef, int forgetMs, float skipDistance, int skipMs, float maxDistance, String include, String exclude, String query) { _refRssi = rssiRef; _forgetMs = forgetMs; _skipDistance = skipDistance; _skipMs = skipMs; _maxDistance = maxDistance; + _include = include; + _exclude = exclude; + _query = query; } int getSkipMs() { return _skipMs; } float getSkipDistance() { return _skipDistance; } int getRefRssi() { return _refRssi; } float getMaxDistance() { return _maxDistance; } + String getInclude() { return _include; } + String getExclude() { return _exclude; } + String getQuery() { return _query; } private: bool _disable = false; @@ -39,6 +45,7 @@ private: float _maxDistance, _skipDistance; int _refRssi, _forgetMs, _skipMs; unsigned long lastCleanup = 0; + String _include, _exclude, _query; SemaphoreHandle_t fingerprintSemaphore; std::list fingerprints; diff --git a/lib/BleFingerprint/util.h b/lib/BleFingerprint/util.h index 2a0ebf4..bd6ac35 100644 --- a/lib/BleFingerprint/util.h +++ b/lib/BleFingerprint/util.h @@ -21,13 +21,13 @@ static BLEUUID meaterService(0xa75cc7fc, 0xc956, 0x488f, 0xac2a2dbc08b63a04); static BLEUUID meaterCharacteristic(0x7edda774, 0x045e, 0x4bbf, 0x909b45d1991a2876); 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 deviceInformationService(uint16_t(0x180A)); static BLEUUID modelChar(uint16_t(0x2A24)); -static BLEUUID hwRevChar(uint16_t(0x2A27)); static BLEUUID fwRevChar(uint16_t(0x2A26)); +static BLEUUID hwRevChar(uint16_t(0x2A27)); +static BLEUUID manufChar(uint16_t(0x2A29)); static int median_of_3(int a, int b, int c) { diff --git a/platformio.ini b/platformio.ini index ad2021a..efd5ef8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,7 @@ default_envs = esp32 [common_env_data] platform = espressif32@3.5 framework = arduino -lib_deps_external = +lib_deps_external = haimoz/SoftFilters@^0.1.0 marvinroger/AsyncMqttClient@^0.9.0 bblanchon/ArduinoJson@^6.17.3 @@ -37,25 +37,23 @@ lib_deps_external = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = - ${common_env_data.lib_deps_external} +lib_deps = ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 upload_speed = 1500000 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D FIRMWARE='"esp32"' [env:adafruit-huzzah32] platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = - ${common_env_data.lib_deps_external} +lib_deps = ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D FIRMWARE='"adafruit-huzzah32"' -D HUZZAH32 @@ -63,13 +61,12 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = - ${common_env_data.lib_deps_external} +lib_deps = ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 upload_speed = 1500000 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D CORE_DEBUG_LEVEL=2 -D FIRMWARE='"esp32-verbose"' -D VERBOSE @@ -78,13 +75,13 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = m5stick-c -lib_deps = +lib_deps = m5stack/M5StickC@^0.2.0 ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D M5STICK -D FIRMWARE='"m5stickc"' @@ -92,13 +89,13 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = m5stick-c -lib_deps = +lib_deps = m5stack/M5StickCPlus@^0.0.2 ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D M5STICK -D PLUS -D FIRMWARE='"m5stickc-plus"' @@ -107,14 +104,14 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = m5stack-atom -lib_deps = +lib_deps = fastled/FastLED@^3.4.0 m5stack/m5atom@^0.0.5 ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 monitor_filters = esp32_exception_decoder -build_flags = +build_flags = -D M5ATOM -D MATRIX -D FIRMWARE='"m5atom-matrix"' @@ -123,11 +120,10 @@ build_flags = platform = ${common_env_data.platform} framework = ${common_env_data.framework} board = esp32dev -lib_deps = - ${common_env_data.lib_deps_external} +lib_deps = ${common_env_data.lib_deps_external} board_build.partitions = partitions_singleapp.csv monitor_speed = 115200 -build_flags = +build_flags = -D MACCHINA_A0 -D FIRMWARE='"macchina-a0"' diff --git a/src/defaults.h b/src/defaults.h index 67e40f9..045f9df 100644 --- a/src/defaults.h +++ b/src/defaults.h @@ -30,6 +30,10 @@ //Define the base topic for room detection. Usually "espresense" #define CHANNEL String("espresense") +#define DEFAULT_QUERY "" +#define DEFAULT_INCLUDE "" +#define DEFAULT_EXCLUDE "" + #define BLE_SCAN_INTERVAL 40 // Used to determine antenna sharing between Bluetooth and WiFi. Do not modify unless you are confident you know what you're doing #define BLE_SCAN_WINDOW 30 // Used to determine antenna sharing between Bluetooth and WiFi. Do not modify unless you are confident you know what you're doing diff --git a/src/main.cpp b/src/main.cpp index 84b7d32..c2c2bae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,7 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total if (discovery && !sentDiscovery) { - if (sendDiscoveryConnectivity() && sendDiscoveryUptime() && sendDiscoveryFreeMem() && sendSwitchDiscovery("Status LED", "config") && sendNumberDiscovery("Max Distance", "config") && sendSwitchDiscovery("Active Scan", "config") && sendSwitchDiscovery("Query", "config") && sendDiscoveryMotion() + if (sendDiscoveryConnectivity() && sendDiscoveryUptime() && sendDiscoveryFreeMem() && sendButtonDiscovery("Restart", "diagnostic") && sendSwitchDiscovery("Status LED", "config") && sendNumberDiscovery("Max Distance", "config") && sendSwitchDiscovery("Active Scan", "config") && sendDeleteDiscovery("switch", "Query") && sendDiscoveryMotion() #ifdef SENSORS && sendDiscoveryHumidity() && sendDiscoveryTemperature() && sendDiscoveryLux() && sendDiscoveryBME280Temperature() && sendDiscoveryBME280Humidity() && sendDiscoveryBME280Pressure() && sendDiscoveryTSL2561Lux() #endif @@ -115,28 +115,29 @@ void connectToWifi() room = WiFiSettings.string("room", ESPMAC, "Room"); WiFiSettings.heading("MQTT Connection"); - mqttHost = WiFiSettings.string("mqtt_host", DEFAULT_MQTT_HOST, "Server"); mqttPort = WiFiSettings.integer("mqtt_port", DEFAULT_MQTT_PORT, "Port"); mqttUser = WiFiSettings.string("mqtt_user", DEFAULT_MQTT_USER, "Username"); mqttPass = WiFiSettings.string("mqtt_pass", DEFAULT_MQTT_PASSWORD, "Password"); WiFiSettings.heading("Preferences"); - statusLed = WiFiSettings.checkbox("status_led", true, "Status LED"); Display.setStatusLed(statusLed); - 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", 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"); - WiFiSettings.heading("Calibration"); + WiFiSettings.heading("Filtering"); + query = WiFiSettings.string("query", DEFAULT_QUERY, "Query device ids for characteristics (eg. apple:1005:9-26)"); + if (query == "1") query = "apple:10"; // This is to keep query=true doing the same thing as older firmwares + include = WiFiSettings.string("include", DEFAULT_INCLUDE, "If set will only send matching to mqtt (eg. apple:iphone10-6 apple:iphone13-2)"); + exclude = WiFiSettings.string("exclude", DEFAULT_EXCLUDE, "Exclude sensing these ids to mqtt (eg. exp:20 apple:iphone10-6)"); + WiFiSettings.heading("Calibration"); maxDistance = WiFiSettings.floating("max_dist", 0, 100, DEFAULT_MAX_DISTANCE, "Maximum distance to report (in meters)"); forgetMs = WiFiSettings.integer("forget_ms", 0, 3000000, DEFAULT_FORGET_MS, "Forget beacon if not seen for (in miliiseconds)"); skipDistance = WiFiSettings.floating("skip_dist", 0, 10, DEFAULT_SKIP_DISTANCE, "Update mqtt if beacon has moved more than this distance since last report (in meters)"); @@ -144,7 +145,6 @@ void connectToWifi() refRssi = WiFiSettings.integer("ref_rssi", -100, 100, DEFAULT_REF_RSSI, "Rssi expected from a 0dBm transmitter at 1 meter"); WiFiSettings.heading("Additional Sensors"); - pirPin = WiFiSettings.integer("pir_pin", 0, "PIR motion pin (0 for disable)"); radarPin = WiFiSettings.integer("radar_pin", 0, "Radar motion pin (0 for disable)"); #ifdef SENSORS @@ -222,19 +222,26 @@ void connectToWifi() Serial.println(TSL2561_I2c + " on bus " + TSL2561_I2c_Bus); Serial.println(BH1750_I2c); #endif - + Serial.print("Query: "); + Serial.println(query); + Serial.print("Include: "); + Serial.println(include); + Serial.print("Exclude: "); + Serial.println(exclude); + localIp = WiFi.localIP().toString(); id = slugify(room); roomsTopic = CHANNEL + "/rooms/" + id; statusTopic = roomsTopic + "/status"; teleTopic = roomsTopic + "/telemetry"; - subTopic = roomsTopic + "/+/set"; + setTopic = roomsTopic + "/+/set"; } void onMqttConnect(bool sessionPresent) { xTimerStop(reconnectTimer, 0); - mqttClient.subscribe(subTopic.c_str(), 2); + mqttClient.subscribe("espresense/rooms/*/+/set", 1); + mqttClient.subscribe(setTopic.c_str(), 1); Display.connected(true, true); } @@ -255,33 +262,56 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties String top = String(topic); String pay = String(new_payload); - if (top == roomsTopic + "/max_distance/set") + + auto setPos = top.lastIndexOf("/set"); + if (setPos <= 1) return; + auto commandPos = top.lastIndexOf("/", setPos - 1); + if (commandPos < 0) return; + auto command = top.substring(commandPos + 1, setPos); + + if (command == "max_distance") { maxDistance = pay.toFloat(); spurt("/max_dist", pay); online = false; } - else if (top == roomsTopic + "/active_scan/set") + else if (command == "active_scan") { activeScan = pay == "ON"; spurt("/active_scan", String(activeScan)); online = false; } - else if (top == roomsTopic + "/query/set") + else if (command == "query") { - allowQuery = pay == "ON"; - spurt("/query", String(allowQuery)); + query = pay; + spurt("/query", String(query)); online = false; } - else if (top == roomsTopic + "/status_led/set") + else if (command == "include") + { + include = pay; + spurt("/include", String(include)); + online = false; + } + else if (command == "exclude") + { + exclude = pay; + spurt("/exclude", String(exclude)); + online = false; + } + else if (command == "status_led") { statusLed = pay == "ON"; spurt("/status_led", String(statusLed)); Display.setStatusLed(statusLed); online = false; } + else if (command == "restart") + { + ESP.restart(); + } - fingerprints.setParams(refRssi, forgetMs, skipDistance, skipMs, maxDistance); + fingerprints.setParams(refRssi, forgetMs, skipDistance, skipMs, maxDistance, include, exclude, query); } void reconnect(TimerHandle_t xTimer) @@ -352,7 +382,7 @@ bool reportDevice(BleFingerprint *f) void scanForDevices(void *parameter) { - fingerprints.setParams(refRssi, forgetMs, skipDistance, skipMs, maxDistance); + fingerprints.setParams(refRssi, forgetMs, skipDistance, skipMs, maxDistance, include, exclude, query); BLEDevice::init(Stdprintf("ESPresense-%06" PRIx64, ESP.getEfuseMac() >> 24)); for (esp_ble_power_type_t i = ESP_BLE_PWR_TYPE_CONN_HDL0; i <= ESP_BLE_PWR_TYPE_CONN_HDL8; i = esp_ble_power_type_t((int)i + 1)) NimBLEDevice::setPower(ESP_PWR_LVL_P9, i); @@ -380,18 +410,25 @@ void scanForDevices(void *parameter) sendTelemetry(totalSeen, totalFpSeen, totalFpQueried, totalFpReported); - if (allowQuery) + if (millis() - lastQueryMillis > 3000) { + auto started = millis(); for (auto it = seen.begin(); it != seen.end(); ++it) { auto f = (*it); if (f->query()) totalFpQueried++; + + if (millis() - started > 3000) break; } if (!pBLEScan->isScanning()) + { if (!pBLEScan->start(0, nullptr, false)) log_e("Error re-starting continuous ble scan"); + + lastQueryMillis = millis(); // If we stopped scanning, don't query for 3 seconds in order for us to catch any missed broadcasts + } } for (auto it = seen.begin(); it != seen.end(); ++it) diff --git a/src/main.h b/src/main.h index 0ad9d6e..a6f311d 100644 --- a/src/main.h +++ b/src/main.h @@ -67,7 +67,7 @@ TaskHandle_t scannerTask; bool updateInProgress = false; String localIp; -unsigned long lastTeleMillis; +unsigned long lastTeleMillis, lastQueryMillis; int reconnectTries = 0; int teleFails = 0; bool online = false; // Have we successfully sent status=online @@ -82,10 +82,13 @@ String id; String statusTopic; String teleTopic; String roomsTopic; -String subTopic; +String setTopic; +String query; +String include; +String exclude; bool autoUpdate, otaUpdate; bool discovery, statusLed; -bool activeScan, allowQuery; +bool activeScan; bool publishTele; bool publishRooms; bool publishDevices; @@ -335,7 +338,7 @@ bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_ bool sendOnline() { - 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 + "/status_led").c_str(), 0, 1, String(statusLed ? "ON" : "OFF").c_str()) && mqttClient.publish((roomsTopic + "/active_scan").c_str(), 0, 1, String(activeScan ? "ON" : "OFF").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, query.c_str()) && mqttClient.publish((roomsTopic + "/include").c_str(), 0, 1, include.c_str()) && mqttClient.publish((roomsTopic + "/exclude").c_str(), 0, 1, exclude.c_str()) && mqttClient.publish((roomsTopic + "/status_led").c_str(), 0, 1, String(statusLed ? "ON" : "OFF").c_str()) && mqttClient.publish((roomsTopic + "/active_scan").c_str(), 0, 1, String(activeScan ? "ON" : "OFF").c_str()); } void commonDiscovery(JsonDocument *doc) @@ -613,6 +616,25 @@ bool sendDiscoveryTSL2561Lux() } #endif +bool sendButtonDiscovery(String name, String entityCategory) +{ + 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["entity_category"] = entityCategory; + + char buffer[1200]; + serializeJson(doc, buffer); + String discoveryTopic = "homeassistant/button/espresense_" + ESPMAC + "/" + slug + "/config"; + return pub(discoveryTopic.c_str(), 0, true, buffer); +} bool sendSwitchDiscovery(String name, String entityCategory) { @@ -634,6 +656,13 @@ bool sendSwitchDiscovery(String name, String entityCategory) return pub(discoveryTopic.c_str(), 0, true, buffer); } +bool sendDeleteDiscovery(String domain, String name) +{ + auto slug = slugify(name); + String discoveryTopic = "homeassistant/" + domain + "/espresense_" + ESPMAC + "/" + slug + "/config"; + return pub(discoveryTopic.c_str(), 0, false, ""); +} + bool sendNumberDiscovery(String name, String entityCategory) { auto slug = slugify(name);