/*!
*
* MQTT interface for Mobius BLE.
*
* HA mqtt auto discovery with a switch and a sensor.
* Switch is to set the Feed mode
* Sensor is to report the QTY of Mobius devices found by the scan feature
* it will intercept the switch to set feed mode and set the Normal schedule back
*
*
* Work in progress
*
*/
#include <ESP32_MobiusBLE.h>
#include "ArduinoSerialDeviceEventListener.h"
#include "EspMQTTClient.h"
#include <string>
#include "secrets.h"
#include <esp_task_wdt.h>
#include <ArduinoJson.h>
#ifndef LED_BUILTIN
#define LED_BUILTIN 6
#endif
//1 seconds WDT
#define WDT_TIMEOUT 30
#define EXE_INTERVAL 300000
unsigned long lastExecutedMillis = 0; // variable to save the last executed time
// Configuration for wifi and mqtt
EspMQTTClient client(
mySSID, // Your Wifi SSID
myPassword, // Your WiFi key
"homeassistant.local", // MQTT Broker server ip
mqttUser, // mqtt username Can be omitted if not needed
mqttPass, // mqtt pass Can be omitted if not needed
"Mobius", // Client name that uniquely identify your device
1883 // MQTT Broker server port
);
char* jsonSwitchDiscovery = "{\
\"name\":\"Feed Mode\",\
\"command_topic\":\"homeassistant/switch/mobiusBridge/set\",\
\"state_topic\":\"homeassistant/switch/mobiusBridge/state\",\
\"unique_id\":\"mobius01ad\",\
\"device\":{\
\"identifiers\":[\
\"mobridge01ad\"\
],\
\"name\":\"Mobius\",\
\"manufacturer\": \"xx\",\
\"model\": \"Mobius BLE Bridge\",\
\"sw_version\": \"2024.1.0\"\
}}";
char* jsonSensorDiscovery = "{ \
\"name\":\"QTY Devices\",\
\"state_topic\":\"homeassistant/sensor/mobiusBridge/state\",\
\"value_template\":\"{{ value_json.qtydevices}}\",\
\"unique_id\":\"qtydev01ae\",\
\"device\":{\
\"identifiers\":[\
\"mobridge01ad\"\
]\
}\
}";
bool prevState = false;
bool currState = false;
bool configPublish = false;
bool firstRun = true;
// wifi and mqtt connection established
void onConnectionEstablished()
{
ESP_LOGI(LOG_TAG, "Connected to MQTT Broker :)");
// Listen for a scene update from mqtt and call the update function
// May need to do this from the loop(). Test.
client.subscribe("homeassistant/switch/mobiusBridge/set", [](const String& feedMode) {
if (feedMode.length() > 0) {
if (feedMode == "ON") {
currState = true;
digitalWrite(LED_BUILTIN, HIGH);
} else {
currState = false;
digitalWrite(LED_BUILTIN, LOW);
}
configPublish = false;
ESP_LOGI(LOG_TAG, "INFO: Update device scene from MQTT trigger: %s\n", feedMode);
}
});
}
// Define a device buffer to hold found Mobius devices
MobiusDevice deviceBuffer[20];
int deviceCount = 0;
JsonDocument doc;
void setup() {
// connect the serial port for logs
Serial.begin(115200);
while (!Serial);
esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts
esp_task_wdt_add(NULL); //add current thread to WDT watch
pinMode(LED_BUILTIN, OUTPUT);
prevState = !currState;
firstRun = true;
client.enableDebuggingMessages(); // Enable debugging messages sent to serial output
client.enableHTTPWebUpdater(); // Enable the web updater. User and password default to values of MQTTUsername and MQTTPassword. These can be overridded with enableHTTPWebUpdater("user", "password").
// Increase default packet size for HA mqtt json messages
client.setMaxPacketSize(2048);
// Initialize the library with a useful event listener
MobiusDevice::init(new ArduinoSerialDeviceEventListener());
ESP_LOGI(LOG_TAG, "Setup run");
}
void loop() {
// Wait for mqtt and wifi connection
while(!client.isConnected()){client.loop();};
// Loop mqtt
client.loop();
// create buffer to store the Mobius devices
MobiusDevice device = deviceBuffer[0];
if (!configPublish) {
//Scan BLE and MQTT Publish the main config only on boot or on status change
//Scan for Mobius devices
int scanDuration = 15; // in seconds
deviceCount = 0;
while (!deviceCount) {
//Scan until at least one device is returned
deviceCount = MobiusDevice::scanForMobiusDevices(scanDuration, deviceBuffer,10);
ESP_LOGD(LOG_TAG, "INFO: Mobius Devices found: %i\n", deviceCount);
esp_task_wdt_reset();
}
if (!client.publish("homeassistant/switch/mobiusBridge/config", jsonSwitchDiscovery)) { //This one is for the switch
ESP_LOGD(LOG_TAG, "ERROR: Did not publish");
}
if (!client.publish("homeassistant/sensor/mobiusBridge/config", jsonSensorDiscovery)) { //This is for the QTY
ESP_LOGD(LOG_TAG, "ERROR: Did not publish");
}
configPublish = true;
}
/***************************************************************
************ BLE Connection starts here ************
***************************************************************/
//inside the mqtt subscribe function onConnectionEstablished(), it will turn the LED on with digitalWrite(LED_BUILTIN, HIGH);
//using GPIO to proxy the Mobius Scene.
bool currState = digitalRead(LED_BUILTIN);
if (prevState != currState) {
//If Current state is different from previous, publish MQTT state
ESP_LOGD(LOG_TAG, "INFO: Publishing state");
if (currState) {
//This is Feed Mode
ESP_LOGD(LOG_TAG, "INFO: LED is ON");
if (!client.publish("homeassistant/switch/mobiusBridge/state", "ON")) {
ESP_LOGD(LOG_TAG, "ERROR: Did not publish LED on");
}
} else {
//This is Normal Operation
ESP_LOGD(LOG_TAG, "INFO: LED is OFF");
if (!client.publish("homeassistant/switch/mobiusBridge/state", "OFF")) {
ESP_LOGD(LOG_TAG, "ERROR: Did not publish LED off");
}
}
if (!firstRun){
// Do not connect to device during boot
for (int i = 0; i < deviceCount; i++) {
// connect to each device in buffer to set the new scene
device = deviceBuffer[i];
int tries = 1;
//loop until connected or exit after 10 tries
while(tries<=10){
// Get manufacturer info
std::string manuData = device._device->getManufacturerData();
// Don't connect unless we have a serial number
if (manuData.length() > 1){
// Connect, get serialNumber and current scene
ESP_LOGI(LOG_TAG, "\nINFO: Connect to device number: %i\n", i);
if (device.connect()) {
ESP_LOGI(LOG_TAG, "INFO: Connected to: %s\n", device._device->toString().c_str());
// serialNumber is from byte 11
std::string serialNumberString = manuData.substr(11, manuData.length());
char serialNumber[serialNumberString.length() + 1] = {};
strcpy(serialNumber, serialNumberString.c_str());
ESP_LOGD(LOG_TAG, "INFO: Device serial number: %s\n", serialNumber);
//Get Current scene
uint16_t sceneId = device.getCurrentScene();
//Convert scene from int to friendly MQTT text
char currScene[2];
sprintf(currScene, "%u", sceneId);
// delaying without sleeping
unsigned long startMillis = millis();
while (1000 > (millis() - startMillis)) {}
/*===============================================
===== Set Scene =====
===============================================*/
if ((sceneId!=1) and (currState) ) {
//Scene is different from feed mode (1), and Feed switch is ON, set device to feed mode
device.setFeedScene();
} else if ((sceneId==1) and !currState) {
//Scene is feed mode (1), and Feed switch is OFF, set device to Normal Schedule
device.runSchedule();
}
//Disconnect from Mobius Device
device.disconnect();
//If connection completed, break the loop
break;
}
else {
tries++;
}
}
}
//Print error message if didn't connect after 10 tries
if (tries>9) {
ESP_LOGE(LOG_TAG, "ERROR: Failed to connect to device");
}
}
}
char cstr[20];
JsonDocument jsonQtyDev;
jsonQtyDev["qtydevices"] = deviceCount;
serializeJson(jsonQtyDev, cstr);
ESP_LOGD(LOG_TAG, "INFO: Mobius BLE device count: %i\n", deviceCount);
if (!client.publish("homeassistant/sensor/mobiusBridge/state", cstr)) {
ESP_LOGD(LOG_TAG, "ERROR: Did not publish qtyitems");
}
prevState = currState;
}
firstRun = false;
esp_task_wdt_reset();
}