From 450b9d1d4152fec64e1013b8167511e303c226ea Mon Sep 17 00:00:00 2001 From: Staars Date: Tue, 30 Jan 2024 16:51:44 +0100 Subject: [PATCH] add BTHOME --- tasmota/include/xsns_62_esp32_mi.h | 14 ++- .../tasmota_xsns_sensor/xsns_62_esp32_mi.ino | 95 ++++++++++++++++--- 2 files changed, 96 insertions(+), 13 deletions(-) diff --git a/tasmota/include/xsns_62_esp32_mi.h b/tasmota/include/xsns_62_esp32_mi.h index f707bddaf954..ef372036ea57 100644 --- a/tasmota/include/xsns_62_esp32_mi.h +++ b/tasmota/include/xsns_62_esp32_mi.h @@ -141,6 +141,14 @@ struct ATCPacket_t{ //and PVVX }; }; +struct BTHome_info_t{ + uint8_t encrypted:1; + uint8_t reserved:1; + uint8_t triggered:1; + uint8_t reserved2:2; + uint8_t version:2; +}; + struct BLERingBufferItem_t{ uint16_t returnCharUUID; uint16_t handle; @@ -349,8 +357,9 @@ void (*const MI32_Commands[])(void) PROGMEM = {&CmndMi32Key, &CmndMi32Name,&Cmnd #define PVVX 15 #define YLKG08 16 #define YLAI003 17 +#define BTHOME 18 -#define MI32_TYPES 17 //count this manually +#define MI32_TYPES 18 //count this manually const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora 0x01aa, // MJ_HT_V1 @@ -369,9 +378,10 @@ const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora 0x944a, // PVVX -> this is a fake ID 0x03b6, // YLKG08 and YLKG07 - version w/wo mains 0x07bf, // YLAI003 + 0xb770, // BTHome -> fake ID }; -const char kMI32DeviceType[] PROGMEM = {"Flora|MJ_HT_V1|LYWSD02|LYWSD03|CGG1|CGD1|NLIGHT|MJYD2S|YLYK01|MHOC401|MHOC303|ATC|MCCGQ02|SJWS01L|PVVX|YLKG08|YLAI003"}; +const char kMI32DeviceType[] PROGMEM = {"Flora|MJ_HT_V1|LYWSD02|LYWSD03|CGG1|CGD1|NLIGHT|MJYD2S|YLYK01|MHOC401|MHOC303|ATC|MCCGQ02|SJWS01L|PVVX|YLKG08|YLAI003|BTHOME"}; const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|did disconnect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out"; diff --git a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino index 5acc0d1b5292..04b31918a065 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino @@ -138,10 +138,14 @@ class MI32AdvCallbacks: public NimBLEScanCallbacks { if(UUID==0xfe95) { MI32ParseResponse((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); } - else if(UUID==0xfdcd) { + else if(UUID==0xfcd2) { + std::string optionalName = advertisedDevice->getName(); + MI32parseBTHomePacket((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI, optionalName.c_str()); + } + else if(UUID==0xfdcd) { // deprecated MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); } - else if(UUID==0x181a) { //ATC and PVVX + else if(UUID==0x181a) { //ATC and PVVX - deprecated, change FW setting of these devices to BTHome V2 MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); } _mutex = false; @@ -435,7 +439,7 @@ int MI32_decryptPacket(char * _buf, uint16_t _bufSize, uint8_t * _payload, uint3 * @param _type Type number of the sensor * @return uint32_t Known or new slot in the sensors-vector */ -uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter){ +uint32_t MIBLEgetSensorSlot(uint8_t * _MAC, uint16_t _type, uint8_t counter){ DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_MI32, _type); uint16_t _pid = _type; // save for unknown types bool _success = false; @@ -450,7 +454,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter) DEBUG_SENSOR_LOG(PSTR("%s: vector size %u"),D_CMND_MI32, MIBLEsensors.size()); for(uint32_t i=0; i100.0f) value=100.0f; //clamp it for now - history[_hour] = (((uint8_t)(value/5.0f))+1) + 0b10000000; //lux + history[_hour] = (((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 + uint8_t _watt = ((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; @@ -997,6 +1000,9 @@ void MI32saveConfig(){ if(_sensor.name != nullptr){ snprintf_P(_name_feat,64,PSTR(",\"name\":\"%s\",\"feat\":%u"),_sensor.name,_sensor.feature.raw); } + else if(_sensor.type == BTHOME && _sensor.name == nullptr){ + snprintf_P(_name_feat,64,PSTR(",\"feat\":%u"),_sensor.feature.raw); + } else{ _name_feat[0] = 0; } @@ -1812,6 +1818,70 @@ if(decryptRet!=0){ if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; } +void MI32parseBTHomePacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI, const char* optionalName){ + uint32_t _slot; + _slot = MIBLEgetSensorSlot(addr, 0xb770, 0); // fake ID, constant fake counter + if (optionalName[0] != '\0'){ + AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), optionalName,_slot); + } + MIBLEsensors[_slot].RSSI = RSSI; + MIBLEsensors[_slot].lastTime = millis(); + + BTHome_info_t info = (BTHome_info_t)_buf[0]; + MIBLEsensors[_slot].feature.needsKey = info.encrypted; + + uint32_t idx = 1; + while(idx < length - 1){ + switch(_buf[idx]){ + case 0: + if(_buf[idx+1] == MIBLEsensors[_slot].lastCnt){ + return; // known packet + } + MIBLEsensors[_slot].lastCnt = _buf[idx+1]; + idx += 2; + break; + case 1: + MIBLEsensors[_slot].bat = _buf[idx+1]; + MIBLEsensors[_slot].eventType.bat = 1; + MIBLEsensors[_slot].feature.bat = 1; + idx += 2; + break; + case 2: + MIBLEsensors[_slot].temp = (int16_t)(_buf[idx+1]|_buf[idx+2] << 8)/100.0f; + MIBLEsensors[_slot].eventType.temp = 1; + MIBLEsensors[_slot].feature.temp = 1; + MI32addHistory(MIBLEsensors[_slot].temp_history, (float)MIBLEsensors[_slot].temp, 0); + idx += 3; + break; + case 3: + MIBLEsensors[_slot].hum = (uint16_t)(_buf[idx+1]|_buf[idx+2] << 8)/100.0f; + MIBLEsensors[_slot].eventType.hum = 1; + MIBLEsensors[_slot].feature.hum = 1; + MI32addHistory(MIBLEsensors[_slot].hum_history, (float)MIBLEsensors[_slot].hum, 1); + idx += 3; + break; + case 0x0b: + // power ?? + idx += 2; + break; + case 0x0c: + //voltage + idx += 2; + break; + default: + AddLog(LOG_LEVEL_INFO,PSTR("M32: unknown BTHome data type: %u, discard rest of data buffer!"),_buf[idx]); + AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_buf,length); + idx = length; // "break" + break; + } + } +#ifdef USE_MI_EXT_GUI + bitSet(MI32.widgetSlot,_slot); +#endif //USE_MI_EXT_GUI + MIBLEsensors[_slot].shallSendMQTT = 1; + if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; +} + void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ ATCPacket_t *_packet = (ATCPacket_t*)_buf; bool isATC = (length == 0x0d); @@ -1846,7 +1916,6 @@ void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI) #endif //USE_MI_EXT_GUI MIBLEsensors[_slot].shallSendMQTT = 1; if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; - } void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ // no MiBeacon @@ -2050,6 +2119,10 @@ void CmndMi32Key(void) { } void CmndMi32Name(void) { + if(XdrvMailbox.index > MIBLEsensors.size() - 1){ + ResponseCmndDone(); + return; + } if(MIBLEsensors[XdrvMailbox.index].name != nullptr){ delete []MIBLEsensors[XdrvMailbox.index].name; }