diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index 8424ab6cd..05ba845fd 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -20,6 +20,9 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.4.0 20200807 added - multiple backports from the HM10-driver (NLIGHT,MJYD2S,YEERC,MHOC401,MHOC303), + fixing Flora values, adding realtime-bridge, better battery for LYWSD03 and MHOC401 + --- 0.9.3.1 20200412 added - clean ups, code shrink, battery bugfix --- 0.9.3.0 20200322 added - multi page web view, command HM10PAGE, polling for MJ_HT_V1, @@ -70,12 +73,23 @@ struct { uint32_t connected:1; uint32_t subscribed:1; uint32_t autoScan:1; + uint32_t shallTriggerTele:1; + uint32_t triggeredTele:1; + // uint32_t shallClearResults:1; // BLE scan results // TODO: more to come } mode; struct { uint8_t sensor; // points to to the number 0...255 // TODO: more to come } state; + struct { + uint32_t allwaysAggregate:1; + uint32_t showRSSI:1; + uint32_t ignoreBogusBattery:1; + uint32_t noSummary:1; + uint32_t minimalSummary:1; + uint32_t noRealTime:1; + } option; } HM10; #pragma pack(1) // byte-aligned structures to read the sensor data @@ -83,6 +97,7 @@ struct { struct { uint16_t temp; uint8_t hum; + uint16_t volt; } LYWSD0x_HT; struct { uint8_t spare; @@ -102,7 +117,7 @@ struct mi_beacon_t{ uint16_t frame; uint16_t productID; uint8_t counter; - uint8_t Mac[6]; + uint8_t MAC[6]; uint8_t spare; uint8_t type; uint8_t ten; @@ -118,28 +133,79 @@ struct mi_beacon_t{ 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]; }; #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 serial[6]; + uint8_t lastCnt; //device generated counter of the packet + uint8_t shallSendMQTT; uint8_t showedUp; + 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 { - float moisture; - float fertility; - uint32_t lux; + 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) }; - uint8_t bat; // all sensors }; + std::vector MIBLEsensors; /*********************************************************************************************\ @@ -158,22 +224,41 @@ const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto #define LYWSD03MMC 4 #define CGG1 5 #define CGD1 6 +#define NLIGHT 7 +#define MJYD2S 8 +#define YEERC 9 +#define MHOC401 10 +#define MHOC303 11 -const uint16_t kHM10SlaveID[6]={ 0x0098, // Flora +#define HM10_TYPES 11 //count this manually + +const uint16_t kHM10SlaveID[HM10_TYPES]={ + 0x0098, // Flora 0x01aa, // MJ_HT_V1 0x045b, // LYWSD02 0x055b, // LYWSD03 0x0347, // CGG1 - 0x0576 // CGD1 + 0x0576, // CGD1 + 0x03dd, // NLIGHT + 0x07f6, // MJYD2S + 0x0153, // yee-rc + 0x0387, // MHO-C401 + 0x06d3 // MHO-C303 }; -const char kHM10SlaveType1[] PROGMEM = "Flora"; -const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1"; -const char kHM10SlaveType3[] PROGMEM = "LYWSD02"; -const char kHM10SlaveType4[] PROGMEM = "LYWSD03"; -const char kHM10SlaveType5[] PROGMEM = "CGG1"; -const char kHM10SlaveType6[] PROGMEM = "CGD1"; -const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4,kHM10SlaveType5,kHM10SlaveType6}; +const char kHM10DeviceType1[] PROGMEM = "Flora"; +const char kHM10DeviceType2[] PROGMEM = "MJ_HT_V1"; +const char kHM10DeviceType3[] PROGMEM = "LYWSD02"; +const char kHM10DeviceType4[] PROGMEM = "LYWSD03"; +const char kHM10DeviceType5[] PROGMEM = "CGG1"; +const char kHM10DeviceType6[] PROGMEM = "CGD1"; +const char kHM10DeviceType7[] PROGMEM = "NLIGHT"; +const char kHM10DeviceType8[] PROGMEM = "MJYD2S"; +const char kHM10DeviceType9[] PROGMEM = "YEERC"; +const char kHM10DeviceType10[] PROGMEM ="MHOC401"; +const char kHM10DeviceType11[] PROGMEM ="MHOC303"; + +const char * kHM10DeviceType[] PROGMEM = {kHM10DeviceType1,kHM10DeviceType2,kHM10DeviceType3,kHM10DeviceType4,kHM10DeviceType5,kHM10DeviceType6,kHM10DeviceType7,kHM10DeviceType8,kHM10DeviceType9,kHM10DeviceType10,kHM10DeviceType11}; /*********************************************************************************************\ * enumerations @@ -283,16 +368,16 @@ void HM10_Discovery_Scan(void) { HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,2); // status } -void HM10_Read_LYWSD03(void) { +void HM10_Read_LYWSD03(void) { //and MHO-C401 HM10_Launchtask(TASK_HM10_CONN,0,1); // connect HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); // get OK+CONN HM10_Launchtask(TASK_HM10_SUB_L3,2,20); // subscribe HM10_Launchtask(TASK_HM10_UN_L3,3,80); // unsubscribe - HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); // read Battery - HM10_Launchtask(TASK_HM10_DISCONN,5,5); // disconnect + // HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); // read Battery + HM10_Launchtask(TASK_HM10_DISCONN,4,5); // disconnect } -void HM10_Read_LYWSD02(void) { +void HM10_Read_LYWSD02(void) { //and MHO-C303 HM10_Launchtask(TASK_HM10_CONN,0,1); // connect HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); // get OK+CONN HM10_Launchtask(TASK_HM10_SUB_L2,2,20); // subscribe @@ -338,15 +423,15 @@ void HM10_Read_MJ_HT_V1(void) { /** * @brief Return the slot number of a known sensor or return create new sensor slot * - * @param _serial BLE address of the sensor + * @param _MAC BLE address of the sensor * @param _type Type number of the sensor * @return uint32_t Known or new slot in the sensors-vector */ -uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ +uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint32_t _rssi){ DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_HM10, _type); bool _success = false; - for (uint32_t i=0;i<6;i++){ // i < sizeof(kHM10SlaveID) gives compiler warning + for (uint32_t i=0;i3) return; // probably false alarm from a damaged packet AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); - DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].serial[5], MIBLEsensors[_slot].serial[4],MIBLEsensors[_slot].serial[3],MIBLEsensors[_slot].serial[2],MIBLEsensors[_slot].serial[1],MIBLEsensors[_slot].serial[0]); - DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.Mac[5], _beacon.Mac[4], _beacon.Mac[3],_beacon.Mac[2],_beacon.Mac[1],_beacon.Mac[0]); + DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].MAC[5], MIBLEsensors[_slot].MAC[4],MIBLEsensors[_slot].MAC[3],MIBLEsensors[_slot].MAC[2],MIBLEsensors[_slot].MAC[1],MIBLEsensors[_slot].MAC[0]); + DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.MAC[5], _beacon.MAC[4], _beacon.MAC[3],_beacon.MAC[2],_beacon.MAC[1],_beacon.MAC[0]); MIBLEsensors.erase(MIBLEsensors.begin()+_slot); return; } @@ -447,17 +567,24 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); + // MIBLEsensors[_slot].rssi = _rssi; if(MIBLEsensors[_slot].type==4 || MIBLEsensors[_slot].type==6){ DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); return; } - DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10SlaveType[MIBLEsensors[_slot].type-1],_slot); + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10DeviceType[MIBLEsensors[_slot].type-1],_slot); switch(_beacon.type){ + case 0x01: + MIBLEsensors[_slot].Btn=_beacon.Btn.num + (_beacon.Btn.longPress/2)*6; + MIBLEsensors[_slot].eventType.Btn = 1; + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn ); + break; case 0x04: _tempFloat=(float)(_beacon.temp)/10.0f; if(_tempFloat<60){ MIBLEsensors[_slot].temp=_tempFloat; DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); + MIBLEsensors[_slot].eventType.temp = 1; } DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); break; @@ -466,26 +593,30 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ if(_tempFloat<101){ MIBLEsensors[_slot].hum=_tempFloat; DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); + MIBLEsensors[_slot].eventType.hum = 1; } DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), _beacon.hum); break; case 0x07: + if(MIBLEsensors[_slot].type==MJYD2S){ + MIBLEsensors[_slot].eventType.noMotion = 1; + } MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); break; case 0x08: - _tempFloat =(float)_beacon.moist; - if(_tempFloat<100){ - MIBLEsensors[_slot].moisture=_tempFloat; + if(_beacon.moist<101){ + MIBLEsensors[_slot].moisture=_beacon.moist; DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); + MIBLEsensors[_slot].eventType.moist = 1; } DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); break; case 0x09: - _tempFloat=(float)(_beacon.fert); - if(_tempFloat<65535){ // ??? - MIBLEsensors[_slot].fertility=_tempFloat; + if(_beacon.fert<65535){ + MIBLEsensors[_slot].fertility=_beacon.fert; DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); + MIBLEsensors[_slot].eventType.fert = 1; } DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); break; @@ -493,6 +624,7 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ if(_beacon.bat<101){ MIBLEsensors[_slot].bat = _beacon.bat; DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + MIBLEsensors[_slot].eventType.bat = 1; } DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), _beacon.bat); break; @@ -507,9 +639,13 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ MIBLEsensors[_slot].hum = _tempFloat; DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); } + MIBLEsensors[_slot].eventType.tempHum = 1; DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); break; } + if(MIBLEsensors[_slot].eventType.raw == 0) return; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; } void HM10ParseResponse(char *buf, uint16_t bufsize) { @@ -527,6 +663,8 @@ void HM10ParseResponse(char *buf, uint16_t bufsize) { if(_pos) { uint8_t _newMacArray[6] = {0}; memcpy((void *)_newMacArray,(void *)(_pos+4),6); + uint32_t _rssi = 255- (uint8_t)(_pos[11]); + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("rssi: %u"),(255- (uint8_t)(_pos[11]))); HM10_ReverseMAC(_newMacArray); DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); uint16_t _type=0xffff; @@ -541,7 +679,7 @@ void HM10ParseResponse(char *buf, uint16_t bufsize) { } } } - uint16_t _slot = MIBLEgetSensorSlot(_newMacArray, _type); + uint16_t _slot = MIBLEgetSensorSlot(_newMacArray, _type, _rssi); if(_slot!=0xff) HM10parseMiBeacon(_pos,_slot); } else if (strstr(buf, "LOST")){ @@ -556,10 +694,10 @@ void HM10ParseResponse(char *buf, uint16_t bufsize) { } void HM10readHT_LY(char *_buf){ - DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); if(_buf[0]==0x4f && _buf[1]==0x4b) return; // "OK" if(_buf[0] != 0 && _buf[1] != 0){ - memcpy(&LYWSD0x_HT,(void *)_buf,3); + memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u"),D_CMND_HM10,LYWSD0x_HT.temp,LYWSD0x_HT.hum); uint32_t _slot = HM10.state.sensor; @@ -577,6 +715,13 @@ void HM10readHT_LY(char *_buf){ 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; + MIBLEsensors[_slot].eventType.bat = 1; + } + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; } } @@ -603,6 +748,9 @@ void HM10readHT_CGD1(char *_buf){ MIBLEsensors[_slot].hum = _tempFloat; DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); } + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; } } @@ -630,6 +778,9 @@ void HM10readHT_MJ_HT_V1(char *_buf){ MIBLEsensors[_slot].hum = _tempFloat; DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1: hum updated")); } + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; } void HM10readTLMF(char *_buf){ @@ -648,12 +799,14 @@ void HM10readTLMF(char *_buf){ MIBLEsensors[_slot].showedUp=255; // this sensor is real } MIBLEsensors[_slot].lux = Flora_TLMF.lux; - _tempFloat=(float)Flora_TLMF.moist; - if(_tempFloat<100){ - MIBLEsensors[_slot].moisture = _tempFloat; - } - - MIBLEsensors[_slot].fertility = (float)Flora_TLMF.fert; + MIBLEsensors[_slot].moisture = Flora_TLMF.moist; + MIBLEsensors[_slot].fertility = Flora_TLMF.fert; + MIBLEsensors[_slot].eventType.temp = 1; + MIBLEsensors[_slot].eventType.lux = 1; + MIBLEsensors[_slot].eventType.moist = 1; + MIBLEsensors[_slot].eventType.fert = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; HM10.mode.awaiting = none; HM10.current_task_delay = 0; @@ -663,13 +816,19 @@ void HM10readTLMF(char *_buf){ bool HM10readBat(char *_buf){ DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_HM10,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); if(_buf[0]==0x4f && _buf[1]==0x4b) return false; // "OK" + uint32_t _slot = HM10.state.sensor; + // if(HM10.option.ignoreBogusBattery){ + // if (MIBLEsensors[_slot].type == LYWSD03MMC || MIBLEsensors[_slot].type == MHOC401) return true; + // } if(_buf[0] != 0){ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_HM10,_buf[0]); - uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); if(_buf[0]<101){ MIBLEsensors[_slot].bat=_buf[0]; MIBLEsensors[_slot].showedUp=255; // this sensor is real + MIBLEsensors[_slot].eventType.bat = 1; + MIBLEsensors[_slot].shallSendMQTT = 1; + HM10.mode.shallTriggerTele = 1; return true; } } @@ -694,7 +853,18 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds success = true; } - if(i==0) return success; + if(i==0){ + if(HM10.mode.shallTriggerTele){ // let us use the spare time for other things + HM10.mode.shallTriggerTele=0; + if(HM10.option.noRealTime){ + HM10.mode.triggeredTele=0; + return success; + } + HM10.mode.triggeredTele=1; + HM10triggerTele(); + } + return success; + } switch (HM10.mode.awaiting){ case bat: @@ -784,8 +954,8 @@ void HM10_TaskEvery100ms(){ break; case TASK_HM10_CONN: char _con[20]; - sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors[HM10.state.sensor].serial[0],MIBLEsensors[HM10.state.sensor].serial[1],MIBLEsensors[HM10.state.sensor].serial[2],MIBLEsensors[HM10.state.sensor].serial[3],MIBLEsensors[HM10.state.sensor].serial[4],MIBLEsensors[HM10.state.sensor].serial[5]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: connect %s"),D_CMND_HM10, _con); + sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors[HM10.state.sensor].MAC[0],MIBLEsensors[HM10.state.sensor].MAC[1],MIBLEsensors[HM10.state.sensor].MAC[2],MIBLEsensors[HM10.state.sensor].MAC[3],MIBLEsensors[HM10.state.sensor].MAC[4],MIBLEsensors[HM10.state.sensor].MAC[5]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: %s connect %s"),D_CMND_HM10,kHM10DeviceType[MIBLEsensors[HM10.state.sensor].type-1],_con); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -825,11 +995,12 @@ void HM10_TaskEvery100ms(){ break; case TASK_HM10_SUB_L2: AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); - HM10.current_task_delay = 25; // set task delay + HM10.current_task_delay = 85; // set task delay HM10.mode.awaiting = tempHumLY; HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; - HM10Serial->write("AT+NOTIFY_ON003C"); + if(MIBLEsensors[HM10.state.sensor].type == LYWSD02) HM10Serial->write("AT+NOTIFY_ON003C"); + else HM10Serial->write("AT+NOTIFY_ON004B"); //MHO-C303 break; case TASK_HM10_UN_L2: AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); @@ -837,7 +1008,8 @@ void HM10_TaskEvery100ms(){ HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10.mode.awaiting = none; - HM10Serial->write("AT+NOTIFYOFF003C"); + if(MIBLEsensors[HM10.state.sensor].type == LYWSD02) HM10Serial->write("AT+NOTIFY_OFF003C"); + else HM10Serial->write("AT+NOTIFY_OFF004B"); //MHO-C303 break; case TASK_HM10_TIME_L2: AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); @@ -850,20 +1022,21 @@ void HM10_TaskEvery100ms(){ HM10Serial->write(Rtc.time_timezone / 60); AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); break; - case TASK_HM10_READ_BT_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); - HM10.current_task_delay = 2; // set task delay - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA003A?"); - HM10.mode.awaiting = bat; - break; + // case TASK_HM10_READ_BT_L3: + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); + // HM10.current_task_delay = 2; // set task delay + // HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + // runningTaskLoop = false; + // HM10Serial->write("AT+READDATA003A?"); + // HM10.mode.awaiting = bat; + // break; case TASK_HM10_READ_BT_L2: AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; - HM10Serial->write("AT+READDATA0043?"); + if(MIBLEsensors[HM10.state.sensor].type == LYWSD02) HM10Serial->write("AT+READDATA0043?"); + else HM10Serial->write("AT+READDATA0052?"); //MHO-C303 HM10.mode.awaiting = bat; break; case TASK_HM10_READ_BF_FL: @@ -1029,10 +1202,10 @@ void HM10EverySecond(bool restart){ case MJ_HT_V1: case CGG1: HM10_Read_MJ_HT_V1(); break; - case LYWSD02: + case LYWSD02: case MHOC303: HM10_Read_LYWSD02(); break; - case LYWSD03MMC: + case LYWSD03MMC: case MHOC401: HM10_Read_LYWSD03(); break; case CGD1: @@ -1150,54 +1323,147 @@ bool HM10Cmd(void) { return serviced; } +/** + * @brief trigger real-time message + * + */ +void HM10triggerTele(void){ + HM10.mode.triggeredTele = 1; + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + #ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages + #endif // USE_RULES + } +} /*********************************************************************************************\ * Presentation \*********************************************************************************************/ const char HTTP_HM10[] PROGMEM = "{s}HM10 V%u{m}%u%s / %u{e}"; -const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_HM10_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; -const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%uus/cm{e}"; +const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; +const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e}"; const char HTTP_HM10_HL[] PROGMEM = "{s}
{m}
{e}"; void HM10Show(bool json) { if (json) { + if(!HM10.mode.triggeredTele){ + if(HM10.option.noSummary) return; // no message at TELEPERIOD + } + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - char slave[33]; - sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); - ResponseAppend_P(PSTR(",\"%s\":{"),slave); - if (MIBLEsensors[i].type==FLORA){ - if(!isnan(MIBLEsensors[i].temp)){ // this is the error code -> no temperature - char temperature[FLOATSZ]; // all sensors have temperature - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); + if(HM10.mode.triggeredTele && MIBLEsensors[i].eventType.raw == 0) continue; + if(HM10.mode.triggeredTele && MIBLEsensors[i].shallSendMQTT==0) continue; + + ResponseAppend_P(PSTR(",\"%s-%02x%02x%02x\":"), // do not add the '{' now ... + kHM10DeviceType[MIBLEsensors[i].type-1], + MIBLEsensors[i].MAC[3], MIBLEsensors[i].MAC[4], MIBLEsensors[i].MAC[5]); + + uint32_t _positionCurlyBracket = strlen(mqtt_data); // ... this will be a ',' first, but later be replaced + + if((!HM10.mode.triggeredTele && !HM10.option.minimalSummary)||HM10.mode.triggeredTele){ + bool tempHumSended = false; + if(MIBLEsensors[i].feature.tempHum){ + if(MIBLEsensors[i].eventType.tempHum || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + tempHumSended = true; } - else { - ResponseAppend_P(PSTR("}")); - continue; - } - if(MIBLEsensors[i].lux!=0x0ffffff){ // this is the error code -> no lux - ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); - } - if(!isnan(MIBLEsensors[i].moisture)){ - ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%d"), MIBLEsensors[i].moisture); - } - if(!isnan(MIBLEsensors[i].fertility)){ - ResponseAppend_P(PSTR(",\"Fertility\":%d"), MIBLEsensors[i].fertility); } } - if (MIBLEsensors[i].type>FLORA){ - if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ - ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum); + if(MIBLEsensors[i].feature.temp && !tempHumSended){ + if(MIBLEsensors[i].eventType.temp || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].temp)) { + char temperature[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } } } - if(MIBLEsensors[i].bat!=0x00){ // this is the error code -> no battery + if(MIBLEsensors[i].feature.hum && !tempHumSended){ + if(MIBLEsensors[i].eventType.hum || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate) { + if (!isnan(MIBLEsensors[i].hum)) { + char hum[FLOATSZ]; + dtostrfd(MIBLEsensors[i].hum, Settings.flag2.humidity_resolution, hum); + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum); + } + } + } + if (MIBLEsensors[i].feature.lux){ + if(MIBLEsensors[i].eventType.lux || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].lux!=0x0ffffff) { // this is the error code -> no lux + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); + } + } + } + if (MIBLEsensors[i].feature.moist){ + if(MIBLEsensors[i].eventType.moist || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].moisture!=0xff) { + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture); + } + } + } + if (MIBLEsensors[i].feature.fert){ + if(MIBLEsensors[i].eventType.fert || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].fertility!=0xffff) { + ResponseAppend_P(PSTR(",\"Fertility\":%u"), MIBLEsensors[i].fertility); + } + } + } + if (MIBLEsensors[i].feature.Btn){ + if(MIBLEsensors[i].eventType.Btn){ + ResponseAppend_P(PSTR(",\"Btn\":%u"),MIBLEsensors[i].Btn); + } + } + } // minimal summary + if (MIBLEsensors[i].feature.PIR){ + if(MIBLEsensors[i].eventType.motion || !HM10.mode.triggeredTele){ + if(HM10.mode.triggeredTele) ResponseAppend_P(PSTR(",\"PIR\":1")); // only real-time + ResponseAppend_P(PSTR(",\"Events\":%u"),MIBLEsensors[i].events); + } + else if(MIBLEsensors[i].eventType.noMotion && HM10.mode.triggeredTele){ + ResponseAppend_P(PSTR(",\"PIR\":0")); + } + } + + if (MIBLEsensors[i].type == FLORA && !HM10.mode.triggeredTele) { + if (MIBLEsensors[i].firmware[0] != '\0') { // this is the error code -> no firmware + ResponseAppend_P(PSTR(",\"Firmware\":\"%s\""), MIBLEsensors[i].firmware); + } + } + + if (MIBLEsensors[i].feature.NMT || !HM10.mode.triggeredTele){ + if(MIBLEsensors[i].eventType.NMT){ + ResponseAppend_P(PSTR(",\"NMT\":%u"), MIBLEsensors[i].NMT); + } + } + if (MIBLEsensors[i].feature.bat){ + if(MIBLEsensors[i].eventType.bat || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){ + if (MIBLEsensors[i].bat != 0x00) { // this is the error code -> no battery ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } } - ResponseAppend_P(PSTR("}")); + } + if (HM10.option.showRSSI && HM10.mode.triggeredTele) ResponseAppend_P(PSTR(",\"RSSI\":%d"), MIBLEsensors[i].rssi); + + + if(_positionCurlyBracket==strlen(mqtt_data)) ResponseAppend_P(PSTR(",")); // write some random char, to be overwritten in the next step + ResponseAppend_P(PSTR("}")); + mqtt_data[_positionCurlyBracket] = '{'; + MIBLEsensors[i].eventType.raw = 0; + if(MIBLEsensors[i].shallSendMQTT==1){ + MIBLEsensors[i].shallSendMQTT = 0; + break; + } } + HM10.mode.triggeredTele = 0; + #ifdef USE_WEBSERVER } else { static uint16_t _page = 0; @@ -1216,31 +1482,32 @@ void HM10Show(bool json) WSContentSend_PD(HTTP_HM10, HM10.firmware, i+1,stemp,MIBLEsensors.size()); for (i; i no valid value - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); } - if(!isnan(MIBLEsensors[i].moisture)){ - WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].moisture); + if(MIBLEsensors[i].moisture!=0xff){ + WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].moisture); } - if(!isnan(MIBLEsensors[i].fertility)){ - WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].fertility); + if(MIBLEsensors[i].fertility!=0xffff){ + WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].fertility); } } if (MIBLEsensors[i].type>FLORA){ // everything "above" Flora if(!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)){ - WSContentSend_THD(kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + WSContentSend_THD(kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); } } if(MIBLEsensors[i].bat!=0x00){ - WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); + WSContentSend_PD(HTTP_BATTERY, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); } + WSContentSend_PD(HTTP_RSSI, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].rssi); } _counter++; if(_counter>3) {