Merge pull request #14491 from Staars/patch-1

Adding HomeKit to legacy MI32 driver
This commit is contained in:
Theo Arends 2022-01-18 11:13:10 +01:00 committed by GitHub
commit d7142fb4f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 2427 additions and 1393 deletions

View File

@ -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

View File

@ -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

View File

@ -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
/*********************************************************************************************\

View File

@ -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
#endif // USE_WEBSERVER

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifdef USE_BERRY
#include <berry.h>
#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)
*/

506
tasmota/xsns_62_esp32_mi.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 = "<p><form action='m32' method='get'><button>Mi Dashboard</button></form></p>";
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 =
"<style onload=setTimeout(setUp,500)>.parent {display: flex;flex-wrap: wrap;justify-content: center;}svg{float:inline-end;}"
".box {flex: 0 1 335px;margin: 5px;padding: 5px;border-radius: 0.8rem;background-color: rgba(221, 221, 221, 0.2);}</style>";
const char HTTP_MI32_STYLE_SVG[] PROGMEM =
"<svg height='0'><defs><linearGradient id='grd%u' x1='0%%' y1='0%%' x2='0%%' y2='15%%'>"
"<stop offset='0' stop-color='rgba(%u, %u, %u, 0.5)'/>"
"<stop offset='1' stop-color='rgba(%u, %u, %u, 0)'/></linearGradient></defs></svg>"
;
const char HTTP_MI32_PARENT_START[] PROGMEM =
"<div class='parent'>"
"<div class='box'><h2>MI32 Bridge</h2>"
"Observing <span id='numDev'>%u</span> devices<br>"
"Uptime: <span class='Ti'>%u</span> seconds<br>"
#ifdef USE_MI_HOMEKIT
"HomeKit setup code: %s<br>"
"HAP controller connections: %d<br>"
#else
"HomeKit not enabled%s<br>"
#endif //USE_MI_HOMEKIT
"Free Heap: %u kB"
"</div>";
const char HTTP_MI32_WIDGET[] PROGMEM =
"<div class='box' id='box%u' style='opacity:%u.5;'>MAC:%s RSSI:%d %s<br>"
"<small>&zwnj;%s</small>"
"<h2 style='margin-top:0em;'>%s"
"<svg height='24' width='24' style='float:inline-end;'>"
"<circle cx='11' cy='11' r='0' fill='#90ee90' opacity='0'>"
"<animate attributeName='r' from='11' to='0' dur='9s' repeatCount='1'></animate>"
"<animate attributeName='opacity' from='1' to='0' dur='9s' repeatCount='1'></animate></circle></svg>"
"</h2>";
const char HTTP_MI32_GRAPH[] PROGMEM =
"<svg height='20' width='150'>"
"<polyline points='%s'"
"style='stroke:rgb(%u, %u, %u);fill:none;'></polyline>"
"<polyline points='%s138,20 0,20'"
"style='stroke:none;fill:url(#grd%u);'></polyline>"
"</svg>";
//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 =
"<div class='box' id='box%u'>"
"<h2 style='margin-top:0em;'>Energy"
"</h2>"
"<p>" D_VOLTAGE ": %.1f " D_UNIT_VOLT "</p>"
"<p>" D_CURRENT ": %.3f " D_UNIT_AMPERE "</p>";
#endif //USE_MI_ESP32_ENERGY
#endif //USE_MI_EXT_GUI
#endif // USE_WEBSERVER
#endif //USE_MI_ESP32

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,333 @@
#if(USE_MI_HOMEKIT==1)
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <hap.h>
#include <hap_apple_servs.h>
#include <hap_apple_chars.h>
#include <hap_platform_keystore.h>
//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<MI32numOfRelays();i++){
char _serialNum[2] = {i+49,0};
hap_acc_cfg_t bridge_cfg = {
.name = _serialNum,
.manufacturer = "Xiaomi",
.model = "Tasmota",
.serial_num = _serialNum,
.fw_rev = "0.9.1",
.hw_rev = NULL,
.pv = "1.1.0",
.cid = HAP_CID_OUTLET,
.identify_routine = MI32_accessory_identify,
};
/* Create accessory object */
accessory = hap_acc_create(&bridge_cfg);
service = hap_serv_outlet_create(false,true);
hap_serv_set_bulk_read_cb(service, MI32_bridge_read_callback);
hap_serv_set_write_cb(service, MI32_outlets_write_callback);
hap_serv_set_priv(service, strdup(_serialNum));
hap_acc_add_serv(accessory, service);
MI32saveHAPhandles(i,0xf0,(void *)hap_serv_get_char_by_uuid(service, HAP_CHAR_UUID_ON));
hap_add_bridged_accessory(accessory, hap_get_unique_aid(_serialNum));
}
hap_register_event_handler(mi_hap_event_handler);
hap_set_setup_code(MI32getSetupCode());
hap_set_setup_id(CONFIG_EXAMPLE_SETUP_ID);
if (hap_start() == HAP_SUCCESS){
MI32didStartHAP(true);
}
else MI32didStartHAP(false);
/* The task ends here. The read/write callbacks will be invoked by the HAP Framework */
vTaskDelete(NULL);
}
void mi_homekit_main(void){
xTaskCreate(MI32_bridge_thread_entry, "MIBRIDGE", 8192, NULL, 5, NULL);
}
void mi_homekit_update_value(void* handle, float value, uint32_t type){
if(handle == NULL) return;
if(MIBridgeWasNeverConnected) return;
hap_val_t new_val;
switch(type){
case 0x01: case 0x19: case 0x0a: case 0x14: case 0xf0:
if(type == 0x19){
value = 1-(uint8_t)value;
}
new_val.i = (uint8_t)value;
break;
case 0x0f:
new_val.b = (value > 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