diff --git a/docs/use/ble.md b/docs/use/ble.md index 129fb91c41..cb475674f4 100644 --- a/docs/use/ble.md +++ b/docs/use/ble.md @@ -26,6 +26,16 @@ With the ability to monitor and analyze data such as temperature, humidity, mois Support the project by purchasing the [Theengs plug](https://shop.theengs.io/products/theengs-plug-smart-plug-ble-gateway-and-energy-consumption) The plug is available in North America only, other regions are planned. +## Disable or enable the BLE gateway (default: true, available with HA discovery) + +If you want to deactivate the BLE gateway: + +`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoBT/config -m '{"enabled":false}'` + +If you want to activate the BLE gateway: + +`mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoBT/config -m '{"enabled":true}'` + ## Receiving signals from [compatible BLE sensors](https://decoder.theengs.io/devices/devices_by_brand.html) to publish it to an MQTT broker. To receive data from BLE sensors you can use an ESP32-based device with a programming USB port or use a Serial adapter. diff --git a/main/ZgatewayBLEConnect.ino b/main/ZgatewayBLEConnect.ino index 813ef2b510..52a24bfaa3 100644 --- a/main/ZgatewayBLEConnect.ino +++ b/main/ZgatewayBLEConnect.ino @@ -6,7 +6,7 @@ # include "ZgatewayBLEConnect.h" # define convertTemp_CtoF(c) ((c * 1.8) + 32) -extern bool ProcessLock; +extern bool BTProcessLock; extern std::vector devices; NimBLERemoteCharacteristic* zBLEConnect::getCharacteristic(const NimBLEUUID& service, @@ -135,7 +135,7 @@ void LYWSD03MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pD if (m_taskHandle == nullptr) { return; // unexpected notification } - if (!ProcessLock) { + if (!BTProcessLock) { Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str()); if (length == 5) { @@ -167,7 +167,7 @@ void LYWSD03MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pD return; } } else { - Log.trace(F("Callback process canceled by processLock" CR)); + Log.trace(F("Callback process canceled by BTProcessLock" CR)); } xTaskNotifyGive(m_taskHandle); @@ -199,7 +199,7 @@ void DT24_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, s return; // unexpected notification } - if (!ProcessLock) { + if (!BTProcessLock) { Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str()); if (length == 20) { m_data.assign(pData, pData + length); @@ -232,7 +232,7 @@ void DT24_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, s return; } } else { - Log.trace(F("Callback process canceled by processLock" CR)); + Log.trace(F("Callback process canceled by BTProcessLock" CR)); } xTaskNotifyGive(m_taskHandle); @@ -264,7 +264,7 @@ void BM2_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, si return; // unexpected notification } - if (!ProcessLock) { + if (!BTProcessLock) { Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str()); if (length == 16) { Log.trace(F("Device identified creating BLE buffer" CR)); @@ -309,7 +309,7 @@ void BM2_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, si return; } } else { - Log.trace(F("Callback process canceled by processLock" CR)); + Log.trace(F("Callback process canceled by BTProcessLock" CR)); } xTaskNotifyGive(m_taskHandle); @@ -375,7 +375,7 @@ void XMWSDJ04MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* p if (m_taskHandle == nullptr) { return; // unexpected notification } - if (!ProcessLock) { + if (!BTProcessLock) { Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str()); if (length == 6) { @@ -399,7 +399,7 @@ void XMWSDJ04MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* p return; } } else { - Log.trace(F("Callback process canceled by processLock" CR)); + Log.trace(F("Callback process canceled by BTProcessLock" CR)); } xTaskNotifyGive(m_taskHandle); @@ -430,7 +430,7 @@ void SBS1_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, s if (m_taskHandle == nullptr) { return; // unexpected notification } - if (!ProcessLock) { + if (!BTProcessLock) { Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str()); if (length) { @@ -440,7 +440,7 @@ void SBS1_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, s return; } } else { - Log.trace(F("Callback process canceled by processLock" CR)); + Log.trace(F("Callback process canceled by BTProcessLock" CR)); } xTaskNotifyGive(m_taskHandle); diff --git a/main/ZgatewayBT.ino b/main/ZgatewayBT.ino index 044baa64a8..e3e057c95b 100644 --- a/main/ZgatewayBT.ino +++ b/main/ZgatewayBT.ino @@ -104,6 +104,7 @@ void BTConfig_init() { BTConfig.presenceAwayTimer = PresenceAwayTimer; BTConfig.movingTimer = MovingTimer; BTConfig.forcePassiveScan = false; + BTConfig.enabled = true; } unsigned long timeBetweenConnect = 0; @@ -133,6 +134,7 @@ String stateBTMeasures(bool start) { jo["presenceawaytimer"] = BTConfig.presenceAwayTimer; jo["movingtimer"] = BTConfig.movingTimer; jo["forcepassivescan"] = BTConfig.forcePassiveScan; + jo["enabled"] = BTConfig.enabled; jo["bletaskstack"] = uxTaskGetStackHighWaterMark(xProcBLETaskHandle); jo["blecoretaskstack"] = uxTaskGetStackHighWaterMark(xCoreTaskHandle); @@ -178,6 +180,13 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) { eraseTopic("number", (char*)getUniqueId("intervalacts", "").c_str()); # endif } + // Identify if the gateway is enabled or not and stop start accordingly + if (BTdata.containsKey("enabled") && BTdata["enabled"] == false && BTConfig.enabled == true) { + stopProcessing(); + } else if (BTdata.containsKey("enabled") && BTdata["enabled"] == true && BTConfig.enabled == false) { + BTProcessLock = false; + setupBTTasksAndBLE(); + } } Config_update(BTdata, "adaptivescan", BTConfig.adaptiveScan); // Home Assistant presence message @@ -225,6 +234,8 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) { Config_update(BTdata, "pubBeaconUuidForTopic", BTConfig.pubBeaconUuidForTopic); // Disable Whitelist & Blacklist Config_update(BTdata, "ignoreWBlist", (BTConfig.ignoreWBlist)); + // Enable or disable the BT gateway + Config_update(BTdata, "enabled", BTConfig.enabled); stateBTMeasures(startup); @@ -266,6 +277,7 @@ void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) { jo["presenceawaytimer"] = BTConfig.presenceAwayTimer; jo["movingtimer"] = BTConfig.movingTimer; jo["forcepassivescan"] = BTConfig.forcePassiveScan; + jo["enabled"] = BTConfig.enabled; // Save config into NVS (non-volatile storage) String conf = ""; serializeJson(jsonBuffer, conf); @@ -580,7 +592,7 @@ void procBLETask(void* pvParameters) { xQueueReceive(BLEQueue, &advertisedDevice, portMAX_DELAY); // Feed the watchdog //esp_task_wdt_reset(); - if (!ProcessLock) { + if (!BTProcessLock) { Log.trace(F("Creating BLE buffer" CR)); StaticJsonDocument BLEdataBuffer; JsonObject BLEdata = BLEdataBuffer.to(); @@ -675,7 +687,7 @@ void BLEscan() { * Connect to BLE devices and initiate the callbacks with a service/characteristic request */ void BLEconnect() { - if (!ProcessLock) { + if (!BTProcessLock) { Log.notice(F("BLE Connect begin" CR)); do { for (vector::iterator it = devices.begin(); it != devices.end(); ++it) { @@ -742,33 +754,39 @@ void BLEconnect() { } void stopProcessing() { - ProcessLock = true; - // We stop the scan - Log.notice(F("Stopping BLE scan" CR)); - BLEScan* pBLEScan = BLEDevice::getScan(); - if (pBLEScan->isScanning()) { - pBLEScan->stop(); + if (BTConfig.enabled) { + BTProcessLock = true; + // We stop the scan + Log.notice(F("Stopping BLE scan" CR)); + BLEScan* pBLEScan = BLEDevice::getScan(); + if (pBLEScan->isScanning()) { + pBLEScan->stop(); + } + + if (xSemaphoreTake(semaphoreBLEOperation, pdMS_TO_TICKS(5000)) == pdTRUE) { + Log.notice(F("Stopping BLE tasks" CR)); + //Suspending, deleting tasks and stopping BT to free memory + vTaskSuspend(xCoreTaskHandle); + vTaskDelete(xCoreTaskHandle); + vTaskSuspend(xProcBLETaskHandle); + vTaskDelete(xProcBLETaskHandle); + xSemaphoreGive(semaphoreBLEOperation); + } } - Log.notice(F("Stopping BLE tasks" CR)); - //Suspending, deleting tasks and stopping BT to free memory - vTaskSuspend(xCoreTaskHandle); - vTaskDelete(xCoreTaskHandle); - vTaskSuspend(xProcBLETaskHandle); - vTaskDelete(xProcBLETaskHandle); Log.notice(F("BLE gateway stopped %T, free heap: %d" CR), ESP.getFreeHeap()); } void coreTask(void* pvParameters) { while (true) { - if (!ProcessLock) { + if (!BTProcessLock) { int n = 0; - while (client.state() != 0 && n <= InitialMQTTConnectionTimeout && !ProcessLock) { + while (client.state() != 0 && n <= InitialMQTTConnectionTimeout && !BTProcessLock) { n++; delay(1000); } if (client.state() != 0) { Log.warning(F("MQTT client disconnected no BLE scan" CR)); - } else if (!ProcessLock) { + } else if (!BTProcessLock) { if (xSemaphoreTake(semaphoreBLEOperation, pdMS_TO_TICKS(30000)) == pdTRUE) { BLEscan(); // Launching a connect every TimeBtwConnect @@ -813,6 +831,7 @@ void deepSleep(uint64_t time_in_us) { Log.trace(F("Deactivating ESP32 components" CR)); stopProcessing(); + ProcessLock = true; // Ignore the deprecated warning, this call is necessary here. # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -891,6 +910,7 @@ void setupBT() { Log.notice(F("Presence Away Timer: %d" CR), BTConfig.presenceAwayTimer); Log.notice(F("Moving Timer: %d" CR), BTConfig.movingTimer); Log.notice(F("Force passive scan: %T" CR), BTConfig.forcePassiveScan); + Log.notice(F("Enabled BLE: %T" CR), BTConfig.enabled); atomic_init(&forceBTScan, 0); // in theory, we don't need this @@ -901,10 +921,12 @@ void setupBT() { xSemaphoreGive(semaphoreBLEOperation); BLEQueue = xQueueCreate(QueueSize, sizeof(NimBLEAdvertisedDevice*)); - - setupBTTasksAndBLE(); - - Log.trace(F("ZgatewayBT multicore ESP32 setup done " CR)); + if (BTConfig.enabled) { + setupBTTasksAndBLE(); + Log.notice(F("ZgatewayBT multicore ESP32 setup done" CR)); + } else { + Log.notice(F("ZgatewayBT multicore ESP32 setup disabled" CR)); + } } bool BTtoMQTT() { // for on demand BLE scans @@ -1230,7 +1252,7 @@ void hass_presence(JsonObject& HomePresence) { } void BTforceScan() { - if (!ProcessLock) { + if (!BTProcessLock) { BTtoMQTT(); Log.trace(F("Scan done" CR)); if (BTConfig.bleConnect) @@ -1243,7 +1265,7 @@ void BTforceScan() { void immediateBTAction(void* pvParameters) { if (BLEactions.size()) { // Immediate action; we need to prevent the normal connection action and stop scanning - ProcessLock = true; + BTProcessLock = true; NimBLEScan* pScan = NimBLEDevice::getScan(); if (pScan->isScanning()) { pScan->stop(); @@ -1261,7 +1283,7 @@ void immediateBTAction(void* pvParameters) { std::swap(BLEactions, act_swap); // Unlock here to allow the action to be performed - ProcessLock = false; + BTProcessLock = false; BLEconnect(); // back to normal std::swap(devices, dev_swap); @@ -1282,7 +1304,7 @@ void immediateBTAction(void* pvParameters) { buildTopicFromId(BLEdata, subjectBTtoMQTT); handleJsonEnqueue(BLEdata, QueueSemaphoreTimeOutTask); BLEactions.pop_back(); - ProcessLock = false; + BTProcessLock = false; } } vTaskDelete(NULL); diff --git a/main/ZmqttDiscovery.ino b/main/ZmqttDiscovery.ino index 9817097fab..bd88978e9f 100644 --- a/main/ZmqttDiscovery.ino +++ b/main/ZmqttDiscovery.ino @@ -1216,6 +1216,16 @@ void pubMqttDiscovery() { stateClassNone, //State Class "false", "true" //state_off, state_on ); + createDiscovery("switch", //set Type + subjectBTtoMQTT, "BT: Enabled", (char*)getUniqueId("enabled", "").c_str(), //set state_topic,name,uniqueId + will_Topic, "", "{{ value_json.enabled }}", //set availability_topic,device_class,value_template, + "{\"enabled\":true,\"save\":true}", "{\"enabled\":false,\"save\":true}", "", //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 MAC, retain + stateClassNone, //State Class + "false", "true" //state_off, state_on + ); # define EntitiesCount 8 const char* obsoleteEntities[EntitiesCount][2] = { diff --git a/main/config_BT.h b/main/config_BT.h index 16ee27c1fc..57ffd9af60 100644 --- a/main/config_BT.h +++ b/main/config_BT.h @@ -166,6 +166,7 @@ struct BTConfig_s { unsigned long presenceAwayTimer; //Timer that trigger a tracker/PIR state as offline/off if not seen unsigned long movingTimer; //Timer that trigger a moving sensor state as offline if not seen bool forcePassiveScan; //Force passive scan + bool enabled; // Enable or disable the BT gateway }; // Global struct to store live BT configuration data diff --git a/main/main.ino b/main/main.ino index 667f8c30cd..f5c1c70135 100644 --- a/main/main.ino +++ b/main/main.ino @@ -273,7 +273,9 @@ static String ota_server_cert = ""; # include # include -bool ProcessLock = true; // Process lock when we want to use a critical function like OTA for example, at start to true so as to wait for critical functions to be performed before BLE start +bool BTProcessLock = true; // Process lock when we want to use a critical function like OTA for example, at start to true so as to wait for critical functions to be performed before BLE start +bool ProcessLock = false; // Process lock when we want to use a critical function like OTA for example + # if !defined(NO_INT_TEMP_READING) // ESP32 internal temperature reading # include @@ -1398,8 +1400,11 @@ void setOTA() { ErrorIndicatorON(); SendReceiveIndicatorON(); last_ota_activity_millis = millis(); -# if defined(ZgatewayBT) && defined(ESP32) +# ifdef ESP32 + ProcessLock = true; +# ifdef ZgatewayBT stopProcessing(); +# endif # endif lpDisplayPrint("OTA in progress"); }); @@ -2099,7 +2104,7 @@ void loop() { checkForUpdates(); # endif # ifdef ZgatewayBT - ProcessLock = false; // Release BLE processes at start + BTProcessLock = !BTConfig.enabled; // Release BLE processes at start if enabled # endif } @@ -2744,11 +2749,12 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { Log.error(F("Invalid URL" CR)); return; } -# if defined(ZgatewayBT) - stopProcessing(); -# endif # ifdef ESP32 + ProcessLock = true; //esp_task_wdt_delete(NULL); // Stop task watchdog during update +# ifdef ZgatewayBT + stopProcessing(); +# endif # endif Log.warning(F("Starting firmware update" CR)); @@ -2885,8 +2891,11 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding } # endif if (SYSdata.containsKey("wifi_ssid") && SYSdata.containsKey("wifi_pass")) { -# if defined(ZgatewayBT) && defined(ESP32) +# ifdef ESP32 + ProcessLock = true; +# ifdef ZgatewayBT stopProcessing(); +# endif # endif String prev_ssid = WiFi.SSID(); String prev_pass = WiFi.psk(); @@ -2953,8 +2962,11 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding } # endif -# if defined(ZgatewayBT) && defined(ESP32) +# ifdef ESP32 + ProcessLock = true; +# ifdef ZgatewayBT stopProcessing(); +# endif # endif disconnectClient = false; client.disconnect(); @@ -2977,8 +2989,11 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding client.setServer(SYSdata["mqtt_server"].as(), SYSdata["mqtt_port"].as()); } else { -# if defined(ZgatewayBT) && defined(ESP32) +# ifdef ESP32 + ProcessLock = true; +# ifdef ZgatewayBT stopProcessing(); +# endif # endif disconnectClient = false; client.disconnect();