From 483069d6dadca3cb634b20b7cc78954e3179c515 Mon Sep 17 00:00:00 2001 From: Christian Baars Date: Mon, 17 Jan 2022 19:46:19 +0100 Subject: [PATCH] Add files via upload --- tasmota/xsns_62_esp32_mi.h | 506 +++++++++++++++++++++++++++++ tasmota/xsns_62_esp32_mi_homekit.c | 333 +++++++++++++++++++ 2 files changed, 839 insertions(+) create mode 100644 tasmota/xsns_62_esp32_mi.h create mode 100644 tasmota/xsns_62_esp32_mi_homekit.c 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_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 +