diff --git a/lib/libesp32/berry/default/be_modtab.c b/lib/libesp32/berry/default/be_modtab.c
index 05a882028..989cad873 100644
--- a/lib/libesp32/berry/default/be_modtab.c
+++ b/lib/libesp32/berry/default/be_modtab.c
@@ -48,6 +48,11 @@ be_extern_native_module(lv_extra);
be_extern_native_module(lv_tasmota);
#endif // USE_LVGL
+#if defined(USE_MI_ESP32) && !defined(USE_BLE_ESP32)
+extern void be_load_MI32_class(bvm *vm);
+extern void be_load_BLE_class(bvm *vm);
+#endif //USE_MI_ESP32
+
/* user-defined modules declare start */
/* user-defined modules declare end */
@@ -229,5 +234,9 @@ BERRY_API void be_load_custom_libs(bvm *vm)
be_load_lv_wifi_arcs_icon_class(vm);
be_load_lv_clock_icon_class(vm);
#endif // USE_LVGL
+#if defined(USE_MI_ESP32) && !defined(USE_BLE_ESP32)
+ be_load_MI32_class(vm);
+ be_load_BLE_class(vm);
+#endif //USE_MI_ESP32
}
#endif
diff --git a/lib/libesp32/berry_tasmota/src/be_MI32_lib.c b/lib/libesp32/berry_tasmota/src/be_MI32_lib.c
new file mode 100644
index 000000000..4714f21a9
--- /dev/null
+++ b/lib/libesp32/berry_tasmota/src/be_MI32_lib.c
@@ -0,0 +1,72 @@
+/********************************************************************
+ * Tasmota lib
+ *
+ * To use: `import MI32`
+ *******************************************************************/
+#include "be_constobj.h"
+
+#ifdef USE_MI_ESP32
+extern int be_MI32_devices(bvm *vm);
+extern int be_MI32_set_bat(bvm *vm);
+extern int be_MI32_get_name(bvm *vm);
+extern int be_MI32_get_MAC(bvm *vm);
+extern int be_MI32_set_hum(bvm *vm);
+extern int be_MI32_set_temp(bvm *vm);
+
+/********************************************************************
+** Solidified class: MI32
+********************************************************************/
+be_local_class(MI32,
+ 0,
+ NULL,
+ be_nested_map(6,
+ ( (struct bmapnode*) &(const bmapnode[]) {
+ { be_nested_key("devices", -1593144448, 7, -1), be_const_func(be_MI32_devices) },
+ { be_nested_key("set_bat", -1558299945, 7, -1), be_const_func(be_MI32_set_bat) },
+ { be_nested_key("set_hum", 964296026, 7, 4), be_const_func(be_MI32_set_hum) },
+ { be_nested_key("get_MAC", 2091521771, 7, -1), be_const_func(be_MI32_get_MAC) },
+ { be_nested_key("set_temp", 1952131250, 8, -1), be_const_func(be_MI32_set_temp) },
+ { be_nested_key("get_name", 1616902907, 8, 3), be_const_func(be_MI32_get_name) },
+ })),
+ (be_nested_const_str("MI32", -220693882, 4))
+);
+/*******************************************************************/
+
+void be_load_MI32_class(bvm *vm) {
+ be_pushntvclass(vm, &be_class_MI32);
+ be_setglobal(vm, "MI32");
+ be_pop(vm, 1);
+}
+
+extern int be_BLE_reg_conn_cb(bvm *vm);
+extern int be_BLE_reg_adv_cb(bvm *vm);
+extern int be_BLE_set_MAC(bvm *vm);
+extern int be_BLE_set_characteristic(bvm *vm);
+extern int be_BLE_run(bvm *vm);
+extern int be_BLE_set_service(bvm *vm);
+/********************************************************************
+** Solidified class: BLE
+********************************************************************/
+be_local_class(BLE,
+ 0,
+ NULL,
+ be_nested_map(6,
+ ( (struct bmapnode*) &(const bmapnode[]) {
+ { be_nested_key("conn_cb", 1381122945, 7, -1), be_const_func(be_BLE_reg_conn_cb) },
+ { be_nested_key("set_svc", 752734654, 7, -1), be_const_func(be_BLE_set_service) },
+ { be_nested_key("run", 718098122, 3, -1), be_const_func(be_BLE_run) },
+ { be_nested_key("set_chr", 102133743, 7, 0), be_const_func(be_BLE_set_characteristic) },
+ { be_nested_key("adv_cb", 1957890034, 6, 1), be_const_func(be_BLE_reg_adv_cb) },
+ { be_nested_key("set_MAC", 1617581015, 7, -1), be_const_func(be_BLE_set_MAC) },
+ })),
+ (be_nested_const_str("BLE", -361123990, 3))
+);
+/*******************************************************************/
+
+void be_load_BLE_class(bvm *vm) {
+ be_pushntvclass(vm, &be_class_BLE);
+ be_setglobal(vm, "BLE");
+ be_pop(vm, 1);
+}
+
+#endif //USE_MI_ESP32
diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h
index bc6620370..20600a4d2 100644
--- a/tasmota/tasmota_configurations_ESP32.h
+++ b/tasmota/tasmota_configurations_ESP32.h
@@ -184,6 +184,12 @@
#define USE_BLE_ESP32 // Enable new BLE driver
#define USE_EQ3_ESP32
#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+#ifdef USE_MI_ESP32
+ #if(USE_MI_HOMEKIT != 1) //only for the .c-file
+ #undef USE_MI_HOMEKIT
+ #endif //USE_MI_HOMEKIT
+ #define USE_MI_EXT_GUI //enable dashboard style GUI
+#endif //USE_MI_ESP32
#endif // FIRMWARE_BLUETOOTH
/*********************************************************************************************\
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index e55fd7203..d3dcd988f 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2582,7 +2582,7 @@ void HandleUploadDone(void) {
WSContentStop();
}
-#ifdef USE_BLE_ESP32
+#if defined(USE_BLE_ESP32) || defined(USE_MI_ESP32)
// declare the fn
int ExtStopBLE();
#endif
@@ -3599,4 +3599,4 @@ bool Xdrv01(uint8_t function)
}
return result;
}
-#endif // USE_WEBSERVER
\ No newline at end of file
+#endif // USE_WEBSERVER
diff --git a/tasmota/xdrv_52_3_berry_MI32.ino b/tasmota/xdrv_52_3_berry_MI32.ino
new file mode 100644
index 000000000..a2f03fe21
--- /dev/null
+++ b/tasmota/xdrv_52_3_berry_MI32.ino
@@ -0,0 +1,234 @@
+/*
+ xdrv_52_3_berry_MI32.ino - Berry scripting language, native functions
+
+ Copyright (C) 2021 Christian Baars & Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+
+#ifdef USE_BERRY
+
+#include
+
+#if defined(USE_MI_ESP32) && !defined(USE_BLE_ESP32)
+
+/*********************************************************************************************\
+ * Native functions mapped to Berry functions
+ *
+ *
+\*********************************************************************************************/
+extern "C" {
+
+/********************************************************************
+** MI32 - sensor specific functions
+********************************************************************/
+
+ extern uint32_t MI32numberOfDevices();
+ extern const char * MI32getDeviceName(uint32_t slot);
+ extern void MI32setBatteryForSlot(uint32_t slot, uint8_t value);
+ extern void MI32setHumidityForSlot(uint32_t slot, float value);
+ extern void MI32setTemperatureForSlot(uint32_t slot, float value);
+ extern uint8_t * MI32getDeviceMAC(uint32_t slot);
+
+ int be_MI32_devices(bvm *vm);
+ int be_MI32_devices(bvm *vm) {
+ uint32_t devices = MI32numberOfDevices();
+ be_pushint(vm, devices);
+ be_return(vm);
+ }
+
+ int be_MI32_set_bat(bvm *vm);
+ int be_MI32_set_bat(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 3 && be_isint(vm, 2) && be_isint(vm, 3)) {
+ uint32_t slot = be_toint(vm, 2);
+ int32_t bat_val = be_toint(vm, 3);
+ MI32setBatteryForSlot(slot,bat_val);
+ be_return(vm); // Return
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_MI32_get_name(bvm *vm);
+ int be_MI32_get_name(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 2 && be_isint(vm, 2)) {
+ uint32_t slot = be_toint(vm, 2);
+ const char * name = MI32getDeviceName(slot);
+ be_pushstring(vm,name);
+ be_return(vm); // Return
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_MI32_get_MAC(bvm *vm);
+ int be_MI32_get_MAC(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 2 && be_isint(vm, 2)) {
+ uint32_t slot = be_toint(vm, 2);
+ uint8_t *buffer = MI32getDeviceMAC(slot);
+ size_t len = 6;
+ if(buffer != NULL) {
+ be_pushbytes(vm,buffer,len);
+ be_return(vm); // Return
+ }
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_MI32_set_hum(bvm *vm);
+ int be_MI32_set_hum(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 3 && be_isint(vm, 2) && be_isreal(vm, 3)) {
+ uint32_t slot = be_toint(vm, 2);
+ float hum_val = be_toreal(vm, 3);
+ MI32setHumidityForSlot(slot,hum_val);
+ be_return(vm); // Return
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_MI32_set_temp(bvm *vm);
+ int be_MI32_set_temp(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 3 && be_isint(vm, 2) && be_isreal(vm, 3)) {
+ uint32_t slot = be_toint(vm, 2);
+ float temp_val = be_toreal(vm, 3);
+ MI32setTemperatureForSlot(slot,temp_val);
+ be_return(vm); // Return
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+
+/********************************************************************
+** BLE - generic BLE functions
+********************************************************************/
+ extern void MI32setBerryAdvCB(void* function, uint8_t *buffer);
+ extern void MI32setBerryConnCB(void* function, uint8_t *buffer);
+ extern bool MI32runBerryConnection(uint8_t operation);
+ extern bool MI32setBerryCtxSvc(const char *Svc);
+ extern bool MI32setBerryCtxChr(const char *Chr);
+ extern bool MI32setBerryCtxMAC(uint8_t *MAC, uint8_t type);
+
+
+ int be_BLE_reg_conn_cb(bvm *vm);
+ int be_BLE_reg_conn_cb(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 3 && be_iscomptr(vm, 2)) {
+ void* cb = be_tocomptr(vm, 2);
+ size_t len;
+ uint8_t * buf = (uint8_t*)be_tobytes(vm, 3, &len);
+ MI32setBerryConnCB(cb,buf);
+ be_return(vm);
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_BLE_reg_adv_cb(bvm *vm);
+ int be_BLE_reg_adv_cb(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 3 && be_iscomptr(vm, 2)) {
+ void* cb = be_tocomptr(vm, 2);
+ size_t len;
+ uint8_t * buf = (uint8_t*)be_tobytes(vm, 3, &len);
+ MI32setBerryAdvCB(cb,buf);
+ be_return(vm); // Return
+ }
+ else if(argc == 2 && be_isint(vm, 2)){
+ if(be_toint(vm, 2) == 0){
+ MI32setBerryAdvCB(NULL,NULL);
+ be_return(vm); // Return
+ }
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_BLE_set_MAC(bvm *vm);
+ int be_BLE_set_MAC(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc > 1 && be_isbytes(vm, 2)) {
+ size_t len = 6;
+ uint8_t type = 0;
+ if(argc == 3 && be_isint(vm, 3)){
+ type = be_toint(vm,3);
+ }
+ if (MI32setBerryCtxMAC((uint8_t*)be_tobytes(vm, 2, &len),type)) be_return(vm);
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_BLE_set_service(bvm *vm);
+ int be_BLE_set_service(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 2 && be_isstring(vm, 2)) {
+ if (MI32setBerryCtxSvc(be_tostring(vm, 2))) be_return(vm);
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_BLE_set_characteristic(bvm *vm);
+ int be_BLE_set_characteristic(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 2 && be_isstring(vm, 2)) {
+ if (MI32setBerryCtxChr(be_tostring(vm, 2))) be_return(vm);
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ int be_BLE_run(bvm *vm);
+ int be_BLE_run(bvm *vm){
+ int32_t argc = be_top(vm); // Get the number of arguments
+ if (argc == 2 && be_isint(vm, 2)) {
+ if (MI32runBerryConnection(be_toint(vm, 2))) be_return(vm);
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+} //extern "C"
+
+
+#endif // USE_MI_ESP32
+
+#endif // USE_BERRY
+
+/*
+BLE.set_svc
+BLE.set_chr
+
+BLE.set_MAC
+BLE.run(op)
+be_BLE_op:
+1 read
+2 write
+3 subscribe
+4 unsubscribe
+5 disconnect
+
+11 read once, then disconnect
+12 write once, then disconnect
+13 subscribe once, then disconnect
+14 unsubscribe once, then disconnect
+
+BLE.conn_cb(cb,buffer)
+BLE.adv_cb(cb,buffer)
+
+MI32.devices()
+MI32.get_name(slot)
+MI32.get_MAC(slot)
+MI32.set_bat(slot,int)
+MI32.set_hum(slot,float)
+MI32.set_temp(slot,float)
+
+*/
diff --git a/tasmota/xsns_62_esp32_mi.h b/tasmota/xsns_62_esp32_mi.h
new file mode 100644
index 000000000..7aa705ebd
--- /dev/null
+++ b/tasmota/xsns_62_esp32_mi.h
@@ -0,0 +1,506 @@
+/*
+ xsns_62_esp32_mi.h - MI-BLE-sensors via ESP32 support for Tasmota
+
+ Copyright (C) 2021 Christian Baars and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifdef USE_MI_ESP32
+/*********************************************************************************************\
+ * structs and types
+\*********************************************************************************************/
+#pragma pack(1) // byte-aligned structures to read the sensor data
+
+struct frame_crtl_t{
+ uint16_t reserved1:1;
+ uint16_t reserved2:1;
+ uint16_t reserved3:1;
+ uint16_t isEncrypted:1;
+ uint16_t includesMAC:1;
+ uint16_t includesCapability:1;
+ uint16_t includesObj:1;
+ uint16_t MESH:1;
+ uint16_t registered:1;
+ uint16_t solicited:1;
+ uint16_t AuthMode:2;
+ uint16_t version:4;
+};
+
+struct mi_payload_t{
+ uint8_t type;
+ uint8_t ten;
+ uint8_t size;
+ union {
+ struct{ //0d
+ int16_t temp;
+ uint16_t hum;
+ }HT;
+ uint8_t bat; //0a
+ int16_t temp; //04
+ uint16_t hum; //06
+ uint32_t lux; //07
+ uint8_t moist; //08
+ uint16_t fert; //09
+ uint8_t leak; //14
+ uint32_t NMT; //17
+ uint8_t door; //19
+ struct{ //01
+ uint8_t num;
+ uint8_t value;
+ uint8_t type;
+ }Btn;
+ };
+ uint8_t padding[12]; //for decryption
+};
+
+struct mi_beacon_t{
+ frame_crtl_t frame;
+ uint16_t productID;
+ uint8_t counter;
+ uint8_t MAC[6];
+ uint8_t capability;
+ mi_payload_t payload;
+};
+
+
+struct cg_packet_t {
+ uint16_t frameID;
+ uint8_t MAC[6];
+ uint16_t mode;
+ union {
+ struct {
+ int16_t temp; // -9 - 59 °C
+ uint16_t hum;
+ };
+ uint8_t bat;
+ };
+};
+
+struct encPacket_t{
+ // the packet is longer, but this part is enough to decrypt
+ uint16_t PID;
+ uint8_t frameCnt;
+ uint8_t MAC[6];
+ uint8_t payload[16]; // only a pointer to the address, size is variable
+};
+
+struct berryAdvPacket_t{
+ uint8_t MAC[6];
+ uint8_t addressType;
+ uint16_t svcUUID;
+ uint8_t RSSI;
+ uint8_t length; // length of svcData
+ uint8_t svcData[40]; // only a pointer to the address, size is variable
+ // the last array contains manufacturer data if present, if svcData is not present
+ // format: svcData[0] = length, svcData[1...length] = payload
+};
+
+
+union mi_bindKey_t{
+ struct{
+ uint8_t key[16];
+ uint8_t MAC[6];
+ };
+ uint8_t buf[22];
+};
+
+struct ATCPacket_t{ //and PVVX
+ uint8_t MAC[6];
+ union {
+ struct{
+ uint16_t temp; //sadly this is in wrong endianess
+ uint8_t hum;
+ uint8_t batPer;
+ uint16_t batMV;
+ uint8_t frameCnt;
+ } A; //ATC
+ struct{
+ int16_t temp;
+ uint16_t hum; // x 0.01 %
+ uint16_t batMV;
+ uint8_t batPer;
+ uint8_t frameCnt;
+ struct {
+ uint8_t reed:1;
+ uint8_t TRGval:1;
+ uint8_t TRGcrtl:1;
+ uint8_t tempTrig:1;
+ uint8_t humTrig:1;
+ uint8_t spare:3;
+ };
+ }P; //PVVX
+ };
+};
+
+#pragma pack(0)
+
+
+struct MI32connectionContextBerry_t{
+ NimBLEUUID serviceUUID;
+ NimBLEUUID charUUID;
+ uint8_t * MAC;
+ uint8_t * buffer;
+ uint8_t operation;
+ uint8_t addrType;
+ int error;
+ bool oneOp;
+};
+
+struct {
+ // uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start
+ TaskHandle_t ScanTask = nullptr;
+ TaskHandle_t ConnTask = nullptr;
+ MI32connectionContextBerry_t *conCtx = nullptr;
+ union {
+ struct {
+ uint32_t init:1;
+ uint32_t connected:1;
+ uint32_t autoScan:1;
+ uint32_t canScan:1;
+ uint32_t runningScan:1;
+
+ uint32_t canConnect:1;
+ uint32_t willConnect:1;
+ uint32_t readingDone:1;
+
+ uint32_t shallTriggerTele:1;
+ uint32_t triggeredTele:1;
+ uint32_t shallClearResults:1; // BLE scan results
+ uint32_t shallShowStatusInfo:1; // react to amount of found sensors via RULES
+ uint32_t didGetConfig:1;
+ uint32_t didStartHAP:1;
+ uint32_t triggerBerryAdvCB:1;
+ uint32_t triggerBerryConnCB:1;
+ };
+ uint32_t all = 0;
+ } mode;
+ struct {
+ uint8_t sensor; // points to to the number 0...255
+ } state;
+ struct {
+ uint32_t allwaysAggregate:1; // always show all known values of one sensor in brdigemode
+ uint32_t noSummary:1; // no sensor values at TELE-period
+ uint32_t directBridgeMode:1; // send every received BLE-packet as a MQTT-message in real-time
+ uint32_t showRSSI:1;
+ uint32_t ignoreBogusBattery:1;
+ uint32_t minimalSummary:1; // DEPRECATED!!
+ } option;
+#ifdef USE_MI_EXT_GUI
+ uint32_t widgetSlot;
+#ifdef USE_ENERGY_SENSOR
+ uint8_t *energy_history;
+#endif //USE_ENERGY_SENSOR
+#endif //USE_MI_EXT_GUI
+
+#ifdef USE_MI_HOMEKIT
+ void *outlet_hap_service[4]; //arbitrary chosen
+ int8_t HKconnectedControllers = 0; //should never be < 0
+ uint8_t HKinfoMsg = 0;
+ char hk_setup_code[11];
+#endif //USE_MI_HOMEKIT
+ void *beConnCB;
+ void *beAdvCB;
+ uint8_t *beAdvBuf;
+ uint8_t infoMsg = 0;
+} MI32;
+
+struct mi_sensor_t{
+ uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4; CGG1=5; CGD1=6
+ uint8_t lastCnt; //device generated counter of the packet
+ uint8_t shallSendMQTT;
+ uint8_t MAC[6];
+ uint8_t *key;
+ uint32_t lastTimeSeen;
+ union {
+ struct {
+ uint32_t needsKey:1;
+ uint32_t hasWrongKey:1;
+ uint32_t temp:1;
+ uint32_t hum:1;
+ uint32_t tempHum:1; //every hum sensor has temp too, easier to use Tasmota dew point functions
+ uint32_t lux:1;
+ uint32_t moist:1;
+ uint32_t fert:1;
+ uint32_t bat:1;
+ uint32_t NMT:1;
+ uint32_t motion:1;
+ uint32_t Btn:1;
+ uint32_t door:1;
+ uint32_t leak:1;
+ };
+ uint32_t raw;
+ } feature;
+ union {
+ struct {
+ uint32_t temp:1;
+ uint32_t hum:1;
+ uint32_t tempHum:1; //can be combined from the sensor
+ uint32_t lux:1;
+ uint32_t moist:1;
+ uint32_t fert:1;
+ uint32_t bat:1;
+ uint32_t NMT:1;
+ uint32_t motion:1;
+ uint32_t noMotion:1;
+ uint32_t Btn:1;
+ uint32_t door:1;
+ uint32_t leak:1;
+ };
+ uint32_t raw;
+ } eventType;
+
+ int RSSI;
+ uint32_t lastTime;
+ uint32_t lux;
+ uint8_t *lux_history;
+ float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx
+ uint8_t *temp_history;
+ union {
+ struct {
+ uint8_t moisture;
+ uint16_t fertility;
+ char firmware[6]; // actually only for FLORA but hopefully we can add for more devices
+ }; // Flora
+ struct {
+ float hum;
+ uint8_t *hum_history;
+ }; // MJ_HT_V1, LYWSD0x
+ struct {
+ uint16_t events; //"alarms" since boot
+ uint32_t NMT; // no motion time in seconds for the MJYD2S
+ };
+ struct {
+ uint16_t Btn;
+ uint8_t leak;
+ };
+ uint8_t door;
+ };
+ union {
+ uint8_t bat; // many values seem to be hard-coded garbage (LYWSD0x, GCD1)
+ };
+#ifdef USE_MI_HOMEKIT
+ //HAP handles
+ void *temp_hap_service;
+ void *hum_hap_service;
+ void *light_hap_service;
+ void *motion_hap_service;
+ void *door_sensor_hap_service;
+ void *button_hap_service[6];
+ void *bat_hap_service;
+ void *leak_hap_service;
+#endif //USE_MI_HOMEKIT
+};
+
+/*********************************************************************************************\
+ * constants
+\*********************************************************************************************/
+
+#define D_CMND_MI32 "MI32"
+
+const char kMI32_Commands[] PROGMEM = D_CMND_MI32 "|Key|"/*Time|Battery|Unit|Beacon|*/"Cfg|Option";
+
+void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, /*&CmndMi32Time, &CmndMi32Battery, &CmndMi32Unit, &CmndMi32Beacon,*/ &CmndMi32Cfg, &CmndMi32Option };
+
+#define FLORA 1
+#define MJ_HT_V1 2
+#define LYWSD02 3
+#define LYWSD03MMC 4
+#define CGG1 5
+#define CGD1 6
+#define NLIGHT 7
+#define MJYD2S 8
+#define YEERC 9
+#define MHOC401 10
+#define MHOC303 11
+#define ATC 12
+#define MCCGQ02 13
+#define SJWS01L 14
+#define PVVX 15
+#define YLKG08 16
+
+#define MI32_TYPES 16 //count this manually
+
+const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
+ 0x01aa, // MJ_HT_V1
+ 0x045b, // LYWSD02
+ 0x055b, // LYWSD03
+ 0x0347, // CGG1
+ 0x0576, // CGD1
+ 0x03dd, // NLIGHT
+ 0x07f6, // MJYD2S
+ 0x0153, // yee-rc
+ 0x0387, // MHO-C401
+ 0x06d3, // MHO-C303
+ 0x0a1c, // ATC -> this is a fake ID
+ 0x098b, // MCCGQ02
+ 0x0863, // SJWS01L
+ 0x944a, // PVVX -> this is a fake ID
+ 0x03b6 // YLKG08 and YLKG07 - version w/wo mains
+ };
+
+const char kMI32DeviceType1[] PROGMEM = "Flora";
+const char kMI32DeviceType2[] PROGMEM = "MJ_HT_V1";
+const char kMI32DeviceType3[] PROGMEM = "LYWSD02";
+const char kMI32DeviceType4[] PROGMEM = "LYWSD03";
+const char kMI32DeviceType5[] PROGMEM = "CGG1";
+const char kMI32DeviceType6[] PROGMEM = "CGD1";
+const char kMI32DeviceType7[] PROGMEM = "NLIGHT";
+const char kMI32DeviceType8[] PROGMEM = "MJYD2S";
+const char kMI32DeviceType9[] PROGMEM = "YEERC";
+const char kMI32DeviceType10[] PROGMEM ="MHOC401";
+const char kMI32DeviceType11[] PROGMEM ="MHOC303";
+const char kMI32DeviceType12[] PROGMEM ="ATC";
+const char kMI32DeviceType13[] PROGMEM ="MCCGQ02";
+const char kMI32DeviceType14[] PROGMEM ="SJWS01L";
+const char kMI32DeviceType15[] PROGMEM ="PVVX";
+const char kMI32DeviceType16[] PROGMEM ="YLKG08";
+const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,
+ kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,
+ kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12,
+ kMI32DeviceType13,kMI32DeviceType14,kMI32DeviceType15,kMI32DeviceType16};
+
+const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out";
+
+const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Start scanning";
+
+const char kMI32_HKInfoMsg[] PROGMEM = "HAP core started|HAP core did not start!!|HAP controller disconnected|HAP controller connected|HAP outlet added";
+/*********************************************************************************************\
+ * enumerations
+\*********************************************************************************************/
+
+enum MI32_Commands { // commands useable in console or rules
+ CMND_MI32_KEY, // add bind key to a mac for packet decryption
+ CMND_MI32_CFG, // save config file as JSON with all sensors w/o keys to mi32cfg
+ CMND_MI32_OPTION // change driver options at runtime
+ };
+
+enum MI32_TASK {
+ MI32_TASK_SCAN = 0,
+ MI32_TASK_CONN = 1,
+};
+
+enum MI32_ConnErrorMsg {
+ MI32_CONN_NO_ERROR = 0,
+ MI32_CONN_NO_CONNECT,
+ MI32_CONN_NO_SERVICE,
+ MI32_CONN_NO_CHARACTERISTIC,
+ MI32_CONN_CAN_NOT_READ,
+ MI32_CONN_CAN_NOT_NOTIFY,
+ MI32_CONN_CAN_NOT_WRITE,
+ MI32_CONN_DID_NOT_WRITE,
+ MI32_CONN_NOTIFY_TIMEOUT
+};
+
+enum MI32_BLEInfoMsg {
+ MI32_SCAN_ENDED = 1,
+ MI32_GOT_NOTIFICATION,
+ MI32_DID_CONNECT,
+ MI32_DID_DISCONNECT,
+ MI32_START_SCANNING
+};
+
+enum MI32_HKInfoMsg {
+ MI32_HAP_DID_START = 1,
+ MI32_HAP_DID_NOT_START,
+ MI32_HAP_CONTROLLER_DISCONNECTED,
+ MI32_HAP_CONTROLLER_CONNECTED,
+ MI32_HAP_OUTLET_ADDED
+};
+
+/*********************************************************************************************\
+ * extended web gui
+\*********************************************************************************************/
+
+#ifdef USE_WEBSERVER
+#ifdef USE_MI_EXT_GUI
+const char HTTP_BTN_MENU_MI32[] PROGMEM = "
";
+
+const char HTTP_MI32_SCRIPT_1[] PROGMEM =
+ "function setUp(){setInterval(countUp,1000); setInterval(update,100);}"
+ "function countUp(){let ti=document.querySelectorAll('.Ti');"
+ "for(const el of ti){var t=parseInt(el.innerText);el.innerText=t+1;}}"
+ "function update(){" //source, value
+ "var xr=new XMLHttpRequest();"
+ "xr.onreadystatechange=function(){"
+ "if(xr.readyState==4&&xr.status==200){"
+ "var r = xr.response;" // new widget
+ "if(r.length>2000){return;};if(r.length==0){return;}"
+ "var d = document.createElement('div');"
+ "d.innerHTML = r.trim();"
+ "var old = eb(d.firstChild.id);"
+ "old.parentNode.replaceChild(d.firstChild,old);"
+ "};"
+ "};"
+ "xr.open('GET','/m32?wi=1',true);"
+ "xr.send();"
+ "};"
+ ;
+
+const char HTTP_MI32_STYLE[] PROGMEM =
+ "";
+
+const char HTTP_MI32_STYLE_SVG[] PROGMEM =
+ ""
+ ;
+
+const char HTTP_MI32_PARENT_START[] PROGMEM =
+ ""
+ "
MI32 Bridge
"
+ "Observing %u devices
"
+ "Uptime: %u seconds
"
+#ifdef USE_MI_HOMEKIT
+ "HomeKit setup code: %s
"
+ "HAP controller connections: %d
"
+#else
+ "HomeKit not enabled%s
"
+#endif //USE_MI_HOMEKIT
+ "Free Heap: %u kB"
+ "";
+
+const char HTTP_MI32_WIDGET[] PROGMEM =
+ "
MAC:%s RSSI:%d %s
"
+ "
%s"
+ "
%s"
+ ""
+ "
";
+
+const char HTTP_MI32_GRAPH[] PROGMEM =
+ "
";
+ //rgb(185, 124, 124) - red, rgb(185, 124, 124) - blue, rgb(242, 240, 176) - yellow
+
+#ifdef USE_MI_ESP32_ENERGY
+const char HTTP_MI32_POWER_WIDGET[] PROGMEM =
+ "
"
+ "
Energy"
+ "
"
+ "
" D_VOLTAGE ": %.1f " D_UNIT_VOLT "
"
+ "
" D_CURRENT ": %.3f " D_UNIT_AMPERE "
";
+#endif //USE_MI_ESP32_ENERGY
+
+#endif //USE_MI_EXT_GUI
+#endif // USE_WEBSERVER
+
+#endif //USE_MI_ESP32
diff --git a/tasmota/xsns_62_esp32_mi.ino b/tasmota/xsns_62_esp32_mi.ino
index ecc9d228c..c3c8bc0a7 100644
--- a/tasmota/xsns_62_esp32_mi.ino
+++ b/tasmota/xsns_62_esp32_mi.ino
@@ -22,22 +22,12 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
+ 0.9.5.0 20211016 changed - major rewrite, added mi32cfg (file and command), Homekit-Bridge,
+ extended GUI,
+ removed BLOCK, PERIOD, TIME, UNIT, BATTERY and PAGE -> replaced via Berry-Support
+ -------
0.9.1.7 20201116 changed - small bugfixes, add BLOCK and OPTION command, send BLE scan via MQTT
-------
- 0.9.1.6 20201022 changed - Beacon support, RSSI at TELEPERIOD, refactoring
- -------
- 0.9.1.5 20201021 changed - HASS related ('null', hold back discovery), number of found sensors for RULES
- -------
- 0.9.1.4 20201020 changed - use BearSSL for decryption, revert to old TELEPERIOD-cycle as default
- -------
- 0.9.1.3 20200926 changed - Improve HA discovery, make key+MAC case insensitive
- -------
- 0.9.1.3 20200916 changed - add ATC (custom FW for LYWSD03MMC), API adaption for NimBLE-Arduino 1.0.2
- -------
- 0.9.1.2 20200802 changed - add MHO-C303
- -------
- 0.9.1.1 20200715 changed - add MHO-C401, refactoring
- -------
0.9.1.0 20200712 changed - add lights and yeerc, add pure passive mode with decryption,
lots of refactoring
-------
@@ -53,334 +43,35 @@
#ifdef USE_MI_ESP32
+#ifdef USE_ENERGY_SENSOR
+// #define USE_MI_ESP32_ENERGY //perpare for some GUI extensions
+#endif
+
#define XSNS_62 62
-#define USE_MI_DECRYPTION
#include
#include
-#ifdef USE_MI_DECRYPTION
+
#include
-#endif //USE_MI_DECRYPTION
+
+#include "xsns_62_esp32_mi.h"
+
+#ifdef USE_MI_HOMEKIT
+extern "C" void mi_homekit_main(void);
+extern "C" void mi_homekit_update_value(void* handle, float value, uint32_t type);
+extern "C" void mi_homekit_stop();
+void MI32getSetupCodeFromMAC(char* code);
+#endif //USE_MI_HOMEKIT
+
void MI32scanEndedCB(NimBLEScanResults results);
void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
-
-struct {
- uint16_t perPage = 4;
- uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start
- union {
- struct {
- uint32_t init:1;
- uint32_t connected:1;
- uint32_t autoScan:1;
- uint32_t canScan:1;
- uint32_t runningScan:1;
- uint32_t canConnect:1;
- uint32_t willConnect:1;
- uint32_t readingDone:1;
- uint32_t shallSetTime:1;
- uint32_t willSetTime:1;
- uint32_t shallReadBatt:1;
- uint32_t willReadBatt:1;
- uint32_t shallSetUnit:1;
- uint32_t willSetUnit:1;
- uint32_t shallTriggerTele:1;
- uint32_t triggeredTele:1;
- uint32_t shallClearResults:1; // BLE scan results
- uint32_t shallShowStatusInfo:1; // react to amount of found sensors via RULES
- uint32_t firstAutodiscoveryDone:1;
- uint32_t activeBeacon:1;
- uint32_t shallShowScanResult:1;
- uint32_t shallShowBlockList:1;
- };
- uint32_t all = 0;
- } mode;
- struct {
- uint8_t sensor; // points to to the number 0...255
- uint8_t beaconScanCounter; // countdown timer in seconds
- } state;
- struct {
- uint32_t allwaysAggregate:1; // always show all known values of one sensor in brdigemode
- uint32_t noSummary:1; // no sensor values at TELE-period
- uint32_t directBridgeMode:1; // send every received BLE-packet as a MQTT-message in real-time
- uint32_t holdBackFirstAutodiscovery:1; // allows to trigger it later
- uint32_t showRSSI:1;
- uint32_t ignoreBogusBattery:1;
- uint32_t minimalSummary:1; // DEPRECATED!!
- } option;
-} MI32;
-
-#pragma pack(1) // byte-aligned structures to read the sensor data
-
- struct {
- int16_t temp;
- uint8_t hum;
- uint16_t volt; // LYWSD03 only
- } LYWSD0x_HT;
- struct {
- uint8_t spare;
- int16_t temp;
- uint16_t hum;
- } CGD1_HT;
- struct {
- int16_t temp;
- uint8_t spare;
- uint32_t lux;
- uint8_t moist;
- uint16_t fert;
- } Flora_TLMF; // temperature, lux, moisture, fertility
-
-
-struct mi_beacon_t{
- uint16_t frame;
- uint16_t productID;
- uint8_t counter;
- uint8_t MAC[6];
- uint8_t spare;
- uint8_t type;
- uint8_t ten;
- uint8_t size;
- union {
- struct{ //0d
- int16_t temp;
- uint16_t hum;
- }HT;
- uint8_t bat; //0a
- int16_t temp; //04
- uint16_t hum; //06
- uint32_t lux; //07
- uint8_t moist; //08
- uint16_t fert; //09
- uint32_t NMT; //17
- struct{ //01
- uint16_t num;
- uint8_t longPress;
- }Btn;
- };
- uint8_t padding[12];
-};
-
-struct cg_packet_t {
- uint16_t frameID;
- uint8_t MAC[6];
- uint16_t mode;
- union {
- struct {
- int16_t temp; // -9 - 59 °C
- uint16_t hum;
- };
- uint8_t bat;
- };
-};
-
-struct encPacket_t{
- // the packet is longer, but this part is enough to decrypt
- uint16_t PID;
- uint8_t frameCnt;
- uint8_t MAC[6];
- uint8_t payload[16]; // only a pointer to the address, size is variable
-};
-
-union mi_bindKey_t{
- struct{
- uint8_t key[16];
- uint8_t MAC[6];
- };
- uint8_t buf[22];
-};
-
-struct ATCPacket_t{
- uint8_t MAC[6];
- uint16_t temp; //sadly this is in wrong endianess
- uint8_t hum;
- uint8_t batPer;
- uint16_t batMV;
- uint8_t frameCnt;
-};
-
-#pragma pack(0)
-
-struct mi_sensor_t{
- uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4; CGG1=5; CGD1=6
- uint8_t lastCnt; //device generated counter of the packet
- uint8_t shallSendMQTT;
- uint8_t MAC[6];
- union {
- struct {
- uint32_t temp:1;
- uint32_t hum:1;
- uint32_t tempHum:1; //every hum sensor has temp too, easier to use Tasmota dew point functions
- uint32_t lux:1;
- uint32_t moist:1;
- uint32_t fert:1;
- uint32_t bat:1;
- uint32_t NMT:1;
- uint32_t PIR:1;
- uint32_t Btn:1;
- };
- uint32_t raw;
- } feature;
- union {
- struct {
- uint32_t temp:1;
- uint32_t hum:1;
- uint32_t tempHum:1; //can be combined from the sensor
- uint32_t lux:1;
- uint32_t moist:1;
- uint32_t fert:1;
- uint32_t bat:1;
- uint32_t NMT:1;
- uint32_t motion:1;
- uint32_t noMotion:1;
- uint32_t Btn:1;
- };
- uint32_t raw;
- } eventType;
-
- int RSSI;
- uint32_t lastTime;
- uint32_t lux;
- float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx
- union {
- struct {
- uint8_t moisture;
- uint16_t fertility;
- char firmware[6]; // actually only for FLORA but hopefully we can add for more devices
- }; // Flora
- struct {
- float hum;
- }; // MJ_HT_V1, LYWSD0x
- struct {
- uint16_t events; //"alarms" since boot
- uint32_t NMT; // no motion time in seconds for the MJYD2S
- };
- uint16_t Btn;
- };
- union {
- uint8_t bat; // many values seem to be hard-coded garbage (LYWSD0x, GCD1)
- };
-};
-
-struct scan_entry_t {
- uint8_t MAC[6];
- uint16_t CID;
- uint16_t SVC;
- uint16_t UUID;
- int32_t RSSI;
-};
-
-struct generic_beacon_t {
- uint8_t MAC[6];
- uint32_t time;
- int32_t RSSI;
- uint16_t CID; // company identifier
- uint16_t UUID; // the first, if more than one exists
- uint16_t SVC;
- bool active = false;
-};
-
-struct MAC_t {
- uint8_t buf[6];
-};
+void MI32AddKey(mi_bindKey_t keyMAC);
std::vector MIBLEsensors;
-std::vector MIBLEbindKeys;
-std::array MIBLEbeacons; // we support a fixed number
-std::vector MIBLEscanResult;
-std::vector MIBLEBlockList;
static BLEScan* MI32Scan;
-/*********************************************************************************************\
- * constants
-\*********************************************************************************************/
-
-#define D_CMND_MI32 "MI32"
-
-const char kMI32_Commands[] PROGMEM = D_CMND_MI32 "|"
-#ifdef USE_MI_DECRYPTION
- "Key|"
-#endif // USE_MI_DECRYPTION
- "Period|Time|Page|Battery|Unit|Beacon|Block|Option";
-
-void (*const MI32_Commands[])(void) PROGMEM = {
-#ifdef USE_MI_DECRYPTION
- &CmndMi32Key,
-#endif // USE_MI_DECRYPTION
- &CmndMi32Period, &CmndMi32Time, &CmndMi32Page, &CmndMi32Battery, &CmndMi32Unit, &CmndMi32Beacon, &CmndMi32Block, &CmndMi32Option };
-
-#define FLORA 1
-#define MJ_HT_V1 2
-#define LYWSD02 3
-#define LYWSD03MMC 4
-#define CGG1 5
-#define CGD1 6
-#define NLIGHT 7
-#define MJYD2S 8
-#define YEERC 9
-#define MHOC401 10
-#define MHOC303 11
-#define ATC 12
-
-#define MI32_TYPES 12 //count this manually
-
-const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
- 0x01aa, // MJ_HT_V1
- 0x045b, // LYWSD02
- 0x055b, // LYWSD03
- 0x0347, // CGG1
- 0x0576, // CGD1
- 0x03dd, // NLIGHT
- 0x07f6, // MJYD2S
- 0x0153, // yee-rc
- 0x0387, // MHO-C401
- 0x06d3, // MHO-C303
- 0x0a1c // ATC -> this is a fake ID
- };
-
-const char kMI32DeviceType1[] PROGMEM = "Flora";
-const char kMI32DeviceType2[] PROGMEM = "MJ_HT_V1";
-const char kMI32DeviceType3[] PROGMEM = "LYWSD02";
-const char kMI32DeviceType4[] PROGMEM = "LYWSD03";
-const char kMI32DeviceType5[] PROGMEM = "CGG1";
-const char kMI32DeviceType6[] PROGMEM = "CGD1";
-const char kMI32DeviceType7[] PROGMEM = "NLIGHT";
-const char kMI32DeviceType8[] PROGMEM = "MJYD2S";
-const char kMI32DeviceType9[] PROGMEM = "YEERC";
-const char kMI32DeviceType10[] PROGMEM ="MHOC401";
-const char kMI32DeviceType11[] PROGMEM ="MHOC303";
-const char kMI32DeviceType12[] PROGMEM ="ATC";
-const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12};
-
-/*********************************************************************************************\
- * enumerations
-\*********************************************************************************************/
-
-enum MI32_Commands { // commands useable in console or rules
- CMND_MI32_PERIOD, // set period like TELE-period in seconds between read-cycles
- CMND_MI32_TIME, // set LYWSD02-Time from ESP8266-time
- CMND_MI32_PAGE, // sensor entries per web page, which will be shown alternated
- CMND_MI32_BATTERY, // read all battery levels
- CMND_MI32_UNIT, // toggles the displayed unit between C/F (LYWSD02)
- CMND_MI32_KEY, // add bind key to a mac for packet decryption
- CMND_MI32_BEACON, // add up to 4 beacons defined by their MAC addresses
- CMND_MI32_BLOCK, // block BLE sensors defined by their MAC addresses
- CMND_MI32_OPTION // change driver options at runtime
- };
-
-enum MI32_TASK {
- MI32_TASK_SCAN = 0,
- MI32_TASK_CONN = 1,
- MI32_TASK_TIME = 2,
- MI32_TASK_BATT = 3,
- MI32_TASK_UNIT = 4,
-};
-
-enum MI32_BEACON_CMND {
- MI32_BEACON_ON = 0,
- MI32_BEACON_OFF = 1,
- MI32_BEACON_DEL = 2,
-};
/*********************************************************************************************\
* Classes
@@ -388,14 +79,15 @@ enum MI32_BEACON_CMND {
class MI32SensorCallback : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pclient) {
- AddLog(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]);
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32DeviceType[(MIBLEsensors[MI32.conCtx->slot].type)-1]);
+ MI32.infoMsg = MI32_DID_CONNECT;
MI32.mode.willConnect = 0;
MI32.mode.connected = 1;
}
void onDisconnect(NimBLEClient* pclient) {
MI32.mode.connected = 0;
- MI32.mode.willReadBatt = 0;
- AddLog(LOG_LEVEL_DEBUG,PSTR("disconnected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]);
+ MI32.infoMsg = MI32_DID_DISCONNECT;
+ //AddLog(LOG_LEVEL_DEBUG,PSTR("disconnected"));
}
bool onConnParamsUpdateRequest(NimBLEClient* MI32Client, const ble_gap_upd_params* params) {
if(params->itvl_min < 24) { /** 1.25ms units */
@@ -413,46 +105,63 @@ class MI32SensorCallback : public NimBLEClientCallbacks {
class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
+ static bool _mutex = false;
+ if(_mutex) return;
+ _mutex = true;
+
int RSSI = advertisedDevice->getRSSI();
uint8_t addr[6];
memcpy(addr,advertisedDevice->getAddress().getNative(),6);
MI32_ReverseMAC(addr);
- if (advertisedDevice->getServiceDataCount() == 0) {
- // AddLog(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
- if(MI32.state.beaconScanCounter==0 && !MI32.mode.activeBeacon){
- MI32Scan->erase(advertisedDevice->getAddress());
- return;
- }
- else{
- MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr);
- return;
- }
+ size_t ServiceDataLength = 0;
+
+ if (advertisedDevice->getServiceDataCount() == 0) {
+ if(MI32.beAdvCB != nullptr && MI32.mode.triggerBerryAdvCB == 0){
+ berryAdvPacket_t *_packet = (berryAdvPacket_t *)MI32.beAdvBuf;
+ memcpy(_packet->MAC,addr,6);
+ _packet->addressType = advertisedDevice->getAddressType();
+ _packet->svcUUID = 0;
+ _packet->RSSI = (uint8_t)RSSI;
+ _packet->length = ServiceDataLength;
+ _packet->svcData[0] = 0; //guarantee it is zero!!
+ if(advertisedDevice->haveManufacturerData()){
+ std::string _md = advertisedDevice->getManufacturerData();
+ _packet->svcData[0] = _md.size();
+ memcpy((_packet->svcData)+ServiceDataLength+1,_md.data(), _md.size());
+ }
+ MI32.mode.triggerBerryAdvCB = 1;
+ }
+ _mutex = false;
+ return;
}
uint16_t UUID = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value;
- // AddLog(LOG_LEVEL_DEBUG,PSTR("UUID: %x"),UUID);
- size_t ServiceDataLength = advertisedDevice->getServiceData(0).length();
+ ServiceDataLength = advertisedDevice->getServiceData(0).length();
+ if(MI32.beAdvCB != nullptr && MI32.mode.triggerBerryAdvCB == 0){
+ berryAdvPacket_t *_packet = (berryAdvPacket_t *)MI32.beAdvBuf;
+ memcpy(_packet->MAC,addr,6);
+ _packet->addressType = advertisedDevice->getAddressType();
+ _packet->svcUUID = UUID;
+ _packet->RSSI = (uint8_t)RSSI;
+ _packet->length = ServiceDataLength;
+ memcpy(_packet->svcData,advertisedDevice->getServiceData(0).data(),ServiceDataLength);
+ MI32.mode.triggerBerryAdvCB = 1;
+ }
+
if(UUID==0xfe95) {
- if(MI32isInBlockList(addr) == true) return;
MI32ParseResponse((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI);
}
else if(UUID==0xfdcd) {
- if(MI32isInBlockList(addr) == true) return;
MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI);
}
- else if(UUID==0x181a) { //ATC
- if(MI32isInBlockList(addr) == true) return;
+ else if(UUID==0x181a) { //ATC and PVVX
MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI);
}
- else {
- if(MI32.state.beaconScanCounter!=0 || MI32.mode.activeBeacon){
- MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr);
- }
- // AddLog(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), UUID, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
- MI32Scan->erase(advertisedDevice->getAddress());
- }
+ // else {
+ // MI32Scan->erase(advertisedDevice->getAddress());
+ // }
+ _mutex = false;
};
};
@@ -466,21 +175,16 @@ static NimBLEClient* MI32Client;
\*********************************************************************************************/
void MI32scanEndedCB(NimBLEScanResults results){
- AddLog(LOG_LEVEL_DEBUG,PSTR("Scan ended"));
+ MI32.infoMsg = MI32_SCAN_ENDED;
MI32.mode.runningScan = 0;
}
void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
- AddLog(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length);
- switch(MIBLEsensors[MI32.state.sensor].type){
- case LYWSD03MMC: case LYWSD02: case MHOC401:
- MI32readHT_LY((char*)pData);
- MI32.mode.readingDone = 1;
- break;
- default:
- MI32.mode.readingDone = 1;
- break;
- }
+ MI32.infoMsg = MI32_GOT_NOTIFICATION;
+ MI32.conCtx->buffer[0] = (uint8_t)length;
+ memcpy(MI32.conCtx->buffer + 1, pData, length);
+ MI32.mode.triggerBerryConnCB = 1;
+ MI32.mode.readingDone = 1;
}
/*********************************************************************************************\
* Helper functions
@@ -542,97 +246,126 @@ void MI32_ReverseMAC(uint8_t _mac[]){
memcpy(_mac,_reversedMAC, sizeof(_reversedMAC));
}
-#ifdef USE_MI_DECRYPTION
-void MI32AddKey(char* payload){
- mi_bindKey_t keyMAC;
- MI32HexStringToBytes(payload,keyMAC.buf);
- bool unknownKey = true;
- for(uint32_t i=0; iMAC[i];
- }
- memcpy((uint8_t*)&nonce+6,(uint8_t*)&packet->PID,2);
- nonce[8] = packet->frameCnt;
- memcpy((uint8_t*)&nonce+9,(uint8_t*)&_buf[_bufSize-9],3);
- // AddLog(LOG_LEVEL_DEBUG,PSTR("nonceCnt1 and 2: %02x %02x %02x"),nonce[9],nonce[10],nonce[11]);
- memcpy((uint8_t*)&tag,(uint8_t*)&_buf[_bufSize-6],4);
- // AddLog(LOG_LEVEL_DEBUG,PSTR("tag: %02x %02x %02x %02x"),tag[0],tag[1],tag[2],tag[3]);
-
- MI32_ReverseMAC(packet->MAC);
- uint8_t _bindkey[16] = {0x0};
- bool foundNoKey = true;
- AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
- for(uint32_t i=0; iMAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){
- memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey));
- AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Decryption Key found"));
- foundNoKey = false;
- break;
- }
- }
- if(foundNoKey){
- AddLog(LOG_LEVEL_DEBUG,PSTR("M32: No Key found !!"));
+ if(MIBLEsensors[_slot].key == nullptr){
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: No Key found !!"));
return -2;
}
+ // uint8_t _testBuf[] = {0x58,0x30,0xb6,0x03,0x36,0x8b,0x98,0xc5,0x41,0x24,0xf8,0x8b,0xb8,0xf2,0x66,0x13,0x51,0x00,0x00,0x00,0xd6};
+ // uint8_t _testKey[] = {0xb8,0x53,0x07,0x51,0x58,0x48,0x8d,0x3d,0x3c,0x97,0x7c,0xa3,0x9a,0x5b,0x5e,0xa9};
+ // _beacon = (mi_beacon_t *)_testBuf;
+ // _bufSize = sizeof(_testBuf);
+ // dataLen = _bufSize - 11 ; // _bufsize - frame - type - frame.counter - MAC
+
+
+ uint32_t _version = (uint32_t)_beacon->frame.version;
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: encrypted msg from %s with version:%u"),kMI32DeviceType[MIBLEsensors[_slot].type-1],_version);
+
+ if(_version == 5){
+ if(_beacon->frame.includesMAC){
+ for (uint32_t i = 0; i<6; i++){
+ nonce[i] = _beacon->MAC[i];
+ }
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: has MAC"));
+ memcpy(_payload,(uint8_t*)&_beacon->capability, dataLen); //special packet
+ dataLen -= 7;
+ }
+ else{
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: has no MAC"));
+ for (uint32_t i = 0; i<6; i++){
+ nonce[i] = MIBLEsensors[_slot].MAC[5-i];
+ }
+ dataLen = _bufSize -5 ;
+ memcpy(_payload,_beacon->MAC, dataLen); //special packet
+ dataLen -= 7;
+ // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) _payload, dataLen);
+ }
+ // nonce: device MAC, device type, frame cnt, ext. cnt
+ memcpy((uint8_t*)&nonce+6,(uint8_t*)&_beacon->productID,2);
+ nonce[8] = _beacon->counter;
+ memcpy((uint8_t*)&nonce+9,(uint8_t*)&_payload[dataLen],3);
+ // memcpy((uint8_t*)&tag,(uint8_t*)&_payload[dataLen-4],4);
+ memcpy((uint8_t*)&tag,(uint8_t*)&_buf[_bufSize-4],4);
+ }
+ else if(_version == 3){
+ // nonce: frame_ctrl, device type, ext. cnt, frame cnt, device MAC(only first 5 bytes)
+ memcpy(_payload,(uint8_t*)&_beacon->capability, dataLen); //special packet
+ nonceLen = 13;
+ memcpy((uint8_t*)&nonce,(uint8_t*)&_beacon->frame,2);
+ memcpy((uint8_t*)&nonce+2,(uint8_t*)&_beacon->productID,2);
+ nonce[4] = _beacon->counter;
+ memcpy((uint8_t*)&nonce+5,(uint8_t*)&_buf[_bufSize-4],3);
+ for (uint32_t i = 0; i<5; i++){
+ nonce[i+8] = _beacon->MAC[i];
+ }
+ tag[0] = _buf[_bufSize-1];
+ // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) nonce, 13);
+ dataLen -= 4;
+ }
+ else{
+ AddLog(LOG_LEVEL_DEBUG,PSTR("M32: unexpected decryption version:%u"),_version); // should never happen
+ }
+
br_aes_small_ctrcbc_keys keyCtx;
- br_aes_small_ctrcbc_init(&keyCtx, _bindkey, sizeof(_bindkey));
+ br_aes_small_ctrcbc_init(&keyCtx, MIBLEsensors[_slot].key, 16);
+ // br_aes_small_ctrcbc_init(&keyCtx, _testKey, 16);
br_ccm_context ctx;
br_ccm_init(&ctx, &keyCtx.vtable);
- br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(authData), data_len, sizeof(tag));
+ br_ccm_reset(&ctx, nonce, nonceLen, sizeof(authData), dataLen, sizeof(tag));
br_ccm_aad_inject(&ctx, authData, sizeof(authData));
br_ccm_flip(&ctx);
-
-
- memcpy(payload,packet->payload,data_len); //we want to be sure about 4-byte alignement
- br_ccm_run(&ctx, 0, payload, data_len);
- memcpy((uint8_t*)packet->payload+1,payload,data_len); //back to the packet
+ br_ccm_run(&ctx, 0, _payload, dataLen);
ret = br_ccm_check_tag(&ctx, &tag);
- AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]);
- return ret-1;
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: decrypted in %.2f mSec"),enctime);
+ // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) _payload, dataLen);
+ return ret;
}
-#endif // USE_MI_DECRYPTION
/*********************************************************************************************\
* common functions
\*********************************************************************************************/
-
/**
* @brief Return the slot number of a known sensor or return create new sensor slot
*
@@ -641,7 +374,6 @@ int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type){
* @return uint32_t Known or new slot in the sensors-vector
*/
uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter){
-
DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_MI32, _type);
bool _success = false;
for (uint32_t i=0;i100.0f) value=100.0f; //clamp it for now
+ history[_hour] = (((uint8_t)(value/5.0f))+1) + 0b10000000; //lux
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: history lux: %u in hour:%u"),history[_hour], _hour);
+ break;
+#ifdef USE_MI_ESP32_ENERGY
+ case 100: // energy
+ if(value == 0.0f) value = 1.0f;
+ uint8_t _watt = ((uint8_t)(MI32ln(value))*2) + 0b10000000; //watt
+ history[_hour] = _watt;
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: history energy: %u for value:%u"),history[_hour], value); //still playing with the mapping
+ break;
+#endif //USE_MI_ESP32_ENERGY
+ }
+}
+
+/**
+ * @brief Returns a value betwenn 0-21 for use as a data point in the history graph of the extended web UI
+ *
+ * @param history - pointer to uint8_t[23]
+ * @param hour - hour of datapoint
+ * @return uint8_t - value for the y-axis, should be between 0-21
+ */
+uint8_t MI32fetchHistory(uint8_t *history, uint32_t hour){
+ if(hour>23) {
+ return 0;} //should never happen
+ if(bitRead(history[hour],7) == 0) {
+ return 0; //invalidated data
+ }
+ return (history[hour]) - 0b10000000;
+}
+
+/**
+ * @brief Invalidates the history data of the following hour by setting MSB to 0, should be called at FUNC_JSON_APPEND
+ *
+ */
+void Mi32invalidateOldHistory(){
+ uint32_t _hour = (LocalTime()%SECS_PER_DAY)/SECS_PER_HOUR;
+ static uint32_t _lastInvalidatedHour = 99;
+ if (_lastInvalidatedHour == _hour){
+ return;
+ }
+ uint32_t _nextHour = (_hour>22)?0:_hour+1;
+ for(auto _sensor:MIBLEsensors){
+ if(_sensor.feature.temp == 1){
+ bitClear(_sensor.temp_history[_nextHour],7);
+ }
+ if(_sensor.feature.hum == 1){
+ bitClear(_sensor.hum_history[_nextHour],7);
+ }
+ if(_sensor.feature.lux == 1){
+ bitClear(_sensor.lux_history[_nextHour],7);
+ }
+ }
+ _lastInvalidatedHour = _hour;
+}
+
+#endif //USE_MI_EXT_GUI
/*********************************************************************************************\
* init NimBLE
\*********************************************************************************************/
void MI32PreInit(void) {
- MIBLEsensors.reserve(10);
- MIBLEbindKeys.reserve(10);
- MIBLEscanResult.reserve(20);
+
MI32.mode.init = false;
//test section for options
@@ -758,7 +620,13 @@ void MI32PreInit(void) {
MI32.option.directBridgeMode = 0;
MI32.option.showRSSI = 1;
MI32.option.ignoreBogusBattery = 1; // from advertisements
- MI32.option.holdBackFirstAutodiscovery = 1;
+
+ MI32loadCfg();
+ if(MIBLEsensors.size()>0){
+ MI32.mode.didGetConfig = 1;
+ }
+
+ MI32.beAdvCB = nullptr;
AddLog(LOG_LEVEL_INFO,PSTR("M32: pre-init"));
}
@@ -775,52 +643,370 @@ void MI32Init(void) {
}
}
+ if(MI32.mode.didGetConfig){
+ MI32.mode.didStartHAP = 0;
+ #ifdef USE_MI_HOMEKIT
+ MI32getSetupCodeFromMAC(MI32.hk_setup_code);
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: Init HAP core"));
+ mi_homekit_main();
+ #else
+ MI32.mode.didStartHAP = 1;
+ #endif //USE_MI_HOMEKIT
+ }
+
if (!MI32.mode.init) {
- NimBLEDevice::init("");
+ NimBLEDevice::init("MI32");
AddLog(LOG_LEVEL_INFO,PSTR("M32: Init BLE device"));
- MI32.mode.canScan = 1;
MI32.mode.init = 1;
- MI32.period = Settings->tele_period;
MI32StartScanTask(); // Let's get started !!
}
+#ifdef USE_MI_EXT_GUI
+#ifdef USE_MI_ESP32_ENERGY
+ MI32.energy_history = (uint8_t*) calloc(24,1);
+#endif //USE_MI_ESP32_ENERGY
+#endif //USE_MI_EXT_GUI
return;
}
+/*********************************************************************************************\
+ * Berry section - partly used by HomeKit too
+\*********************************************************************************************/
+extern "C" {
+
+ bool MI32runBerryConnection(uint8_t operation){
+ if(MI32.conCtx != nullptr){
+ MI32.conCtx->operation = operation%100;
+ AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Berry connection op: %d, addrType: %d"),MI32.conCtx->operation, MI32.conCtx->addrType);
+ MI32StartConnectionTask();
+ return true;
+ }
+ return false;
+ }
+
+ void MI32setBerryConnCB(void* function, uint8_t *buffer){
+ if(MI32.conCtx == nullptr){
+ MI32.conCtx = new MI32connectionContextBerry_t;
+ }
+ MI32.conCtx->buffer = buffer;
+ MI32.beConnCB = function;
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: Connection Ctx created"));
+ }
+
+ bool MI32setBerryCtxSvc(const char *Svc){
+ if(MI32.conCtx != nullptr){
+ MI32.conCtx->serviceUUID = NimBLEUUID(Svc);
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: SVC: %s"),MI32.conCtx->serviceUUID.toString().c_str());
+ // AddLog(LOG_LEVEL_INFO,PSTR("M32: SVC: %s"),Svc);
+ return true;
+ }
+ return false;
+ }
+
+ bool MI32setBerryCtxChr(const char *Chr){
+ if(MI32.conCtx != nullptr){
+ MI32.conCtx->charUUID = NimBLEUUID(Chr);
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: CHR: %s"),MI32.conCtx->charUUID.toString().c_str());
+ // AddLog(LOG_LEVEL_INFO,PSTR("M32: CHR: %s"),Chr);
+ return true;
+ }
+ return false;
+ }
+
+ bool MI32setBerryCtxMAC(uint8_t *MAC, uint8_t type){
+ if(MI32.conCtx != nullptr){
+ MI32.conCtx->MAC = MAC;
+ if(type<4) MI32.conCtx->addrType = type;
+ else MI32.conCtx->addrType = 0;
+ return true;
+ }
+ return false;
+ }
+
+ void MI32setBerryAdvCB(void* function, uint8_t *buffer){
+ MI32.beAdvCB = function;
+ MI32.beAdvBuf = buffer;
+ }
+
+ void MI32setBatteryForSlot(uint32_t slot, uint8_t value){
+ if(slot>MIBLEsensors.size()-1) return;
+ if(MIBLEsensors[slot].feature.bat){
+ MIBLEsensors[slot].bat = value;
+ }
+ }
+
+ void MI32setHumidityForSlot(uint32_t slot, float value){
+ if(slot>MIBLEsensors.size()-1) return;
+ if(MIBLEsensors[slot].feature.hum){
+ MIBLEsensors[slot].hum = value;
+ }
+ }
+
+ void MI32setTemperatureForSlot(uint32_t slot, float value){
+ if(slot>MIBLEsensors.size()-1) return;
+ if(MIBLEsensors[slot].feature.temp){
+ MIBLEsensors[slot].temp = value;
+ }
+ }
+
+ uint32_t MI32numberOfDevices(){
+ return MIBLEsensors.size();
+ }
+
+ uint8_t * MI32getDeviceMAC(uint32_t slot){
+ if(slot>MIBLEsensors.size()-1) return NULL;
+ return MIBLEsensors[slot].MAC;
+ }
+
+ const char * MI32getDeviceName(uint32_t slot){
+ if(slot>MIBLEsensors.size()-1) return "";
+ return kMI32DeviceType[MIBLEsensors[slot].type-1];
+ }
+
+} //extern "C"
+/*********************************************************************************************\
+ * Homekit section
+\*********************************************************************************************/
+#ifdef USE_MI_HOMEKIT
+extern "C" {
+
+ const char * MI32getSetupCode(){
+ return (const char*)MI32.hk_setup_code;
+ }
+
+ uint32_t MI32numOfRelays(){
+ if(TasmotaGlobal.devices_present>0) MI32.HKinfoMsg = MI32_HAP_OUTLET_ADDED;
+ return TasmotaGlobal.devices_present;
+ }
+
+ void MI32setRelayFromHK(uint32_t relay, bool onOff){
+ ExecuteCommandPower(relay, onOff, SRC_IGNORE);
+ }
+
+ uint32_t MI32getDeviceType(uint32_t slot){
+ return MIBLEsensors[slot].type;
+ }
+
+/**
+ * @brief Get at least a bit of the status of the HAP core, i.e. to reduce the activy of the driver while doing the pairing
+ *
+ * @param event
+ */
+ void MI32passHapEvent(uint32_t event){
+ switch(event){
+ case 5: //HAP_EVENT_PAIRING_STARTED
+ MI32suspendScanTask();
+ default:
+ vTaskResume(MI32.ScanTask);
+ }
+ if(event==4){
+ MI32.HKinfoMsg = MI32_HAP_CONTROLLER_DISCONNECTED;
+ MI32.HKconnectedControllers--;
+ }
+ if(event==3){
+ MI32.HKinfoMsg = MI32_HAP_CONTROLLER_CONNECTED;
+ MI32.HKconnectedControllers++;
+ }
+ }
+
+ void MI32didStartHAP(bool HAPdidStart){
+ if(HAPdidStart) {
+ MI32.mode.didStartHAP = 1;
+ MI32.HKinfoMsg = MI32_HAP_DID_START;
+ }
+ else{
+ MI32.HKinfoMsg = MI32_HAP_DID_NOT_START;
+ }
+ }
+
+/**
+ * @brief Simply store the writeable HAP characteristics as void pointers in the "main" driver for updates of the values
+ *
+ * @param slot - sensor slot in MIBLEsensors
+ * @param type - sensors type, except for the buttons this is equal to the mibeacon types
+ * @param handle - a void ponter to a characteristic
+ */
+ void MI32saveHAPhandles(uint32_t slot, uint32_t type, void* handle){
+ // AddLog(LOG_LEVEL_INFO,PSTR("M32: pass ptr to hap service, type:%u"), type);
+ switch(type){
+ case 1000: case 1001: case 1002: case 1003: case 1004: case 1005:
+ MIBLEsensors[slot].button_hap_service[type-1000] = handle;
+ break;
+ case 0x04:
+ MIBLEsensors[slot].temp_hap_service = handle;
+ break;
+ case 0x06:
+ MIBLEsensors[slot].hum_hap_service = handle;
+ break;
+ case 0x0a:
+ MIBLEsensors[slot].bat_hap_service = handle;
+ break;
+ case 0x07:
+ MIBLEsensors[slot].light_hap_service = handle;
+ break;
+ case 0x0f:
+ MIBLEsensors[slot].motion_hap_service = handle;
+ break;
+ case 0x14:
+ MIBLEsensors[slot].leak_hap_service = handle;
+ break;
+ case 0x19:
+ MIBLEsensors[slot].door_sensor_hap_service = handle;
+ break;
+ case 0xf0:
+ if(slot>3) break; //support only 4 for now
+ MI32.outlet_hap_service[slot] = handle;
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Creates a simplified setup code from the Wifi MAC for HomeKit by converting every ascii-converted byte to 1, if it not 2-9
+ * Example: AABBCC1234f2
+ * -> 111-11-234
+ * This is no security feature, only for convenience
+ * * @param setupcode
+ */
+ void MI32getSetupCodeFromMAC(char *setupcode){
+ uint8_t _mac[6];
+ char _macStr[13] = { 0 };
+ WiFi.macAddress(_mac);
+ ToHex_P(_mac,6,_macStr,13);
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: Wifi MAC: %s"), _macStr);
+ for(int i = 0; i<10; i++){
+ if(_macStr[i]>'9' || _macStr[i]<'1') setupcode[i]='1';
+ else setupcode[i] = _macStr[i];
+ }
+ setupcode[3] = '-';
+ setupcode[6] = '-';
+ setupcode[10] = 0;
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: HK setup code: %s"), setupcode);
+ return;
+ }
+
+#endif //USE_MI_HOMEKIT
+/*********************************************************************************************\
+ * Config section
+\*********************************************************************************************/
+
+void MI32loadCfg(){
+ if (TfsFileExists("/mi32cfg")){
+ MIBLEsensors.reserve(10);
+ const size_t _buf_size = 2048;
+ char * _filebuf = (char*)calloc(_buf_size,1);
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: found config file"));
+ if(TfsLoadFile("/mi32cfg",(uint8_t*)_filebuf,_buf_size)){
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: %s"),_filebuf);
+ JsonParser parser(_filebuf);
+ JsonParserToken root = parser.getRoot();
+ if (!root) {AddLog(LOG_LEVEL_INFO,PSTR("M32: invalid root "));}
+ JsonParserArray arr = root.getArray();
+ if (!arr) {AddLog(LOG_LEVEL_INFO,PSTR("M32: invalid array object"));; }
+ bool _error;
+ int32_t _numberOfDevices;
+ for (auto _dev : arr) {
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: found device in config file"));
+ JsonParserObject _device = _dev.getObject();
+ uint8_t _mac[6];
+ JsonParserToken _val = _device[PSTR("MAC")];
+ _error = true;
+ if (_val) {
+ char *_macStr = (char *)_val.getStr();
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: found MAC: %s"), _macStr);
+ if(strlen(_macStr)!=12){
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: wrong MAC length: %u"), strlen(_macStr));
+ break;
+ }
+ MI32HexStringToBytes(_macStr,_mac);
+ _val = _device[PSTR("PID")];
+ if(_val){
+ uint8_t _pid[2];
+ char *_pidStr = (char *)_val.getStr();
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: found PID: %s"), _pidStr);
+ if(strlen(_pidStr)!=4){
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: wrong PID length: %u"), strlen(_pidStr));
+ break;
+ }
+ MI32HexStringToBytes(_pidStr,_pid);
+ uint16_t _pid16 = _pid[0]*256 + _pid[1];
+ _numberOfDevices = MIBLEgetSensorSlot(_mac,_pid16,0);
+ _error = false;
+ }
+ }
+ _val = _device[PSTR("key")];
+ if (_val) {
+ mi_bindKey_t _keyMAC;
+ uint8_t *_key = (uint8_t*) malloc(16);
+ char *_keyStr = (char *)_val.getStr();
+ if(strlen(_keyStr)==0){
+ continue;
+ }
+ if(strlen(_keyStr)!=32){
+ _error = true;
+ break;
+ }
+ MI32HexStringToBytes(_keyStr,_key);
+ MIBLEsensors[_numberOfDevices].key = _key;
+ }
+ }
+ if(!_error){
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: added %u devices from config file"), _numberOfDevices + 1);
+ }
+ }
+ free(_filebuf);
+ }
+}
+
+void MI32saveConfig(){
+ const size_t _buf_size = 2048;
+ char * _filebuf = (char*) malloc(_buf_size);
+ _filebuf[0] = '[';
+ uint32_t _pos = 1;
+ for(auto _sensor: MIBLEsensors){
+ char _MAC[13];
+ ToHex_P(_sensor.MAC,6,_MAC,13);
+ char _key[33];
+ _key[0] = 0;
+ if(_sensor.key != nullptr){
+ ToHex_P(_sensor.key,16,_key,33);
+ }
+ uint32_t _inc = snprintf_P(_filebuf+_pos,200,PSTR("{\"MAC\":\"%s\",\"PID\":\"%04x\",\"key\":\"%s\"},"),_MAC,kMI32DeviceID[_sensor.type - 1],_key);
+ _pos += _inc;
+ }
+ _filebuf[_pos-1] = ']';
+ _filebuf[_pos] = '\0';
+ if (_pos>2){
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: %s"), _filebuf);
+ if (TfsSaveFile("/mi32cfg",(uint8_t*)_filebuf,_pos+1)) {
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: %u bytes written to config"), _pos+1);
+ }
+ }
+ else{
+ AddLog(LOG_LEVEL_ERROR,PSTR("M32: nothing written to config"));
+ }
+ free(_filebuf);
+}
+
/*********************************************************************************************\
* Task section
\*********************************************************************************************/
+void MI32suspendScanTask(void){
+ if (MI32.ScanTask != nullptr) vTaskSuspend(MI32.ScanTask);
+}
+
void MI32StartTask(uint32_t task){
switch(task){
case MI32_TASK_SCAN:
- if (MI32.mode.canScan == 0 || MI32.mode.willConnect == 1) return;
+ if (MI32.mode.willConnect == 1) return;
if (MI32.mode.runningScan == 1 || MI32.mode.connected == 1) return;
MI32StartScanTask();
break;
case MI32_TASK_CONN:
if (MI32.mode.canConnect == 0 || MI32.mode.willConnect == 1 ) return;
if (MI32.mode.connected == 1) return;
- MI32StartSensorTask();
- break;
- case MI32_TASK_TIME:
- if (MI32.mode.shallSetTime == 0) return;
- MI32StartTimeTask();
- break;
- case MI32_TASK_BATT:
- if (MI32.mode.willReadBatt == 1) return;
- switch(MIBLEsensors[MI32.state.sensor].type) {
- case LYWSD03MMC: case MHOC401: // the "original" battery value is crap ...
- MI32.mode.willReadBatt = 1;
- MI32StartSensorTask(); // ... but the part of the temp/hum-message is good!
- break;
- default:
- MI32StartBatteryTask();
- }
- break;
- case MI32_TASK_UNIT:
- if (MI32.mode.shallSetUnit == 0) return;
- MI32StartUnitTask();
+ MI32StartConnectionTask();
break;
default:
break;
@@ -828,49 +1014,27 @@ void MI32StartTask(uint32_t task){
}
bool MI32ConnectActiveSensor(){ // only use inside a task !!
- MI32.mode.connected = 0;
-
- NimBLEAddress _address = NimBLEAddress(MIBLEsensors[MI32.state.sensor].MAC);
+ NimBLEAddress _address = NimBLEAddress(MI32.conCtx->MAC, MI32.conCtx->addrType);
+ MI32Client = nullptr;
if(NimBLEDevice::getClientListSize()) {
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: found any clients in the list"),D_CMND_MI32);
MI32Client = NimBLEDevice::getClientByPeerAddress(_address);
- if(MI32Client){
- // Should be impossible
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: got connected client"),D_CMND_MI32);
- }
- else {
- // Should be the norm after the first iteration
- MI32Client = NimBLEDevice::getDisconnectedClient();
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: got disconnected client"),D_CMND_MI32);
- }
}
-
- if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
- MI32.mode.willConnect = 0;
- DEBUG_SENSOR_LOG(PSTR("%s: max connection already reached"),D_CMND_MI32);
- return false;
- }
- if(!MI32Client) {
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: will create client"),D_CMND_MI32);
- MI32Client = NimBLEDevice::createClient();
+ if (!MI32Client){
+ MI32Client = NimBLEDevice::createClient(_address);
MI32Client->setClientCallbacks(&MI32SensorCB , false);
- MI32Client->setConnectionParams(12,12,0,48);
- MI32Client->setConnectTimeout(30);
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: did create new client"),D_CMND_MI32);
}
- vTaskDelay(300/ portTICK_PERIOD_MS);
- if (!MI32Client->connect(_address,false)) {
+ if (!MI32Client->connect(false)) {
MI32.mode.willConnect = 0;
- // NimBLEDevice::deleteClient(MI32Client);
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: did not connect client"),D_CMND_MI32);
+ NimBLEDevice::deleteClient(MI32Client);
+ // AddLog(LOG_LEVEL_ERROR,PSTR("M32: did not connect client"));
return false;
}
return true;
- // }
}
void MI32StartScanTask(){
if (MI32.mode.connected) return;
+ if(MI32.ScanTask!=nullptr) vTaskDelete(MI32.ScanTask);
MI32.mode.runningScan = 1;
xTaskCreatePinnedToCore(
MI32ScanTask, /* Function to implement the task */
@@ -878,447 +1042,287 @@ void MI32StartScanTask(){
2048, /* Stack size in words */
NULL, /* Task input parameter */
0, /* Priority of the task */
- NULL, /* Task handle. */
+ &MI32.ScanTask, /* Task handle. */
0); /* Core where the task should run */
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),D_CMND_MI32);
}
void MI32ScanTask(void *pvParameters){
+ if(MI32.mode.didGetConfig){
+ vTaskDelay(5000/ portTICK_PERIOD_MS);
+ }
if (MI32Scan == nullptr) MI32Scan = NimBLEDevice::getScan();
- // DEBUG_SENSOR_LOG(PSTR("%s: Scan Cache Length: %u"),D_CMND_MI32, MI32Scan->getResults().getCount());
+
MI32Scan->setInterval(70);
MI32Scan->setWindow(50);
MI32Scan->setAdvertisedDeviceCallbacks(&MI32ScanCallbacks,true);
MI32Scan->setActiveScan(false);
+ MI32Scan->setMaxResults(0);
MI32Scan->start(0, MI32scanEndedCB, true); // never stop scanning, will pause automatically while connecting
-
+ MI32.infoMsg = MI32_START_SCANNING;
+
uint32_t timer = 0;
for(;;){
- if(MI32.mode.shallClearResults){
- MI32Scan->clearResults();
- MI32.mode.shallClearResults=0;
- }
vTaskDelay(10000/ portTICK_PERIOD_MS);
}
vTaskDelete( NULL );
}
-void MI32StartSensorTask(){
- MI32.mode.willConnect = 1;
- switch(MIBLEsensors[MI32.state.sensor].type){
- case LYWSD03MMC: case MHOC401:
- break;
- default:
- MI32.mode.willConnect = 0;
- return;
- }
+bool MI32StartConnectionTask(){
+ if(MI32.conCtx == nullptr) return false;
+ if(MI32.conCtx->buffer == nullptr) return false;
+ MI32.mode.willConnect = 1;
+ MI32Scan->stop();
+ MI32suspendScanTask();
xTaskCreatePinnedToCore(
- MI32SensorTask, /* Function to implement the task */
- "MI32SensorTask", /* Name of the task */
+ MI32ConnectionTask, /* Function to implement the task */
+ "MI32ConnectionTask", /* Name of the task */
4096, /* Stack size in words */
NULL, /* Task input parameter */
- 15, /* Priority of the task */
- NULL, /* Task handle. */
+ 2, /* Priority of the task */
+ &MI32.ConnTask, /* Task handle. */
0); /* Core where the task should run */
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start sensor connections"),D_CMND_MI32);
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor);
+ return true;
}
-void MI32SensorTask(void *pvParameters){
+void MI32ConnectionTask(void *pvParameters){
+ MI32.mode.connected = 0;
+ MI32.conCtx->error = MI32_CONN_NO_ERROR;
if (MI32ConnectActiveSensor()){
+ MI32.mode.readingDone = 0;
uint32_t timer = 0;
while (MI32.mode.connected == 0){
if (timer>1000){
MI32Client->disconnect();
- // NimBLEDevice::deleteClient(MI32Client);
+ NimBLEDevice::deleteClient(MI32Client);
MI32.mode.willConnect = 0;
- MI32.mode.willReadBatt = 0; //could be a "battery task" for LYWSD03MMC or MHO-C401
+ MI32.mode.triggerBerryConnCB = 1;
+ MI32.conCtx->error = MI32_CONN_NO_CONNECT; // not connected
+ MI32StartTask(MI32_TASK_SCAN);
vTaskDelay(100/ portTICK_PERIOD_MS);
vTaskDelete( NULL );
}
timer++;
vTaskDelay(10/ portTICK_PERIOD_MS);
}
+ NimBLERemoteService* pSvc = nullptr;
+ NimBLERemoteCharacteristic* pChr = nullptr;
- timer = 150;
- switch(MIBLEsensors[MI32.state.sensor].type){
- case LYWSD03MMC: case MHOC401:
- MI32.mode.readingDone = 0;
- if(MI32connectLYWSD03forNotification()) timer=0;
- break;
- default:
- break;
+ pSvc = MI32Client->getService(MI32.conCtx->serviceUUID);
+ if(pSvc) {
+ pChr = pSvc->getCharacteristic(MI32.conCtx->charUUID);
}
+ else{
+ MI32.conCtx->error = MI32_CONN_NO_SERVICE;
+ }
+ if (pChr){
+ switch(MI32.conCtx->operation){
+ case 11:
+ if(pChr->canRead()) {
+ std::string _val = pChr->readValue();
+ MI32.conCtx->buffer[0] = (uint8_t)_val.size();
+ const char *_c_val = _val.c_str();
+ memcpy( MI32.conCtx->buffer + 1,_c_val,MI32.conCtx->buffer[0]);
+ }
+ else{
+ MI32.conCtx->error = MI32_CONN_CAN_NOT_READ;
+ }
+ break;
+ case 13:
+ if(pChr->canNotify()) {
+ if(pChr->subscribe(true,MI32notifyCB,false)) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: subscribe"));
+ }
+ else{
+ MI32.conCtx->error = MI32_CONN_CAN_NOT_NOTIFY;
+ }
+ break;
+ case 12:
+ if(pChr->canWrite()) {
+ uint8_t len = MI32.conCtx->buffer[0];
+ if(pChr->writeValue(MI32.conCtx->buffer + 1,len,true)) { // true is important !
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: write op done"));
+ }
+ else{
+ MI32.conCtx->error = MI32_CONN_DID_NOT_WRITE;
+ }
+ }
+ else{
+ MI32.conCtx->error = MI32_CONN_CAN_NOT_WRITE;
+ }
+ MI32.mode.readingDone = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ MI32.conCtx->error = MI32_CONN_NO_CHARACTERISTIC;
+ }
+ timer = 0;
- while (!MI32.mode.readingDone){
- if (timer>150){
+ while (timer<150){
+ if (MI32.mode.readingDone){
break;
}
+ else{
+ MI32.conCtx->error = MI32_CONN_NOTIFY_TIMEOUT; //did not read on notify - timeout
+ }
timer++;
vTaskDelay(100/ portTICK_PERIOD_MS);
- }
- MI32Client->disconnect();
- DEBUG_SENSOR_LOG(PSTR("%s: requested disconnect"),D_CMND_MI32);
+
}
-
- MI32.mode.connected = 0;
- vTaskDelete( NULL );
-}
-
-bool MI32connectLYWSD03forNotification(){
- NimBLERemoteService* pSvc = nullptr;
- NimBLERemoteCharacteristic* pChr = nullptr;
- static BLEUUID serviceUUID(0xebe0ccb0,0x7a0a,0x4b0c,0x8a1a6ff2997da3a6);
- static BLEUUID charUUID(0xebe0ccc1,0x7a0a,0x4b0c,0x8a1a6ff2997da3a6);
- pSvc = MI32Client->getService(serviceUUID);
- if(pSvc) {
- pChr = pSvc->getCharacteristic(charUUID);
+ MI32Client->disconnect();
+ DEBUG_SENSOR_LOG(PSTR("M32: requested disconnect"));
}
- if (pChr){
- if(pChr->canNotify()) {
- if(pChr->subscribe(true,MI32notifyCB,false)) {
- return true;
- }
- }
+ else{
+ MI32.conCtx->error = MI32_CONN_NO_CONNECT; // not connected
}
- return false;
-}
-
-void MI32StartTimeTask(){
- MI32.mode.willConnect = 1;
- xTaskCreatePinnedToCore(
- MI32TimeTask, /* Function to implement the task */
- "MI32TimeTask", /* Name of the task */
- 4096, /* Stack size in words */
- NULL, /* Task input parameter */
- 15, /* Priority of the task */
- NULL, /* Task handle. */
- 0); /* Core where the task should run */
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start time set"),D_CMND_MI32);
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor);
-}
-
-void MI32TimeTask(void *pvParameters){
- if (MIBLEsensors[MI32.state.sensor].type != LYWSD02 && MIBLEsensors[MI32.state.sensor].type != MHOC303) {
- MI32.mode.shallSetTime = 0;
- vTaskDelete( NULL );
- }
- if(MI32ConnectActiveSensor()){
- uint32_t timer = 0;
- while (MI32.mode.connected == 0){
- if (timer>1000){
- break;
- }
- timer++;
- vTaskDelay(10/ portTICK_PERIOD_MS);
- }
-
- NimBLERemoteService* pSvc = nullptr;
- NimBLERemoteCharacteristic* pChr = nullptr;
- static BLEUUID serviceUUID(0xEBE0CCB0,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6);
- static BLEUUID charUUID(0xEBE0CCB7,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6);
- pSvc = MI32Client->getService(serviceUUID);
- if(pSvc) {
- pChr = pSvc->getCharacteristic(charUUID);
-
- }
- if (pChr){
- if(pChr->canWrite()) {
- union {
- uint8_t buf[5];
- uint32_t time;
- } _utc;
- _utc.time = Rtc.utc_time;
- _utc.buf[4] = Rtc.time_timezone / 60;
-
- if(!pChr->writeValue(_utc.buf,sizeof(_utc.buf),true)) { // true is important !
- MI32.mode.willConnect = 0;
- MI32Client->disconnect();
- }
- else {
- MI32.mode.shallSetTime = 0;
- MI32.mode.willSetTime = 0;
- }
- }
- }
- MI32Client->disconnect();
- }
-
MI32.mode.connected = 0;
- MI32.mode.canScan = 1;
+ MI32.mode.triggerBerryConnCB = 1;
+ MI32StartTask(MI32_TASK_SCAN);
vTaskDelete( NULL );
}
-void MI32StartUnitTask(){
- MI32.mode.willConnect = 1;
- xTaskCreatePinnedToCore(
- MI32UnitTask, /* Function to implement the task */
- "MI32UnitTask", /* Name of the task */
- 4096, /* Stack size in words */
- NULL, /* Task input parameter */
- 15, /* Priority of the task */
- NULL, /* Task handle. */
- 0); /* Core where the task should run */
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start unit set"),D_CMND_MI32);
- // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor);
-}
-
-void MI32UnitTask(void *pvParameters){
- if (MIBLEsensors[MI32.state.sensor].type != LYWSD02 && MIBLEsensors[MI32.state.sensor].type != MHOC303) {
- MI32.mode.shallSetUnit = 0;
- vTaskDelete( NULL );
- }
-
- if(MI32ConnectActiveSensor()){
- uint32_t timer = 0;
- while (MI32.mode.connected == 0){
- if (timer>1000){
- break;
- }
- timer++;
- vTaskDelay(10/ portTICK_PERIOD_MS);
- }
-
- NimBLERemoteService* pSvc = nullptr;
- NimBLERemoteCharacteristic* pChr = nullptr;
- static BLEUUID serviceUUID(0xEBE0CCB0,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6);
- static BLEUUID charUUID(0xEBE0CCBE,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6);
- pSvc = MI32Client->getService(serviceUUID);
- if(pSvc) {
- pChr = pSvc->getCharacteristic(charUUID);
- }
-
- if(pChr->canRead()){
- uint8_t curUnit;
- const char *buf = pChr->readValue().c_str();
- if( buf[0] != 0 && buf[0]<101 ){
- curUnit = buf[0];
- }
-
- if(pChr->canWrite()) {
- curUnit = curUnit == 0x01?0xFF:0x01; // C/F
-
- if(!pChr->writeValue(&curUnit,sizeof(curUnit),true)) { // true is important !
- MI32.mode.willConnect = 0;
- MI32Client->disconnect();
- }
- else {
- MI32.mode.shallSetUnit = 0;
- MI32.mode.willSetUnit = 0;
- }
- }
- }
- MI32Client->disconnect();
- }
-
- MI32.mode.connected = 0;
- MI32.mode.canScan = 1;
- vTaskDelete( NULL );
-}
-
-void MI32StartBatteryTask(){
- if (MI32.mode.connected) return;
- MI32.mode.willReadBatt = 1;
- MI32.mode.willConnect = 1;
- MI32.mode.canScan = 0;
-
- switch (MIBLEsensors[MI32.state.sensor].type){
- case LYWSD03MMC: case MJ_HT_V1: case CGG1: case NLIGHT: case MJYD2S: case YEERC: case MHOC401:
- MI32.mode.willConnect = 0;
- MI32.mode.willReadBatt = 0;
- return;
- }
-
- xTaskCreatePinnedToCore(
- MI32BatteryTask, /* Function to implement the task */
- "MI32BatteryTask", /* Name of the task */
- 4096, /* Stack size in words */
- NULL, /* Task input parameter */
- 15, /* Priority of the task */
- NULL, /* Task handle. */
- 0); /* Core where the task should run */
-}
-
-void MI32BatteryTask(void *pvParameters){
- // all reported battery values are probably crap, but we allow the reading on demand
-
- MI32.mode.connected = 0;
- if(MI32ConnectActiveSensor()){
- uint32_t timer = 0;
- while (MI32.mode.connected == 0){
- if (timer>1000){
- break;
- }
- timer++;
- vTaskDelay(30/ portTICK_PERIOD_MS);
- }
-
- switch(MIBLEsensors[MI32.state.sensor].type){
- case FLORA: case LYWSD02: case CGD1:
- MI32batteryRead(MIBLEsensors[MI32.state.sensor].type);
- break;
- }
- MI32Client->disconnect();
- }
- MI32.mode.willReadBatt = 0;
- MI32.mode.connected = 0;
- vTaskDelete( NULL );
-}
-
-void MI32batteryRead(uint32_t _type){
- uint32_t timer = 0;
- while (!MI32.mode.connected){
- if (timer>1000){
- break;
- }
- timer++;
- vTaskDelay(10/ portTICK_PERIOD_MS);
- }
- DEBUG_SENSOR_LOG(PSTR("%s connected for battery"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] );
- NimBLERemoteService* pSvc = nullptr;
- NimBLERemoteCharacteristic* pChr = nullptr;
-
- switch(_type){
- case FLORA:
- {
- static BLEUUID _serviceUUID(0x00001204,0x0000,0x1000,0x800000805f9b34fb);
- static BLEUUID _charUUID(0x00001a02,0x0000,0x1000,0x800000805f9b34fb);
- pSvc = MI32Client->getService(_serviceUUID);
- if(pSvc) {
- pChr = pSvc->getCharacteristic(_charUUID);
- }
- }
- break;
- case LYWSD02:
- {
- static BLEUUID _serviceUUID(0xEBE0CCB0,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6);
- static BLEUUID _charUUID(0xEBE0CCC4,0x7A0A,0x4B0C,0x8A1A6FF2997DA3A6);
- pSvc = MI32Client->getService(_serviceUUID);
- if(pSvc) {
- pChr = pSvc->getCharacteristic(_charUUID);
- }
- }
- break;
- case CGD1:
- {
- static BLEUUID _serviceUUID((uint16_t)0x180F);
- static BLEUUID _charUUID((uint16_t)0x2A19);
- pSvc = MI32Client->getService(_serviceUUID);
- if(pSvc) {
- pChr = pSvc->getCharacteristic(_charUUID);
- }
- }
- break;
- }
-
- if (pChr){
- DEBUG_SENSOR_LOG(PSTR("%s: got %s char %s"),D_CMND_MI32, kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1], pChr->getUUID().toString().c_str());
- if(pChr->canRead()) {
- const char *buf = pChr->readValue().c_str();
- MI32readBat((char*)buf);
- }
- }
- MI32.mode.readingDone = 1;
-}
-
/*********************************************************************************************\
* parse the response from advertisements
\*********************************************************************************************/
void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){
+
float _tempFloat;
- mi_beacon_t _beacon;
+ mi_beacon_t* _beacon = (mi_beacon_t*)_buf;
+ mi_payload_t _payload;
- if (MIBLEsensors[_slot].type==MJ_HT_V1 || MIBLEsensors[_slot].type==CGG1 || MIBLEsensors[_slot].type==YEERC){
- memcpy((uint8_t*)&_beacon+1,(uint8_t*)_buf, sizeof(_beacon)-1); // shift by one byte for the MJ_HT_V1 DANGER!!!
- memcpy((uint8_t*)&_beacon.MAC,(uint8_t*)&_beacon.MAC+1,6); // but shift back the MAC
- _beacon.counter = _buf[4]; // restore the counter
- }
- else{
- memcpy((char *)&_beacon, _buf, _bufSize);
- }
+ MIBLEsensors[_slot].lastCnt = _beacon->counter;
- MIBLEsensors[_slot].lastCnt = _beacon.counter;
-#ifdef USE_MI_DECRYPTION
- int decryptRet = 0;
- switch(MIBLEsensors[_slot].type){
- case LYWSD03MMC: case MHOC401:
- if (_beacon.frame == 0x5858){
- decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize, LYWSD03MMC); //start with PID
- // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)&_beacon.productID,_bufSize);
- }
- else return; // 0x3058 holds no data, TODO: check for unpaired devices, that need connections
- break;
- case MJYD2S:
- AddLog(LOG_LEVEL_DEBUG,PSTR("MJYD2S: %x"),_beacon.frame);
- if (_beacon.frame == 0x5948){ // Now let's build/recreate a special MiBeacon
- memmove((uint8_t*)&_beacon.MAC+6,(uint8_t*)&_beacon.MAC, _bufSize); // shift payload by the size of the MAC = 6 bytes
- memcpy((uint8_t*)&_beacon.MAC,MIBLEsensors[_slot].MAC,6); // now insert the real MAC from our internal vector
- _bufSize+=6; // the packet has grown
- MI32_ReverseMAC(_beacon.MAC); // payload MAC is always reversed
- AddLog(LOG_LEVEL_DEBUG,PSTR("MJYD2S: special packet"));
- }
- if (_beacon.frame != 0x5910){
- decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize,MJYD2S); //start with PID
- }
- break;
+#ifdef USE_MI_EXT_GUI
+ bitSet(MI32.widgetSlot,_slot);
+#endif //USE_MI_EXT_GUI
+if(_beacon->frame.includesObj == 0){
+ return; //nothing to parse
+}
+
+int decryptRet = 0;
+if(_beacon->frame.isEncrypted){
+ decryptRet = MI32_decryptPacket(_buf,_bufSize, (uint8_t*)&_payload,_slot);
}
-if(decryptRet!=0){
+else{
+ uint32_t _offset = (_beacon->frame.includesCapability)?0:1;
+ uint32_t _payloadSize = (_beacon->frame.includesCapability)?_beacon->payload.size:_beacon->payload.ten;
+ if(_beacon->frame.includesMAC && _beacon->frame.includesObj) {
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: offset %u, size: %u"),_offset,_payloadSize);
+ memcpy((uint8_t*)&_payload,(uint8_t*)(&_beacon->payload)-_offset, _payloadSize + 3);
+ // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)&_payload,_payloadSize + 3);
+ }
+ }
+if(decryptRet<0){
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Decryption failed with error: %d"),decryptRet);
+ MIBLEsensors[_slot].feature.hasWrongKey = 1;
return;
}
-#endif //USE_MI_DECRYPTION
+// if (_beacon->frame.solicited){
+// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: sensor unbonded: %s"),kMI32DeviceType[MIBLEsensors[_slot].type-1]);
+// }
+// if (_beacon->frame.registered){
+// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: registered: %s"),kMI32DeviceType[MIBLEsensors[_slot].type-1]);
+// }
- if(MIBLEsensors[_slot].type==6){
- DEBUG_SENSOR_LOG(PSTR("CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type);
- return;
- }
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_beacon.type);
- switch(_beacon.type){
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_payload.type);
+ MIBLEsensors[_slot].lastTime = millis();
+ switch(_payload.type){
case 0x01:
- MIBLEsensors[_slot].Btn=_beacon.Btn.num + (_beacon.Btn.longPress/2)*6;
+ if(_payload.Btn.type == 4){ //knob dimmer
+ if(_payload.Btn.num == 0){
+ if(_payload.Btn.value<128){
+ AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate right: %u"),_payload.Btn.value);
+ }
+ else{
+ AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate left: %u"),256 - _payload.Btn.value);
+ }
+ }
+ else if(_payload.Btn.num<128){
+ AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate right: %u"),_payload.Btn.num);
+ }
+ else{
+ AddLog(LOG_LEVEL_DEBUG,PSTR("Rotate left: %u"),256 - _payload.Btn.num);
+ }
+ return; //TODO: implement MQTT later
+ }
+ MIBLEsensors[_slot].Btn=_payload.Btn.num + (_payload.Btn.type/2)*6;
MIBLEsensors[_slot].eventType.Btn = 1;
MI32.mode.shallTriggerTele = 1;
+#ifdef USE_MI_HOMEKIT
+ {
+ // {uint32_t _button = _payload.Btn.num + (_payload.Btn.type/2)*6;
+ uint32_t _singleLong = 0;
+ if(MIBLEsensors[_slot].Btn>5){
+ MIBLEsensors[_slot].Btn = MIBLEsensors[_slot].Btn - 6;
+ _singleLong = 2;
+ }
+ if(MIBLEsensors[_slot].Btn>5) break; //
+ if((void**)MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn] != nullptr){
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Send Button %u: SingleLong:%u, pointer: %x"), MIBLEsensors[_slot].Btn,_singleLong,MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn] );
+ mi_homekit_update_value(MIBLEsensors[_slot].button_hap_service[MIBLEsensors[_slot].Btn], (float)_singleLong, 0x01);
+ }
+ }
+#endif //USE_MI_HOMEKIT
// AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn );
break;
case 0x04:
- _tempFloat=(float)(_beacon.temp)/10.0f;
+ _tempFloat=(float)(_payload.temp)/10.0f;
if(_tempFloat<60){
MIBLEsensors[_slot].temp=_tempFloat;
MIBLEsensors[_slot].eventType.temp = 1;
DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated"));
}
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 4: U16: %u Temp"), _beacon.temp );
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].temp_hap_service, _tempFloat, 0x04);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ MI32addHistory(MIBLEsensors[_slot].temp_history, _tempFloat, 0);
+#endif //USE_MI_EXT_GUI
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 4: U16: %u Temp"), _payload.temp );
break;
case 0x06:
- _tempFloat=(float)(_beacon.hum)/10.0f;
+ _tempFloat=(float)(_payload.hum)/10.0f;
if(_tempFloat<101){
MIBLEsensors[_slot].hum=_tempFloat;
MIBLEsensors[_slot].eventType.hum = 1;
DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated"));
}
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 6: U16: %u Hum"), _beacon.hum);
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].hum_hap_service, _tempFloat,0x06);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ MI32addHistory(MIBLEsensors[_slot].hum_history, _tempFloat, 1);
+#endif //USE_MI_EXT_GUI
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 6: U16: %u Hum"), _payload.hum);
break;
case 0x07:
- MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff;
+ MIBLEsensors[_slot].lux=_payload.lux & 0x00ffffff;
if(MIBLEsensors[_slot].type==MJYD2S){
MIBLEsensors[_slot].eventType.noMotion = 1;
}
MIBLEsensors[_slot].eventType.lux = 1;
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff);
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].light_hap_service, (float)MIBLEsensors[_slot].lux,0x07);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ MI32addHistory(MIBLEsensors[_slot].lux_history, (float)MIBLEsensors[_slot].lux, 2);
+#endif //USE_MI_EXT_GUI
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 7: U24: %u Lux"), _payload.lux & 0x00ffffff);
break;
case 0x08:
- MIBLEsensors[_slot].moisture=_beacon.moist;
+ MIBLEsensors[_slot].moisture=_payload.moist;
MIBLEsensors[_slot].eventType.moist = 1;
DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated"));
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 8: U8: %u Moisture"), _beacon.moist);
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 8: U8: %u Moisture"), _payload.moist);
break;
case 0x09:
- MIBLEsensors[_slot].fertility=_beacon.fert;
+ MIBLEsensors[_slot].fertility=_payload.fert;
MIBLEsensors[_slot].eventType.fert = 1;
DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated"));
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 9: U16: %u Fertility"), _beacon.fert);
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 9: U16: %u Fertility"), _payload.fert);
break;
case 0x0a:
if(MI32.option.ignoreBogusBattery){
@@ -1326,56 +1330,85 @@ if(decryptRet!=0){
break;
}
}
- if(_beacon.bat<101){
- MIBLEsensors[_slot].bat = _beacon.bat;
+ if(_payload.bat<101){
+ MIBLEsensors[_slot].bat = _payload.bat;
MIBLEsensors[_slot].eventType.bat = 1;
DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated"));
- }
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode a: U8: %u %%"), _beacon.bat);
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].bat_hap_service, (float)_payload.bat,0xa);
+#endif //USE_MI_HOMEKIT
+ }
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode a: U8: %u %%"), _payload.bat);
break;
case 0x0d:
- _tempFloat=(float)(_beacon.HT.temp)/10.0f;
+ _tempFloat=(float)(_payload.HT.temp)/10.0f;
if(_tempFloat<60){
MIBLEsensors[_slot].temp = _tempFloat;
DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated"));
}
- _tempFloat=(float)(_beacon.HT.hum)/10.0f;
+ _tempFloat=(float)(_payload.HT.hum)/10.0f;
if(_tempFloat<100){
MIBLEsensors[_slot].hum = _tempFloat;
DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated"));
}
MIBLEsensors[_slot].eventType.tempHum = 1;
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum);
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode d: U16: %x Temp U16: %x Hum"), _payload.HT.temp, _payload.HT.hum);
break;
-#ifdef USE_MI_DECRYPTION
+
case 0x0f:
- if (_beacon.ten!=0) break;
+ if (_payload.ten!=0) break;
MIBLEsensors[_slot].eventType.motion = 1;
- MIBLEsensors[_slot].lastTime = millis();
MIBLEsensors[_slot].events++;
- MIBLEsensors[_slot].lux = _beacon.lux;
+ MIBLEsensors[_slot].lux = _payload.lux & 0x00ffffff;
MIBLEsensors[_slot].eventType.lux = 1;
MIBLEsensors[_slot].NMT = 0;
MI32.mode.shallTriggerTele = 1;
- // AddLog(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux );
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].motion_hap_service, (float)1,0x0f);
+ mi_homekit_update_value(MIBLEsensors[_slot].light_hap_service, (float)_payload.lux,0x07);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ MI32addHistory(MIBLEsensors[_slot].lux_history, (float)MIBLEsensors[_slot].lux, 2);
+#endif //USE_MI_EXT_GUI
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("motion: primary"),MIBLEsensors[_slot].lux );
break;
+ case 0x14:
+ MIBLEsensors[_slot].leak = _payload.leak;
+ MIBLEsensors[_slot].eventType.leak = 1;
+ if(_payload.leak>0) MI32.mode.shallTriggerTele = 1;
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].leak_hap_service, (float)_payload.leak,0x14);
+#endif //USE_MI_HOMEKIT
+ break;
case 0x17:
- MIBLEsensors[_slot].NMT = _beacon.NMT;
+ MIBLEsensors[_slot].NMT = _payload.NMT;
MIBLEsensors[_slot].eventType.NMT = 1;
MI32.mode.shallTriggerTele = 1;
- // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _beacon.NMT);
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _payload.NMT);
break;
-#endif //USE_MI_DECRYPTION
+ case 0x19:
+ MIBLEsensors[_slot].door = _payload.door;
+ MIBLEsensors[_slot].eventType.door = 1;
+ MIBLEsensors[_slot].events++;
+ MI32.mode.shallTriggerTele = 1;
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].door_sensor_hap_service, (float)_payload.door,0x19);
+#endif //USE_MI_HOMEKIT
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 19: %u"), _payload.door);
+ break;
+
default:
if (MIBLEsensors[_slot].type==NLIGHT){
- MIBLEsensors[_slot].eventType.motion = 1; //PIR
+ MIBLEsensors[_slot].eventType.motion = 1; //motion
MIBLEsensors[_slot].events++;
MIBLEsensors[_slot].NMT = 0;
- MIBLEsensors[_slot].lastTime = millis();
MI32.mode.shallTriggerTele = 1;
- // AddLog(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux );
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].motion_hap_service, (float)1,0x0f);
+#endif //USE_MI_HOMEKIT
}
else{
+ //unknown payload
AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_buf,_bufSize);
}
break;
@@ -1387,18 +1420,41 @@ if(decryptRet!=0){
void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){
ATCPacket_t *_packet = (ATCPacket_t*)_buf;
- uint32_t _slot = MIBLEgetSensorSlot(_packet->MAC, 0x0a1c, _packet->frameCnt); // This must be a hard-coded fake ID
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
+ bool isATC = (length == 0x0d);
+ uint32_t _slot;
+ if (isATC) _slot = MIBLEgetSensorSlot(_packet->MAC, 0x0a1c, _packet->A.frameCnt); // This must be a hard-coded fake ID
+ else {
+ MI32_ReverseMAC(_packet->MAC);
+ _slot = MIBLEgetSensorSlot(_packet->MAC, 0x944a, _packet->P.frameCnt); // ... and again
+ }
if(_slot==0xff) return;
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
MIBLEsensors[_slot].RSSI=RSSI;
+ MIBLEsensors[_slot].lastTime = millis();
+ if(isATC){
+ MIBLEsensors[_slot].temp = (float)(int16_t(__builtin_bswap16(_packet->A.temp)))/10.0f;
+ MIBLEsensors[_slot].hum = (float)_packet->A.hum;
+ MIBLEsensors[_slot].bat = _packet->A.batPer;
+ }
+ else{
+ MIBLEsensors[_slot].temp = (float)(_packet->P.temp)/100.0f;
+ MIBLEsensors[_slot].hum = (float)_packet->P.hum/100.0f;
+ MIBLEsensors[_slot].bat = _packet->P.batPer;
+ }
- MIBLEsensors.at(_slot).temp = (float)(int16_t(__builtin_bswap16(_packet->temp)))/10.0f;
- MIBLEsensors.at(_slot).hum = (float)_packet->hum;
MIBLEsensors[_slot].eventType.tempHum = 1;
- MIBLEsensors.at(_slot).bat = _packet->batPer;
MIBLEsensors[_slot].eventType.bat = 1;
-
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].temp_hap_service, MIBLEsensors.at(_slot).temp,0x04);
+ mi_homekit_update_value(MIBLEsensors[_slot].hum_hap_service, MIBLEsensors.at(_slot).hum,0x06);
+ mi_homekit_update_value(MIBLEsensors[_slot].bat_hap_service, (float)MIBLEsensors.at(_slot).bat,0x0a);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ bitSet(MI32.widgetSlot,_slot);
+ MI32addHistory(MIBLEsensors[_slot].temp_history, (float)MIBLEsensors[_slot].temp, 0);
+ MI32addHistory(MIBLEsensors[_slot].hum_history, (float)MIBLEsensors[_slot].hum, 1);
+#endif //USE_MI_EXT_GUI
MIBLEsensors[_slot].shallSendMQTT = 1;
if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1;
@@ -1408,9 +1464,10 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI
uint8_t _addr[6];
memcpy(_addr,addr,6);
uint32_t _slot = MIBLEgetSensorSlot(_addr, 0x0576, 0); // This must be hard-coded, no object-id in Cleargrass-packet, we have no packet counter too
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
if(_slot==0xff) return;
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
MIBLEsensors[_slot].RSSI=RSSI;
+ MIBLEsensors[_slot].lastTime = millis();
cg_packet_t _packet;
memcpy((char*)&_packet,_buf,sizeof(_packet));
switch (_packet.mode){
@@ -1418,21 +1475,33 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI
float _tempFloat;
_tempFloat=(float)(_packet.temp)/10.0f;
if(_tempFloat<60){
- MIBLEsensors.at(_slot).temp = _tempFloat;
+ MIBLEsensors[_slot].temp = _tempFloat;
MIBLEsensors[_slot].eventType.temp = 1;
DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated"));
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].temp_hap_service, _tempFloat,0x04);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ MI32addHistory(MIBLEsensors[_slot].temp_history, (float)MIBLEsensors[_slot].temp, 0);
+#endif //USE_MI_EXT_GUI
}
_tempFloat=(float)(_packet.hum)/10.0f;
if(_tempFloat<100){
- MIBLEsensors.at(_slot).hum = _tempFloat;
+ MIBLEsensors[_slot].hum = _tempFloat;
MIBLEsensors[_slot].eventType.hum = 1;
DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated"));
+#ifdef USE_MI_HOMEKIT
+ mi_homekit_update_value(MIBLEsensors[_slot].hum_hap_service, _tempFloat,0x06);
+#endif //USE_MI_HOMEKIT
+#ifdef USE_MI_EXT_GUI
+ MI32addHistory(MIBLEsensors[_slot].hum_history, (float)MIBLEsensors[_slot].hum, 1);
+#endif //USE_MI_EXT_GUI
}
DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), _packet.temp, _packet.hum);
break;
case 0x0102:
if(_packet.bat<101){
- MIBLEsensors.at(_slot).bat = _packet.bat;
+ MIBLEsensors[_slot].bat = _packet.bat;
MIBLEsensors[_slot].eventType.bat = 1;
DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated"));
}
@@ -1443,6 +1512,9 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI
if(MIBLEsensors[_slot].eventType.raw == 0) return;
MIBLEsensors[_slot].shallSendMQTT = 1;
if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1;
+#ifdef USE_MI_EXT_GUI
+ bitSet(MI32.widgetSlot,_slot);
+#endif //USE_MI_EXT_GUI
}
void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) {
@@ -1460,221 +1532,6 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) {
}
}
-/**
- * @brief Parse a BLE advertisement packet
- *
- * @param payload
- * @param payloadLength
- * @param CID
- * @param SVC
- * @param UUID
- */
-void MI32ParseGenericBeacon(uint8_t* payload, size_t payloadLength, uint16_t* CID, uint16_t*SVC, uint16_t* UUID){
- AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Beacon:____________"));
- for (uint32_t i = 0; i19) {
- AddLog(LOG_LEVEL_INFO,PSTR("M32: Scan buffer full"));
- MI32.state.beaconScanCounter = 1;
- return;
- }
- for(auto _scanResult : MIBLEscanResult){
- if(memcmp(addr,_scanResult.MAC,6)==0){
- // AddLog(LOG_LEVEL_INFO,PSTR("M32: known device"));
- return;
- }
- }
- scan_entry_t _new;
- _new.RSSI = RSSI;
- _new.CID = 0;
- _new.SVC = 0;
- _new.UUID = 0;
- memcpy(_new.MAC,addr,sizeof(_new.MAC));
- MI32ParseGenericBeacon(payload,payloadLength,&_new.CID,&_new.SVC,&_new.UUID);
- MIBLEscanResult.push_back(_new);
-}
-
-
-/**
- * @brief Add a beacon defined by its MAC-address, if only zeros are given, the beacon will be deactivated
- *
- * @param index 1-4 beacons are currently supported
- * @param data null terminated char array representing a MAC-address in hex
- */
-void MI32addBeacon(uint8_t index, char* data){
- auto &_new = MIBLEbeacons[index-1]; //TODO: check
- MI32HexStringToBytes(data,_new.MAC);
- char _MAC[18];
- ToHex_P(MIBLEbeacons[index-1].MAC,6,_MAC,18,':');
- char _empty[6] = {0};
- _new.time = 0;
- if(memcmp(_empty,_new.MAC,6) == 0){
- _new.active = false;
- AddLog(LOG_LEVEL_INFO,PSTR("M32: Beacon%u deactivated"), index);
- }
- else{
- _new.active = true;
- MI32.mode.activeBeacon = 1;
- AddLog(LOG_LEVEL_INFO,PSTR("M32: Beacon added with MAC: %s"), _MAC);
- }
-}
-
-/**
- * @brief Present BLE scan in the console, after that deleting the scan data
- *
- */
-void MI32showScanResults(){
- size_t _size = MIBLEscanResult.size();
- ResponseAppend_P(PSTR(",\"BLEScan\":{\"Found\":%u,\"Devices\":["), _size);
- bool add_comma = false;
- for(auto _scanResult : MIBLEscanResult){
- char _MAC[18];
- ToHex_P(_scanResult.MAC,6,_MAC,18,':');
- ResponseAppend_P(PSTR("%s{\"MAC\":\"%s\",\"CID\":\"0x%04x\",\"SVC\":\"0x%04x\",\"UUID\":\"0x%04x\",\"RSSI\":%d}"),
- (add_comma)?",":"", _MAC, _scanResult.CID, _scanResult.SVC, _scanResult.UUID, _scanResult.RSSI);
- add_comma = true;
- }
- ResponseAppend_P(PSTR("]}"));
- MIBLEscanResult.clear();
- MI32.mode.shallShowScanResult = 0;
-}
-
-void MI32showBlockList(){
- ResponseAppend_P(PSTR(",\"Block\":["));
- bool add_comma = false;
- for(auto _scanResult : MIBLEBlockList){
- char _MAC[18];
- ToHex_P(_scanResult.buf,6,_MAC,18,':');
- ResponseAppend_P(PSTR("%s\"%s\""), (add_comma)?",":"", _MAC);
- add_comma = true;
- }
- ResponseAppend_P(PSTR("]"));
- MI32.mode.shallShowBlockList = 0;
-}
-
-bool MI32isInBlockList(uint8_t* MAC){
- bool isBlocked = false;
- for(auto &_blockedMAC : MIBLEBlockList){
- if(memcmp(_blockedMAC.buf,MAC,6) == 0) isBlocked = true;
- }
- return isBlocked;
-}
-
-void MI32removeMIBLEsensor(uint8_t* MAC){
- MIBLEsensors.erase( std::remove_if( MIBLEsensors.begin() , MIBLEsensors.end(), [MAC]( mi_sensor_t _sensor )->bool
- { return (memcmp(_sensor.MAC,MAC,6) == 0); }
- ), end( MIBLEsensors ) );
-}
-/***********************************************************************\
- * Read data from connections
-\***********************************************************************/
-
-void MI32readHT_LY(char *_buf){
- DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]);
- if(_buf[0] != 0 && _buf[1] != 0){
- memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT));
- AddLog(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt);
- uint32_t _slot = MI32.state.sensor;
-
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot);
- static float _tempFloat;
- _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f;
- if(_tempFloat<60){
- MIBLEsensors[_slot].temp=_tempFloat;
- // MIBLEsensors[_slot].showedUp=255; // this sensor is real
- }
- _tempFloat=(float)LYWSD0x_HT.hum;
- if(_tempFloat<100){
- MIBLEsensors[_slot].hum = _tempFloat;
- DEBUG_SENSOR_LOG(PSTR("LYWSD0x: hum updated"));
- }
- MIBLEsensors[_slot].eventType.tempHum = 1;
- if (MIBLEsensors[_slot].type == LYWSD03MMC || MIBLEsensors[_slot].type == MHOC401){
- MIBLEsensors[_slot].bat = ((float)LYWSD0x_HT.volt-2100.0f)/12.0f;
- MI32.mode.willReadBatt = 0;
- MIBLEsensors[_slot].eventType.bat = 1;
- }
- MIBLEsensors[_slot].shallSendMQTT = 1;
- MI32.mode.shallTriggerTele = 1;
- }
-}
-
-bool MI32readBat(char *_buf){
- DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]);
- if(_buf[0] != 0){
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_MI32,_buf[0]);
- uint32_t _slot = MI32.state.sensor;
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot);
- if(_buf[0]<101){
- MIBLEsensors[_slot].bat=_buf[0];
- if(MIBLEsensors[_slot].type==FLORA){
- memcpy(MIBLEsensors[_slot].firmware, _buf+2, 5);
- MIBLEsensors[_slot].firmware[5] = '\0';
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Firmware: %s"),D_CMND_MI32,MIBLEsensors[_slot].firmware);
- }
- MIBLEsensors[_slot].eventType.bat = 1;
- MIBLEsensors[_slot].shallSendMQTT = 1;
- MI32.mode.shallTriggerTele = 1;
- return true;
- }
- }
- return false;
-}
-
/**
* @brief Launch functions from Core 1 to make race conditions less likely
*
@@ -1685,6 +1542,37 @@ void MI32Every50mSecond(){
MI32.mode.shallTriggerTele = 0;
MI32triggerTele();
}
+ if(MI32.mode.triggerBerryAdvCB == 1){
+ if(MI32.beAdvCB != nullptr){
+ void (*func_ptr)(void) = (void (*)(void))MI32.beAdvCB;
+ func_ptr();
+ }
+ MI32.mode.triggerBerryAdvCB = 0;
+ }
+ if(MI32.mode.triggerBerryConnCB == 1){
+ if(MI32.beConnCB != nullptr){
+ void (*func_ptr)(int) = (void (*)(int))MI32.beConnCB;
+ char _message[32];
+ GetTextIndexed(_message, sizeof(_message), MI32.conCtx->error, kMI32_ConnErrorMsg);
+ AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s"),_message);
+ func_ptr(MI32.conCtx->error);
+ }
+ MI32.mode.triggerBerryConnCB = 0;
+ }
+ if(MI32.infoMsg > 0){
+ char _message[32];
+ GetTextIndexed(_message, sizeof(_message), MI32.infoMsg-1, kMI32_BLEInfoMsg);
+ AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s"),_message);
+ MI32.infoMsg = 0;
+ }
+#ifdef USE_MI_HOMEKIT
+ if(MI32.HKinfoMsg > 0){
+ char _message[32];
+ GetTextIndexed(_message, sizeof(_message), MI32.HKinfoMsg-1, kMI32_HKInfoMsg);
+ AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s"),_message);
+ MI32.HKinfoMsg = 0;
+ }
+#endif //USE_MI_HOMEKIT
}
/**
@@ -1693,256 +1581,52 @@ void MI32Every50mSecond(){
*/
void MI32EverySecond(bool restart){
- static uint32_t _counter = MI32.period - 15;
- static uint32_t _nextSensorSlot = 0;
+
+#ifdef USE_MI_HOMEKIT
+ if(TasmotaGlobal.devices_present>0){
+ for(uint32_t i=0;iMI32.period) {
- _counter = 0;
- MI32.mode.canScan = 0;
- MI32.mode.canConnect = 1;
- }
-
- if(MI32.mode.connected == 1 || MI32.mode.willConnect == 1) return;
-
- if(MIBLEsensors.size()==0) {
- if (MI32.mode.runningScan == 0 && MI32.mode.canScan == 1) MI32StartTask(MI32_TASK_SCAN);
- return;
- }
-
- if(_counter==0) {
-
- MI32.state.sensor = _nextSensorSlot;
- MI32.mode.canScan = 0;
- // if (MI32.mode.runningScan|| MI32.mode.connected || MI32.mode.willConnect) return;
- if (MI32.mode.connected || MI32.mode.willConnect) return;
- _nextSensorSlot++;
- MI32.mode.canConnect = 1;
- if(MI32.mode.connected == 0) {
- if (MI32.mode.shallReadBatt) {
- //TODO: decide automatically, which sensor can not work without connections
- AddLog(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u of %u"),D_CMND_MI32, MI32.state.sensor, MIBLEsensors.size()-1);
- AddLog(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] );
-
- MI32StartTask(MI32_TASK_BATT);
+#ifdef USE_MI_HOMEKIT
+ if(MIBLEsensors[i].NMT > 20){ //TODO: Make a choosable timeout later
+ mi_homekit_update_value(MIBLEsensors[i].motion_hap_service,0.0f,0x0f);
}
-#ifndef USE_MI_DECRYPTION // turn off connections, because we only listen to advertisements
- else{
- MI32StartTask(MI32_TASK_CONN);
- }
-#endif //USE_MI_DECRYPTION
- }
- if (_nextSensorSlot>(MIBLEsensors.size()-1)) {
- _nextSensorSlot= 0;
- _counter++;
- if (MI32.mode.shallReadBatt){
- MI32.mode.shallReadBatt = 0;
- }
- MI32.mode.canConnect = 0;
- MI32.mode.canScan = 1;
+#endif //USE_MI_HOMEKIT
}
}
- else _counter++;
- if (MI32.state.sensor>MIBLEsensors.size()-1) {
- _nextSensorSlot = 0;
- MI32.mode.canScan = 1;
- }
- MI32StartTask(MI32_TASK_SCAN);
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
-void CmndMi32Period(void) {
- if (XdrvMailbox.data_len > 0) {
- if (1 == XdrvMailbox.payload) {
- MI32EverySecond(true);
- } else {
- MI32.period = XdrvMailbox.payload;
- }
- }
- ResponseCmndNumber(MI32.period);
-}
-
-void CmndMi32Time(void) {
- if (XdrvMailbox.data_len > 0) {
- if (MIBLEsensors.size() > XdrvMailbox.payload) {
- if ((LYWSD02 == MIBLEsensors[XdrvMailbox.payload].type) || (MHOC303 == MIBLEsensors[XdrvMailbox.payload].type)) {
- AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Will set Time"));
- MI32.state.sensor = XdrvMailbox.payload;
- MI32.mode.canScan = 0;
- MI32.mode.canConnect = 0;
- MI32.mode.shallSetTime = 1;
- MI32.mode.willSetTime = 0;
- ResponseCmndNumber(XdrvMailbox.payload);
- }
- }
- }
-}
-
-void CmndMi32Page(void) {
- if (XdrvMailbox.payload > 0) {
- MI32.perPage = XdrvMailbox.payload;
- }
- ResponseCmndNumber(MI32.perPage);
-}
-
-void CmndMi32Battery(void) {
- MI32EverySecond(true);
- MI32.mode.shallReadBatt = 1;
- MI32.mode.canConnect = 1;
- ResponseCmndDone();
-}
-
-void CmndMi32Unit(void) {
- if (XdrvMailbox.data_len > 0) {
- if (MIBLEsensors.size() > XdrvMailbox.payload) {
- if ((LYWSD02 == MIBLEsensors[XdrvMailbox.payload].type) || (MHOC303 == MIBLEsensors[XdrvMailbox.payload].type)) {
- AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Will set Unit"));
- MI32.state.sensor = XdrvMailbox.payload;
- MI32.mode.canScan = 0;
- MI32.mode.canConnect = 0;
- MI32.mode.shallSetUnit = 1;
- MI32.mode.willSetUnit = 0;
- ResponseCmndNumber(XdrvMailbox.payload);
- }
- }
- }
-}
-
-#ifdef USE_MI_DECRYPTION
void CmndMi32Key(void) {
- if (44 == XdrvMailbox.data_len) { // a KEY-MAC-string
- MI32AddKey(XdrvMailbox.data);
+ if (44 == XdrvMailbox.data_len || 36 == XdrvMailbox.data_len) { // a KEY-MAC-string
+ mi_bindKey_t keyMAC;
+ MI32HexStringToBytes(XdrvMailbox.data,keyMAC.buf);
+ if(36 == XdrvMailbox.data_len){
+ memmove(keyMAC.buf + 10, keyMAC.buf + 6, 12);
+ const uint8_t _fillbytes[4] = {0x8d,0x3d,0x3c,0x97}; // only valid for YLKG08 and YLKG07 ??
+ memcpy(keyMAC.buf + 6,_fillbytes,4);
+ AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*) keyMAC.buf, 16);
+ }
+ MI32AddKey(keyMAC);
ResponseCmndDone();
}
}
-#endif // USE_MI_DECRYPTION
-void CmndMi32Beacon(void) {
- if (XdrvMailbox.data_len == 0) {
- switch (XdrvMailbox.index) {
- case 0:
- MI32.state.beaconScanCounter = 8;
- ResponseCmndIdxChar(PSTR("Scanning..."));
- break;
- case 1: case 2: case 3: case 4:
- char _MAC[18];
- ResponseCmndIdxChar(ToHex_P(MIBLEbeacons[XdrvMailbox.index-1].MAC, 6, _MAC, 18, ':'));
- break;
- }
- } else {
- if ((12 == XdrvMailbox.data_len) || (17 == XdrvMailbox.data_len)) { // MAC-string without or with colons
- switch (XdrvMailbox.index) {
- case 1: case 2: case 3: case 4:
- MI32addBeacon(XdrvMailbox.index, XdrvMailbox.data);
- break;
- }
- }
- ResponseCmndIdxChar(XdrvMailbox.data);
- }
-}
-
-void CmndMi32Block(void){
- if (XdrvMailbox.data_len == 0) {
- switch (XdrvMailbox.index) {
- case 0:
- MIBLEBlockList.clear();
- // AddLog(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size());
- ResponseCmndIdxChar(PSTR("Block list cleared"));
- break;
- case 1:
- ResponseCmndIdxChar(PSTR("Show block list"));
- break;
- }
- }
- else {
- MAC_t _MACasBytes;
- MI32HexStringToBytes(XdrvMailbox.data,_MACasBytes.buf);
- switch (XdrvMailbox.index) {
- case 0:
- MIBLEBlockList.erase( std::remove_if( begin( MIBLEBlockList ), end( MIBLEBlockList ), [_MACasBytes]( MAC_t& _entry )->bool
- { return (memcmp(_entry.buf,_MACasBytes.buf,6) == 0); }
- ), end( MIBLEBlockList ) );
- ResponseCmndIdxChar(PSTR("MAC not blocked anymore"));
- break;
- case 1:
- bool _notYetInList = true;
- for (auto &_entry : MIBLEBlockList) {
- if (memcmp(_entry.buf,_MACasBytes.buf,6) == 0){
- _notYetInList = false;
- }
- }
- if (_notYetInList) {
- MIBLEBlockList.push_back(_MACasBytes);
- ResponseCmndIdxChar(XdrvMailbox.data);
- MI32removeMIBLEsensor(_MACasBytes.buf);
- }
- // AddLog(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size());
- break;
- }
- }
- MI32.mode.shallShowBlockList = 1;
- MI32triggerTele();
+void CmndMi32Cfg(void) {
+ MI32saveConfig();
+ ResponseCmndDone();
}
void CmndMi32Option(void){
@@ -1957,6 +1641,9 @@ void CmndMi32Option(void){
case 2:
MI32.option.directBridgeMode = onOff;
break;
+ case 3:
+ MI32.mode.didGetConfig = onOff;
+ break;
}
ResponseCmndDone();
}
@@ -1964,16 +1651,205 @@ void CmndMi32Option(void){
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
+#ifdef USE_MI_EXT_GUI
+bool MI32HandleWebGUIResponse(void){
+ char tmp[16];
+ WebGetArg(PSTR("wi"), tmp, sizeof(tmp));
+ if (strlen(tmp)) {
+ WSContentBegin(200, CT_PLAIN);
+ if(MI32.widgetSlot==0) {WSContentEnd();return true;}
+ for(uint32_t i=0;i<32;i++){
+ if(bitRead(MI32.widgetSlot,i)){
+ MI32sendWidget(i);
+ WSContentEnd();
+ bitClear(MI32.widgetSlot,i);
+ return true;
+ }
+ }
+ WSContentEnd();
+ return true;
+ }
+ return false;
+}
-const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0917a{m}%u%s / %u{e}";
-const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}";
-const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}";
+#ifdef USE_MI_ESP32_ENERGY
+//https://gist.github.com/LingDong-/7e4c4cae5cbbc44400a05fba65f06f23
+// used for logarithmic mapping of 0 - 3600 watts to 0-20 pixel - TaylorLog did not work as expected
+float MI32ln(float x) {
+ unsigned int bx = * (unsigned int *) (&x);
+ unsigned int ex = bx >> 23;
+ signed int t = (signed int)ex-(signed int)127;
+ unsigned int s = (t < 0) ? (-t) : t;
+ bx = 1065353216 | (bx & 8388607);
+ x = * (float *) (&bx);
+ return -1.49278+(2.11263+(-0.729104+0.10969*x)*x)*x+0.6931471806*t;
+}
+#endif //USE_MI_ESP32_ENERGY
+
+void MI32createPolyline(char *polyline, uint8_t *history){
+ uint32_t _pos = 0;
+ uint32_t _inc = 0;
+ for (uint32_t i = 0; i<24;i++){
+ uint32_t y = 21-MI32fetchHistory(history,i);
+ if (y>20){
+ y = 150; //create a big gap in the graph to represent invalidated data
+ }
+ _inc = snprintf_P(polyline+_pos,10,PSTR("%u,%u "),i*6,y);
+ _pos+=_inc;
+ }
+ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: polyline: %s"),polyline);
+}
+
+#ifdef USE_MI_ESP32_ENERGY
+void MI32sendEnergyWidget(){
+ if (Energy.current_available && Energy.voltage_available) {
+ WSContentSend_P(HTTP_MI32_POWER_WIDGET,MIBLEsensors.size()+1, Energy.voltage,Energy.current[1]);
+ char _polyline[176];
+ MI32createPolyline(_polyline,MI32.energy_history);
+ WSContentSend_P(PSTR("" D_POWERUSAGE ": %.1f " D_UNIT_WATT ""),Energy.active_power);
+ WSContentSend_P(HTTP_MI32_GRAPH,_polyline,185,124,124,_polyline,1);
+ WSContentSend_P(PSTR("
"));
+ }
+}
+#endif //USE_MI_ESP32_ENERGY
+
+void MI32sendWidget(uint32_t slot){
+ auto _sensor = MIBLEsensors[slot];
+ char _MAC[13];
+ ToHex_P(_sensor.MAC,6,_MAC,13);
+ uint32_t _opacity = 1;
+ if(_sensor.RSSI == 0){
+ _opacity=0;
+ }
+ char _key[33] ={0};
+ if(_sensor.feature.needsKey){
+ snprintf_P(_key,32,PSTR("!! needs key!!"));
+ _opacity=0;
+ }
+ if(_sensor.key!=nullptr){
+ ToHex_P(_sensor.key,16,_key,33);
+ }
+ char _bat[24];
+ snprintf_P(_bat,24,PSTR("🔋%u%%"), _sensor.bat);
+ if(!_sensor.feature.bat) _bat[0] = 0;
+ if (_sensor.bat == 0) _bat[9] = 0;
+ WSContentSend_P(HTTP_MI32_WIDGET,slot+1,_opacity,_MAC,_sensor.RSSI,_bat,_key,kMI32DeviceType[_sensor.type-1]);
+ if(_sensor.feature.tempHum){
+ if(!isnan(_sensor.temp)){
+ char _polyline[176];
+ MI32createPolyline(_polyline,_sensor.temp_history);
+ WSContentSend_P(PSTR("
" D_JSON_TEMPERATURE ": %.1f °C"),_sensor.temp);
+ WSContentSend_P(HTTP_MI32_GRAPH,_polyline,185,124,124,_polyline,1);
+ WSContentSend_P(PSTR("
"));
+ }
+ if(!isnan(_sensor.hum)){
+ char _polyline[176];
+ MI32createPolyline(_polyline,_sensor.hum_history);
+ WSContentSend_P(PSTR("
" D_JSON_HUMIDITY ": %.1f %%"),_sensor.hum);
+ WSContentSend_P(HTTP_MI32_GRAPH,_polyline,151,190,216,_polyline,2);
+ WSContentSend_P(PSTR("
"));
+ }
+ if(!isnan(_sensor.temp) && !isnan(_sensor.hum)){
+ WSContentSend_P(PSTR("" D_JSON_DEWPOINT ": %.1f °C"),CalcTempHumToDew(_sensor.temp,_sensor.hum));
+ }
+
+ }
+ else if(_sensor.feature.temp){
+ if(!isnan(_sensor.temp)){
+ char _polyline[176];
+ MI32createPolyline(_polyline,_sensor.temp_history);
+ WSContentSend_P(PSTR("
" D_JSON_TEMPERATURE ": %.1f °C"),_sensor.temp);
+ WSContentSend_P(HTTP_MI32_GRAPH,_polyline,185,124,124,_polyline,1);
+ WSContentSend_P(PSTR("
"));
+ }
+ }
+ if(_sensor.feature.lux){
+ if(_sensor.lux!=0x00ffffff){
+ char _polyline[176];
+ MI32createPolyline(_polyline,_sensor.lux_history);
+ WSContentSend_P(PSTR("
" D_JSON_ILLUMINANCE ": %d Lux"),_sensor.lux);
+ WSContentSend_P(HTTP_MI32_GRAPH,_polyline,242,240,176,_polyline,3);
+ WSContentSend_P(PSTR("
"));
+ }
+ }
+ if(_sensor.feature.Btn){
+ if(_sensor.Btn<12) WSContentSend_P(PSTR("
Last Button: %u
"),_sensor.Btn);
+ }
+ if(_sensor.feature.motion){
+ WSContentSend_P(PSTR("
Events: %u
"),_sensor.events);
+ WSContentSend_P(PSTR("
No motion for > %u seconds
"),_sensor.NMT);
+ }
+ if(_sensor.feature.door){
+ if(_sensor.door!=255){
+ if(_sensor.door==1){
+ WSContentSend_P(PSTR("
Contact open
"));
+ }
+ else{
+ WSContentSend_P(PSTR("
Contact closed
"));
+ }
+ WSContentSend_P(PSTR("
Events: %u
"),_sensor.events);
+ }
+ }
+ if(_sensor.feature.leak){
+ if(_sensor.leak==1){
+ WSContentSend_P(PSTR("
Leak !!!
"));
+ }
+ else{
+ WSContentSend_P(PSTR("
no leak
"));
+ }
+ }
+ WSContentSend_P(PSTR("
"));
+}
+
+void MI32InitGUI(void){
+ MI32suspendScanTask();
+ MI32.widgetSlot=0;
+ WSContentStart_P("m32");
+ WSContentSend_P(HTTP_MI32_SCRIPT_1);
+ // WSContentSend_P(HTTP_MI32_SCRIPT_1);
+ WSContentSendStyle();
+ WSContentSend_P(HTTP_MI32_STYLE);
+ WSContentSend_P(HTTP_MI32_STYLE_SVG,1,185,124,124,185,124,124);
+ WSContentSend_P(HTTP_MI32_STYLE_SVG,2,151,190,216,151,190,216);
+ WSContentSend_P(HTTP_MI32_STYLE_SVG,3,242,240,176,242,240,176);
+#ifdef USE_MI_HOMEKIT
+ WSContentSend_P((HTTP_MI32_PARENT_START),MIBLEsensors.size(),UpTime(),MI32.hk_setup_code,MI32.HKconnectedControllers,ESP.getFreeHeap()/1024);
+#else
+ const char _setupCode[1] = {0};
+ WSContentSend_P((HTTP_MI32_PARENT_START),MIBLEsensors.size(),UpTime(),_setupCode,ESP.getFreeHeap()/1024);
+#endif //USE_MI_HOMEKIT
+ for(uint32_t _slot = 0;_slot
"));
+ WSContentSpaceButton(BUTTON_MAIN);
+ WSContentStop();
+ vTaskResume(MI32.ScanTask);
+}
+
+void MI32HandleWebGUI(void){
+ if (!HttpCheckPriviledgedAccess()) { return; }
+ if (MI32HandleWebGUIResponse()) { return; }
+ MI32InitGUI();
+}
+#endif //USE_MI_EXT_GUI
+
+const char HTTP_MI32[] PROGMEM = "{s}Mi ESP32 {m} %u devices{e}";
+
+#ifndef USE_MI_EXT_GUI
const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u %%{e}";
const char HTTP_LASTBUTTON[] PROGMEM = "{s}%s Last Button{m}%u {e}";
const char HTTP_EVENTS[] PROGMEM = "{s}%s Events{m}%u {e}";
const char HTTP_NMT[] PROGMEM = "{s}%s No motion{m}> %u seconds{e}";
+const char HTTP_DOOR[] PROGMEM = "{s}%s Door{m}> %u open/closed{e}";
const char HTTP_MI32_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e}";
+#endif //USE_MI_EXT_GUI
+const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}";
const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}";
+const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}";
void MI32ShowContinuation(bool *commaflg) {
if (*commaflg) {
@@ -1986,32 +1862,20 @@ void MI32ShowContinuation(bool *commaflg) {
void MI32Show(bool json)
{
if (json) {
- if(MI32.mode.shallShowScanResult) {
- return MI32showScanResults();
- }
- else if(MI32.mode.shallShowBlockList) {
- return MI32showBlockList();
- }
#ifdef USE_HOME_ASSISTANT
bool _noSummarySave = MI32.option.noSummary;
bool _minimalSummarySave = MI32.option.minimalSummary;
if(hass_mode==2){
- if(MI32.option.holdBackFirstAutodiscovery){
- if(!MI32.mode.firstAutodiscoveryDone){
- MI32.mode.firstAutodiscoveryDone = 1;
- return;
- }
- }
MI32.option.noSummary = false;
MI32.option.minimalSummary = false;
}
#endif //USE_HOME_ASSISTANT
if(!MI32.mode.triggeredTele){
- MI32.mode.shallClearResults=1;
+ // MI32.mode.shallClearResults=1;
if(MI32.option.noSummary) return; // no message at TELEPERIOD
}
-
+ MI32suspendScanTask();
for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
if(MI32.mode.triggeredTele && MIBLEsensors[i].eventType.raw == 0) continue;
if(MI32.mode.triggeredTele && MIBLEsensors[i].shallSendMQTT==0) continue;
@@ -2128,18 +1992,29 @@ void MI32Show(bool json)
}
}
} // minimal summary
- if (MIBLEsensors[i].feature.PIR){
+ if (MIBLEsensors[i].feature.motion){
if(MIBLEsensors[i].eventType.motion || !MI32.mode.triggeredTele){
if(MI32.mode.triggeredTele) {
MI32ShowContinuation(&commaflg);
- ResponseAppend_P(PSTR("\"PIR\":1")); // only real-time
+ ResponseAppend_P(PSTR("\"motion\":1")); // only real-time
}
MI32ShowContinuation(&commaflg);
ResponseAppend_P(PSTR("\"Events\":%u"),MIBLEsensors[i].events);
}
else if(MIBLEsensors[i].eventType.noMotion && MI32.mode.triggeredTele){
MI32ShowContinuation(&commaflg);
- ResponseAppend_P(PSTR("\"PIR\":0"));
+ ResponseAppend_P(PSTR("\"motion\":0"));
+ }
+ }
+
+ if (MIBLEsensors[i].feature.door){
+ if(MIBLEsensors[i].eventType.door || !MI32.mode.triggeredTele){
+ if(MI32.mode.triggeredTele) {
+ MI32ShowContinuation(&commaflg);
+ ResponseAppend_P(PSTR("\"DOOR\":%u"),MIBLEsensors[i].door); // only real-time
+ }
+ MI32ShowContinuation(&commaflg);
+ ResponseAppend_P(PSTR("\"Events\":%u"),MIBLEsensors[i].events);
}
}
@@ -2187,40 +2062,31 @@ void MI32Show(bool json)
}
}
MI32.mode.triggeredTele = 0;
-// add beacons
- uint32_t _idx = 0;
- for (auto _beacon : MIBLEbeacons){
- _idx++;
- if(!_beacon.active) continue;
- char _MAC[18];
- ToHex_P(_beacon.MAC,6,_MAC,18,':');
- ResponseAppend_P(PSTR(",\"Beacon%u\":{\"MAC\":\"%s\",\"CID\":\"0x%04x\",\"SVC\":\"0x%04x\","
- "\"UUID\":\"0x%04x\",\"Time\":%u,\"RSSI\":%d}"),
- _idx,_MAC,_beacon.CID,_beacon.SVC,_beacon.UUID,_beacon.time,_beacon.RSSI);
- }
#ifdef USE_HOME_ASSISTANT
if(hass_mode==2){
MI32.option.noSummary = _noSummarySave;
MI32.option.minimalSummary = _minimalSummarySave;
}
#endif //USE_HOME_ASSISTANT
+#ifdef USE_MI_EXT_GUI
+ Mi32invalidateOldHistory();
+#ifdef USE_MI_ESP32_ENERGY
+ MI32addHistory(MI32.energy_history,Energy.active_power[0],100); //TODO: which value??
+#endif //USE_MI_ESP32_ENERGY
+#endif //USE_MI_EXT_GUI
+ vTaskResume(MI32.ScanTask);
#ifdef USE_WEBSERVER
} else {
- static uint16_t _page = 0;
- static uint16_t _counter = 0;
- int32_t i = _page * MI32.perPage;
- uint32_t j = i + MI32.perPage;
- if (j+1>MIBLEsensors.size()){
- j = MIBLEsensors.size();
- }
- char stemp[5] ={0};
- if (MIBLEsensors.size()-(_page*MI32.perPage)>1 && MI32.perPage!=1) {
- sprintf_P(stemp,"-%u",j);
- }
- if (MIBLEsensors.size()==0) i=-1; // only for the GUI
+ MI32suspendScanTask();
- WSContentSend_PD(HTTP_MI32, i+1,stemp,MIBLEsensors.size());
- for (i; i0) WSContentSend_PD(HTTP_NMT, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].NMT);
}
+ if(MIBLEsensors[i].door != 255 && MIBLEsensors[i].type==MCCGQ02){
+ WSContentSend_PD(HTTP_DOOR, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].door);
+ }
if (MIBLEsensors[i].lux!=0x00ffffff) { // this is the error code -> no valid value
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux);
}
@@ -2260,36 +2133,26 @@ void MI32Show(bool json)
WSContentSend_PD(HTTP_LASTBUTTON, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].Btn);
}
}
- _counter++;
- if(_counter>3) {
- _page++;
- _counter=0;
- }
- if (MIBLEsensors.size()%MI32.perPage==0 && _page==MIBLEsensors.size()/MI32.perPage) { _page = 0; }
- if (_page>MIBLEsensors.size()/MI32.perPage) { _page = 0; }
-
- //always at the bottom of the page
- uint32_t _idx=0;
- if(MI32.mode.activeBeacon){
- WSContentSend_PD(HTTP_MI32_HL);
- char _sbeacon[] = "Beacon1";
- for (auto &_beacon : MIBLEbeacons){
- _idx++;
- if(!_beacon.active) continue;
- WSContentSend_PD(HTTP_MI32_HL);
- _sbeacon[6] = _idx + 0x30;
- char _MAC[18];
- ToHex_P(_beacon.MAC,6,_MAC,18,':');
- WSContentSend_PD(HTTP_MI32_MAC, _sbeacon, D_MAC_ADDRESS, _MAC);
- WSContentSend_PD(HTTP_RSSI, _sbeacon, _beacon.RSSI);
- if(_beacon.CID!=0) WSContentSend_PD(PSTR("{s}Beacon%u CID{m}0x%04X{e}"),_idx, _beacon.CID);
- if(_beacon.SVC!=0) WSContentSend_PD(PSTR("{s}Beacon%u SVC{m}0x%04X{e}"),_idx, _beacon.SVC);
- if(_beacon.UUID!=0) WSContentSend_PD(PSTR("{s}Beacon%u UUID{m}0x%04X{e}"),_idx, _beacon.UUID);
- WSContentSend_PD(PSTR("{s}Beacon%u Time{m}%u seconds{e}"),_idx, _beacon.time);
- }
- }
+#endif //USE_MI_EXT_GUI
#endif // USE_WEBSERVER
}
+ vTaskResume(MI32.ScanTask);
+}
+
+int ExtStopBLE(){
+ if(Settings->flag5.mi32_enable == 0) return 0;
+ if (MI32.ScanTask != nullptr){
+ MI32Scan->stop();
+ vTaskDelete(MI32.ScanTask);
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: stop BLE"));
+ }
+#ifdef USE_MI_HOMEKIT
+ if(MI32.mode.didStartHAP) {
+ AddLog(LOG_LEVEL_INFO,PSTR("M32: stop Homebridge"));
+ mi_homekit_stop();
+ }
+#endif //USE_MI_HOMEKIT
+ return 0;
}
/*********************************************************************************************\
@@ -2319,6 +2182,9 @@ bool Xsns62(uint8_t function)
case FUNC_EVERY_SECOND:
MI32EverySecond(false);
break;
+ case FUNC_SAVE_BEFORE_RESTART:
+ ExtStopBLE();
+ break;
case FUNC_COMMAND:
result = DecodeCommand(kMI32_Commands, MI32_Commands);
break;
@@ -2329,6 +2195,14 @@ bool Xsns62(uint8_t function)
case FUNC_WEB_SENSOR:
MI32Show(0);
break;
+#ifdef USE_MI_EXT_GUI
+ case FUNC_WEB_ADD_MAIN_BUTTON:
+ if (MI32.mode.didGetConfig) WSContentSend_P(HTTP_BTN_MENU_MI32);
+ break;
+ case FUNC_WEB_ADD_HANDLER:
+ WebServer_on(PSTR("/m32"), MI32HandleWebGUI);
+ break;
+#endif //USE_MI_EXT_GUI
#endif // USE_WEBSERVER
}
return result;
diff --git a/tasmota/xsns_62_esp32_mi_homekit.c b/tasmota/xsns_62_esp32_mi_homekit.c
new file mode 100644
index 000000000..7e6c92ff3
--- /dev/null
+++ b/tasmota/xsns_62_esp32_mi_homekit.c
@@ -0,0 +1,333 @@
+#if(USE_MI_HOMEKIT==1)
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+//Homekit
+static int MI32_bridge_identify(hap_acc_t *ha);
+static int MI32_accessory_identify(hap_acc_t *ha);
+static void MI32_bridge_thread_entry(void *p);
+
+extern uint32_t MI32numberOfDevices();
+extern const char *MI32getDeviceName(uint32_t slot);
+extern uint32_t MI32getDeviceType(uint32_t slot);
+extern void MI32saveHAPhandles(uint32_t slot, uint32_t type, void* handle);
+extern void MI32passHapEvent(uint32_t event);
+extern void MI32didStartHAP();
+extern const char * MI32getSetupCode();
+extern uint32_t MI32numOfRelays();
+extern void MI32setRelayFromHK(uint32_t relay, bool onOff);
+
+// static const char *TAG = "Mi Bridge";
+static bool MIBridgeWasNeverConnected = true;
+
+#define CONFIG_EXAMPLE_SETUP_ID "MI32"
+
+#define FLORA 1
+#define MJ_HT_V1 2
+#define LYWSD02 3
+#define LYWSD03MMC 4
+#define CGG1 5
+#define CGD1 6
+#define NLIGHT 7
+#define MJYD2S 8
+#define YEERC 9
+#define MHOC401 10
+#define MHOC303 11
+#define ATC 12
+#define MCCGQ02 13
+#define SJWS01L 14
+#define PVVX 15
+#define YLKG08 16
+
+/*********************************************************************************************\
+ * Homekit
+\*********************************************************************************************/
+/* Mandatory identify routine for the bridge.
+ * In a real accessory, something like LED blink should be implemented
+ * got visual identification
+ */
+static int MI32_bridge_identify(hap_acc_t *ha)
+{
+ return HAP_SUCCESS;
+}
+
+void mi_hap_event_handler(hap_event_t event, void *data)
+{
+ MI32passHapEvent((uint32_t)event);
+ if(event == HAP_EVENT_CTRL_CONNECTED) MIBridgeWasNeverConnected = false;
+}
+
+static int MI32_bridge_read_callback(hap_read_data_t read_data[], int count,
+ void *serv_priv, void *read_priv)
+{
+ return HAP_SUCCESS;
+}
+
+static int MI32_outlets_write_callback(hap_write_data_t write_data[], int count,
+ void *serv_priv, void *write_priv)
+{
+ uint8_t _relay = ((uint8_t*)serv_priv)[0];
+ int i, ret = HAP_SUCCESS;
+ hap_write_data_t *write;
+ for (i = 0; i < count; i++) {
+ write = &write_data[i];
+ if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ON)) {
+ MI32setRelayFromHK(_relay-48, write->val.b);
+ hap_char_update_val(write->hc, &(write->val));
+ *(write->status) = HAP_STATUS_SUCCESS;
+ } else {
+ *(write->status) = HAP_STATUS_RES_ABSENT;
+ }
+ }
+ return ret;
+}
+
+/* Mandatory identify routine for the bridged accessory
+ * In a real bridge, the actual accessory must be sent some request to
+ * identify itself visually
+ */
+static int MI32_accessory_identify(hap_acc_t *ha)
+{
+ return HAP_SUCCESS;
+}
+
+/*The main thread for handling the Smart Outlet Accessory */
+static void MI32_bridge_thread_entry(void *p)
+{
+ // esp_log_level_set("*", ESP_LOG_NONE);
+ hap_acc_t *accessory;
+ hap_serv_t *service;
+
+ /* Initialize the HAP core */
+ hap_init(HAP_TRANSPORT_WIFI);
+
+ /* Initialise the mandatory parameters for Accessory which will be added as
+ * the mandatory services internally
+ */
+ hap_acc_cfg_t cfg = {
+ .name = "Mi-Home-Bridge",
+ .manufacturer = "Tasmota",
+ .model = "ESP32",
+ .serial_num = "9600",
+ .fw_rev = "0.9.5",
+ .hw_rev = NULL,
+ .pv = "1.1.0",
+ .cid = HAP_CID_BRIDGE,
+ .identify_routine = MI32_bridge_identify
+ };
+ /* Create accessory object */
+ accessory = hap_acc_create(&cfg);
+
+ /* Add a dummy Product Data */
+ uint8_t product_data[] = {'T','M','H'};
+ hap_acc_add_product_data(accessory, product_data, sizeof(product_data));
+
+ /* Add the Accessory to the HomeKit Database */
+ hap_add_accessory(accessory);
+
+#define NUM_BRIDGED_ACCESSORIES 1
+ /* Create and add the Accessory to the Bridge object*/
+ uint32_t _numDevices = MI32numberOfDevices();
+ for (uint32_t i = 0; i < _numDevices; i++) {
+ char *accessory_name = (char*)MI32getDeviceName(i);
+ char _serialNum[4] = {0};
+ snprintf(_serialNum,sizeof(_serialNum),"%u", i);
+
+ hap_acc_cfg_t bridge_cfg = {
+ .name = accessory_name,
+ .manufacturer = "Xiaomi",
+ .model = accessory_name,
+ .serial_num = _serialNum,
+ .fw_rev = "0.9.1",
+ .hw_rev = NULL,
+ .pv = "1.1.0",
+ .cid = HAP_CID_SENSOR,
+ .identify_routine = MI32_accessory_identify,
+ };
+
+ /* Create accessory object */
+ accessory = hap_acc_create(&bridge_cfg);
+
+ switch (MI32getDeviceType(i)){
+ case LYWSD02: case LYWSD03MMC: case CGG1: case CGD1: case MHOC303: case MHOC401: case ATC: case PVVX:
+ {
+ service = hap_serv_humidity_sensor_create(50.0f);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x06,(void *)hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_CURRENT_RELATIVE_HUMIDITY));
+
+ service = hap_serv_temperature_sensor_create(22.5f);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x04,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_CURRENT_TEMPERATURE));
+
+ service = hap_serv_battery_service_create(99,0,0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0a,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_BATTERY_LEVEL));
+ }
+ break;
+ case FLORA: case MJYD2S:
+ {
+ service = hap_serv_light_sensor_create(100.0f);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x07,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_CURRENT_AMBIENT_LIGHT_LEVEL));
+ service = hap_serv_battery_service_create(50,0,0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0a,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_BATTERY_LEVEL));
+ if(MI32getDeviceType(i) == MJYD2S){
+ service = hap_serv_motion_sensor_create(false);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0f,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_MOTION_DETECTED));
+ }
+ break;
+ }
+ case NLIGHT:
+ {
+ service = hap_serv_motion_sensor_create(false);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0f,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_MOTION_DETECTED));
+
+ service = hap_serv_battery_service_create(50,0,0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0a,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_BATTERY_LEVEL));
+ break;
+ //motion 0x0f
+ }
+ case MCCGQ02:
+ {
+ service = hap_serv_contact_sensor_create(0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x19,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_CONTACT_SENSOR_STATE));
+ service = hap_serv_battery_service_create(50,0,0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0a,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_BATTERY_LEVEL));
+ break;
+ }
+ case YEERC:
+ {
+ bridge_cfg.cid = HAP_CID_PROGRAMMABLE_SWITCH;
+ hap_serv_t * _label = hap_serv_service_label_create(1);
+ hap_acc_add_serv(accessory, _label);
+ for(uint8_t _but=0;_but<6;_but++){
+ hap_serv_t * _newSwitch = hap_serv_stateless_programmable_switch_create(0);
+ const uint8_t _validVals[] = {0,2};
+ hap_char_add_valid_vals(hap_serv_get_char_by_uuid(_newSwitch, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT), _validVals, 2);
+ hap_char_t *_index = hap_char_service_label_index_create(_but+1);
+ hap_serv_add_char(_newSwitch,_index);
+ hap_acc_add_serv(accessory, _newSwitch);
+ MI32saveHAPhandles(i,_but+1000,hap_serv_get_char_by_uuid(_newSwitch, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT));
+ }
+ }
+ break;
+ case SJWS01L:
+ service = hap_serv_leak_sensor_create(0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x14,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_LEAK_DETECTED));
+ hap_serv_t * _newSwitch = hap_serv_stateless_programmable_switch_create(0);
+ const uint8_t _validVals[] = {0,2};
+ hap_char_add_valid_vals(hap_serv_get_char_by_uuid(_newSwitch, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT), _validVals, 2);
+ hap_acc_add_serv(accessory, _newSwitch);
+ MI32saveHAPhandles(i,1000,hap_serv_get_char_by_uuid(_newSwitch, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT));
+ service = hap_serv_battery_service_create(50,0,0);
+ hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
+ hap_acc_add_serv(accessory, service);
+ MI32saveHAPhandles(i,0x0a,hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_BATTERY_LEVEL));
+ break;
+ default:
+ break;
+ }
+ /* Add the Accessory to the HomeKit Database */
+ hap_add_bridged_accessory(accessory, hap_get_unique_aid(accessory_name));
+ }
+ // add internal Tasmota devices
+ for(uint32_t i = 0;i 0.0f);
+ break;
+ default:
+ new_val.f = value;
+ }
+ int ret = hap_char_update_val((hap_char_t *)handle, &new_val);
+ // if(ret!= HAP_SUCCESS){
+ // ESP_LOGE(TAG,"error:",ret);
+ // }
+}
+
+void mi_homekit_stop(){
+ hap_stop();
+}
+
+#endif //USE_MI_ESP32
+