Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MI32 legacy: add BTHOME sensor parser #20625

Merged
merged 1 commit into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions tasmota/include/xsns_62_esp32_mi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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";

Expand Down
95 changes: 84 additions & 11 deletions tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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; i<MIBLEsensors.size(); i++){
if(memcmp(_MAC,MIBLEsensors[i].MAC,sizeof(_MAC))==0){
if(memcmp(_MAC,MIBLEsensors[i].MAC,6)==0){
DEBUG_SENSOR_LOG(PSTR("%s: known sensor at slot: %u"),D_CMND_MI32, i);
// AddLog(LOG_LEVEL_DEBUG,PSTR("Counters: %x %x"),MIBLEsensors[i].lastCnt, counter);
if(MIBLEsensors[i].lastCnt==counter) {
Expand All @@ -468,7 +472,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
}
DEBUG_SENSOR_LOG(PSTR("%s: found new sensor"),D_CMND_MI32);
mi_sensor_t _newSensor;
memcpy(_newSensor.MAC,_MAC, sizeof(_MAC));
memcpy(_newSensor.MAC,_MAC, 6);
_newSensor.PID = _pid;
_newSensor.type = _type;
_newSensor.eventType.raw = 0;
Expand Down Expand Up @@ -509,7 +513,6 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
_newSensor.feature.NMT=1;
_newSensor.feature.lux=1;
_newSensor.feature.bat=1;
_newSensor.feature.bat=1;
_newSensor.NMT=0;
break;
case YLYK01: case YLKG08: case YLAI003:
Expand Down Expand Up @@ -579,20 +582,20 @@ void MI32addHistory(uint8_t *history, float value, uint32_t type){
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: history hour: %u"),_hour);
switch(type){
case 0: //temperature
history[_hour] = (((uint8_t)(value + 5.0f)/4)+1) + 0b10000000; //temp
history[_hour] = ((((value + 5.0f)/4) + 1) + 0b10000000); //temp
break;
case 1: //humidity
history[_hour] = (((uint8_t)(value/5 ))+1) + 0b10000000; //hum
history[_hour] = (((value/5.0f) + 1) + 0b10000000) ; //hum
break;
case 2: //light
if(value>100.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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down