diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_MI_ESP32.ino index 814d31151..36fdd7fdf 100644 --- a/tasmota/xsns_62_MI_ESP32.ino +++ b/tasmota/xsns_62_MI_ESP32.ino @@ -20,6 +20,8 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.1.6 20201022 changed - Beacon support, RSSI at TELEPERIOD, refactoring + ------- 0.9.1.5 20201021 changed - HASS related ('null', hold back discovery), number of found sensors for RULES ------- 0.9.1.4 20201020 changed - use BearSSL for decryption, revert to old TELEPERIOD-cycle as default @@ -57,7 +59,6 @@ void MI32scanEndedCB(NimBLEScanResults results); void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); - struct { uint16_t perPage = 4; uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start @@ -82,11 +83,13 @@ struct { uint32_t shallClearResults:1; // BLE scan results uint32_t shallShowStatusInfo:1; // react to amount of found sensors via RULES uint32_t firstAutodiscoveryDone:1; + uint32_t activeBeacon; }; uint32_t all = 0; } mode; struct { - uint8_t sensor; // points to to the number 0...255 + uint8_t sensor; // points to to the number 0...255 + uint8_t beaconScanCounter; // countdown timer in seconds } state; struct { uint32_t allwaysAggregate:1; // always show all known values of one sensor in brdigemode @@ -226,7 +229,7 @@ struct mi_sensor_t{ uint32_t raw; } eventType; - int rssi; + int RSSI; uint32_t lastTime; uint32_t lux; float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx @@ -250,8 +253,28 @@ struct mi_sensor_t{ }; }; +struct scan_entry_t { + uint8_t MAC[6]; + uint16_t CID; + uint16_t SVC; + uint16_t UUID; + int32_t RSSI; +}; + +struct generic_beacon_t { + uint8_t MAC[6]; + uint32_t time; + int32_t RSSI; + uint16_t CID; // company identifier + uint16_t UUID; // the first, if more than one exists + uint16_t SVC; + bool active = false; +}; + std::vector MIBLEsensors; std::vector MIBLEbindKeys; +std::array MIBLEbeacons; // we support a fixed number +std::vector MINBLEscanResult; static BLEScan* MI32Scan; @@ -263,7 +286,9 @@ static BLEScan* MI32Scan; const char S_JSON_MI32_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s\":%d}"; const char S_JSON_MI32_COMMAND[] PROGMEM = "{\"" D_CMND_MI32 "%s%s\"}"; -const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery|Unit|Key"; +// const char S_JSON_MI32_BCOMMAND_SVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s\":%s}"; +const char S_JSON_MI32_BCOMMAND_SVALUE[] PROGMEM = "{\"" D_CMND_MI32 "%s%u\":\"%s\"}"; +const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery|Unit|Key|Beacon"; #define FLORA 1 #define MJ_HT_V1 2 @@ -318,7 +343,8 @@ enum MI32_Commands { // commands useable in console or rules CMND_MI32_PAGE, // sensor entries per web page, which will be shown alternated CMND_MI32_BATTERY, // read all battery levels CMND_MI32_UNIT, // toggles the displayed unit between C/F (LYWSD02) - CMND_MI32_KEY // add bind key to a mac for packet decryption + CMND_MI32_KEY, // add bind key to a mac for packet decryption + CMND_MI32_BEACON // add up to 4 beacons defined by their MAC addresses }; enum MI32_TASK { @@ -329,6 +355,12 @@ enum MI32_TASK { MI32_TASK_UNIT = 4, }; +enum MI32_BEACON_CMND { + MI32_BEACON_ON = 0, + MI32_BEACON_OFF = 1, + MI32_BEACON_DEL = 2, +}; + /*********************************************************************************************\ * Classes \*********************************************************************************************/ @@ -360,32 +392,40 @@ class MI32SensorCallback : public NimBLEClientCallbacks { class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { void onResult(NimBLEAdvertisedDevice* advertisedDevice) { // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); - if (advertisedDevice->getServiceDataCount() == 0) { - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); - MI32Scan->erase(advertisedDevice->getAddress()); - return; - } - uint16_t uuid = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value; - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("UUID: %x"),uuid); + int RSSI = advertisedDevice->getRSSI(); uint8_t addr[6]; memcpy(addr,advertisedDevice->getAddress().getNative(),6); MI32_ReverseMAC(addr); - int rssi = 0xffff; - if(advertisedDevice->haveRSSI()) { - rssi = advertisedDevice->getRSSI(); + if (advertisedDevice->getServiceDataCount() == 0) { + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + if(MI32.state.beaconScanCounter==0 && !MI32.mode.activeBeacon){ + MI32Scan->erase(advertisedDevice->getAddress()); + return; + } + else{ + MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr); + return; + } + } - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("RSSI: %d"),rssi); // actually i never got a 0xffff - if(uuid==0xfe95) { - MI32ParseResponse((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi); + uint16_t UUID = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value; + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("UUID: %x"),UUID); + + size_t ServiceDataLength = advertisedDevice->getServiceData(0).length(); + if(UUID==0xfe95) { + MI32ParseResponse((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); } - else if(uuid==0xfdcd) { - MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi); + else if(UUID==0xfdcd) { + MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); } - else if(uuid==0x181a) { //ATC - MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi); + else if(UUID==0x181a) { //ATC + MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),ServiceDataLength, addr, RSSI); } else { - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), uuid, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + if(MI32.state.beaconScanCounter!=0 || MI32.mode.activeBeacon){ + MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr); + } + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), UUID, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); MI32Scan->erase(advertisedDevice->getAddress()); } }; @@ -421,6 +461,54 @@ void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pD * Helper functions \*********************************************************************************************/ +/** + * @brief Remove all colons from null terminated char array + * + * @param _string Typically representing a MAC-address like AA:BB:CC:DD:EE:FF + */ +void MI32stripColon(char* _string){ + uint32_t _length = strlen(_string); + uint32_t _index = 0; + while (_index < _length) { + char c = _string[_index]; + if(c==':'){ + memmove(_string+_index,_string+_index+1,_length-_index); + } + _index++; + } + _string[_index] = 0; +} + +/** + * @brief Convert string that repesents a hexadecimal number to a byte array + * + * @param _string input string in format: AABBCCDDEEFF or AA:BB:CC:DD:EE:FF, caseinsensitive + * @param _mac target byte array must match the correct size (i.e. AA:BB -> uint8_t bytes[2]) + */ + +void MI32HexStringToBytes(char* _string, uint8_t* _byteArray) { + MI32stripColon(_string); + UpperCase(_string,_string); + uint32_t index = 0; + uint32_t _end = strlen(_string); + memset(_byteArray,0,_end/2); + while (index < _end) { + char c = _string[index]; + uint8_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _byteArray[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } +} + +/** + * @brief Reverse an array of 6 bytes + * + * @param _mac a byte array of size 6 (typicalliy representing a MAC address) + */ void MI32_ReverseMAC(uint8_t _mac[]){ uint8_t _reversedMAC[6]; for (uint8_t i=0; i<6; i++){ @@ -432,9 +520,7 @@ void MI32_ReverseMAC(uint8_t _mac[]){ #ifdef USE_MI_DECRYPTION void MI32AddKey(char* payload){ mi_bindKey_t keyMAC; - memset(keyMAC.buf,0,sizeof(keyMAC)); - UpperCase(payload,payload); - MI32KeyMACStringToBytes(payload,keyMAC.buf); + MI32HexStringToBytes(payload,keyMAC.buf); bool unknownKey = true; for(uint32_t i=0; i= '0' && c <= '9') - value = (c - '0'); - else if (c >= 'A' && c <= 'F') - value = (10 + (c - 'A')); - _keyMAC[(index/2)] += value << (((index + 1) % 2) * 4); - index++; - } - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: %s to:"),_string); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: key-array: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),_keyMAC[0],_keyMAC[1],_keyMAC[2],_keyMAC[3],_keyMAC[4],_keyMAC[5],_keyMAC[6],_keyMAC[7],_keyMAC[8],_keyMAC[9],_keyMAC[10],_keyMAC[11],_string,_keyMAC[12],_keyMAC[13],_keyMAC[14],_keyMAC[15]); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: MAC-array: %02X%02X%02X%02X%02X%02X"),_keyMAC[16],_keyMAC[17],_keyMAC[18],_keyMAC[19],_keyMAC[20],_keyMAC[21]); -} - /** * @brief Decrypts payload in place * @@ -533,10 +596,7 @@ int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type){ br_ccm_run(&ctx, 0, payload, data_len); memcpy((uint8_t*)packet->payload+1,payload,data_len); //back to the packet - // br_ccm_get_tag(&ctx, &checkTag); ret = br_ccm_check_tag(&ctx, &tag); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("packetTag: %08x"),tag); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("computedTag: %08x"),checkTag); AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]); return ret-1; @@ -611,7 +671,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter) _newSensor.feature.raw = 0; _newSensor.temp =NAN; _newSensor.bat=0x00; - _newSensor.rssi=0xffff; + _newSensor.RSSI=0xffff; _newSensor.lux = 0x00ffffff; switch (_type) { @@ -670,6 +730,10 @@ void MI32triggerTele(void){ } } +/** + * @brief Is called after every finding of new BLE sensor + * + */ void MI32StatusInfo() { MI32.mode.shallShowStatusInfo = 0; Response_P(PSTR("{\"%s\":{\"found\":%u}}"), D_CMND_MI32, MIBLEsensors.size()); @@ -684,6 +748,7 @@ void MI32Init(void) { MIBLEsensors.reserve(10); MIBLEbindKeys.reserve(10); +MINBLEscanResult.reserve(20); MI32.mode.init = false; if (!MI32.mode.init) { NimBLEDevice::init(""); @@ -1167,6 +1232,7 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){ decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize, LYWSD03MMC); //start with PID // AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)&_beacon.productID,_bufSize); } + else return; // 0x3058 holds no data, TODO: check for unpaired devices, that need connections break; case MJYD2S: AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MJYD2S: %x"),_beacon.frame); @@ -1180,18 +1246,6 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){ if (_beacon.frame != 0x5910){ decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize,MJYD2S); //start with PID } - else{ - // This seems to be some kind of wake-up packet only, as it shows up before all kinds of messages, not only motion - // if(millis()-MIBLEsensors[_slot].lastTime>120000){ - // MIBLEsensors[_slot].eventType = 1; - // MIBLEsensors[_slot].events++; - // MIBLEsensors[_slot].shallSendMQTT = 1; - // MIBLEsensors[_slot].lastTime = millis(); - // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: MJYD2S secondary PIR")); - // MIBLEsensors[_slot].NMT = 0; - // MI32.mode.shallTriggerTele = 1; - // } - } break; } if(decryptRet!=0){ @@ -1315,13 +1369,13 @@ if(decryptRet!=0){ if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; } -void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int rssi){ +void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ ATCPacket_t *_packet = (ATCPacket_t*)_buf; uint32_t _slot = MIBLEgetSensorSlot(_packet->MAC, 0x0a1c, _packet->frameCnt); // This must be a hard-coded fake ID AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); if(_slot==0xff) return; - MIBLEsensors[_slot].rssi=rssi; + MIBLEsensors[_slot].RSSI=RSSI; MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; MIBLEsensors.at(_slot).hum = (float)_packet->hum; @@ -1334,13 +1388,13 @@ void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int rssi) } -void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int rssi){ // no MiBeacon +void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ // no MiBeacon uint8_t _addr[6]; memcpy(_addr,addr,6); uint32_t _slot = MIBLEgetSensorSlot(_addr, 0x0576, 0); // This must be hard-coded, no object-id in Cleargrass-packet, we have no packet counter too AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); if(_slot==0xff) return; - MIBLEsensors[_slot].rssi=rssi; + MIBLEsensors[_slot].RSSI=RSSI; cg_packet_t _packet; memcpy((char*)&_packet,_buf,sizeof(_packet)); switch (_packet.mode){ @@ -1375,7 +1429,7 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int rssi if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1; } -void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int rssi) { +void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) { if(bufsize<9) { //9 is from the NLIGHT return; } @@ -1385,11 +1439,137 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int rssi) { memcpy(_addr,addr,6); uint16_t _slot = MIBLEgetSensorSlot(_addr, _type, buf[4]); if(_slot!=0xff) { - MIBLEsensors[_slot].rssi=rssi; + MIBLEsensors[_slot].RSSI=RSSI; MI32parseMiBeacon(buf,_slot,bufsize); } } +/** + * @brief Parse a BLE advertisement packet + * + * @param payload + * @param payloadLength + * @param CID + * @param SVC + * @param UUID + */ +void MI32ParseGenericBeacon(uint8_t* payload, size_t payloadLength, uint16_t* CID, uint16_t*SVC, uint16_t* UUID){ + AddLog_P2(LOG_LEVEL_DEBUG_MORE,PSTR("MI32: Beacon:____________")); + for (uint32_t i = 0; i19) { + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: Scan buffer full")); + MI32.state.beaconScanCounter = 1; + return; + } + for(auto _scanResult : MINBLEscanResult){ + if(memcmp(addr,_scanResult.MAC,6)==0){ + // AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: known device")); + return; + } + } + scan_entry_t _new; + _new.RSSI = RSSI; + _new.CID = 0; + _new.SVC = 0; + _new.UUID = 0; + memcpy(_new.MAC,addr,sizeof(_new.MAC)); + MI32ParseGenericBeacon(payload,payloadLength,&_new.CID,&_new.SVC,&_new.UUID); + MINBLEscanResult.push_back(_new); +} + + +/** + * @brief Add a beacon defined by its MAC-address, if only zeros are given, the beacon will be deactivated + * + * @param index 1-4 beacons are currently supported + * @param data null terminated char array representing a MAC-address in hex + */ +void MI32addBeacon(uint8_t index, char* data){ + auto &_new = MIBLEbeacons[index-1]; //TODO: check + MI32HexStringToBytes(data,_new.MAC); + char _MAC[18]; + ToHex_P(MIBLEbeacons[index-1].MAC,6,_MAC,18,':'); + char _empty[6] = {0}; + _new.time = 0; + if(memcmp(_empty,_new.MAC,6) == 0){ + _new.active = false; + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: beacon%u deactivated"), index); + } + else{ + _new.active = true; + MI32.mode.activeBeacon = 1; + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: beacon added with MAC: %s"), _MAC); + } +} + +/** + * @brief Present BLE scan in the console, after that deleting the scan data + * + */ +void MI32showScanResults(){ + AddLog_P2(LOG_LEVEL_INFO,PSTR("MI32: found %u devices in scan:"), MINBLEscanResult.size()); + for(auto _scanResult : MINBLEscanResult){ + char _MAC[18]; + ToHex_P(_scanResult.MAC,6,_MAC,18,':'); + AddLog_P2(LOG_LEVEL_INFO,PSTR("MAC: %s _ CID: %04x _ SVC: %04x _ UUID: %04x _ RSSI: %d"), _MAC, _scanResult.CID, _scanResult.SVC, _scanResult.UUID, _scanResult.RSSI); + } + MINBLEscanResult.clear(); +} /***********************************************************************\ * Read data from connections \***********************************************************************/ @@ -1473,6 +1653,25 @@ void MI32EverySecond(bool restart){ } } + uint32_t _idx = 0; + uint32_t _activeBeacons = 0; + for (auto &_beacon : MIBLEbeacons){ + _idx++; + if(_beacon.active == false) continue; + _activeBeacons++; + _beacon.time++; + Response_P(PSTR("{\"Beacon%u\":{\"Time\":%u}}"), _beacon.time); + XdrvRulesProcess(); + } + if(_activeBeacons==0) MI32.mode.activeBeacon = 0; + + if(MI32.state.beaconScanCounter!=0){ + MI32.state.beaconScanCounter--; + if(MI32.state.beaconScanCounter==0){ + MI32showScanResults(); + } + } + if(MI32.mode.shallShowStatusInfo == 1){ MI32StatusInfo(); } @@ -1522,16 +1721,17 @@ void MI32EverySecond(bool restart){ if(_counter==0) { MI32.state.sensor = _nextSensorSlot; - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u of %u"),D_CMND_MI32, MI32.state.sensor, MIBLEsensors.size()-1); MI32.mode.canScan = 0; // if (MI32.mode.runningScan|| MI32.mode.connected || MI32.mode.willConnect) return; if (MI32.mode.connected || MI32.mode.willConnect) return; _nextSensorSlot++; MI32.mode.canConnect = 1; if(MI32.mode.connected == 0) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] ); + if (MI32.mode.shallReadBatt) { + //TODO: decide automatically, which sensor can not work without connections + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u of %u"),D_CMND_MI32, MI32.state.sensor, MIBLEsensors.size()-1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] ); - if (MI32.mode.shallReadBatt) { MI32StartTask(MI32_TASK_BATT); } #ifndef USE_MI_DECRYPTION // turn off connections, because we only listen to advertisements @@ -1638,6 +1838,32 @@ bool MI32Cmd(void) { } break; #endif //USE_MI_DECRYPTION + case CMND_MI32_BEACON: + if (XdrvMailbox.data_len == 0) { + switch(XdrvMailbox.index){ + case 0: + MI32.state.beaconScanCounter = 8; + Response_P(S_JSON_MI32_BCOMMAND_SVALUE, command, XdrvMailbox.index,PSTR("\"scanning\"")); + break; + case 1: case 2: case 3: case 4: + char _MAC[18]; + ToHex_P(MIBLEbeacons[XdrvMailbox.index-1].MAC,6,_MAC,18,':'); + Response_P(S_JSON_MI32_BCOMMAND_SVALUE, command, XdrvMailbox.index,_MAC); + break; + } + } + else { + if(XdrvMailbox.data_len == 12 || XdrvMailbox.data_len == 17){ // MAC-string without or with colons + switch(XdrvMailbox.index){ + case 1: case 2: case 3: case 4: + MI32addBeacon(XdrvMailbox.index,XdrvMailbox.data); + break; + } + } + Response_P(S_JSON_MI32_BCOMMAND_SVALUE, command, XdrvMailbox.index,XdrvMailbox.data); + } + break; + default: // else for Unknown command serviced = false; @@ -1654,7 +1880,7 @@ bool MI32Cmd(void) { * Presentation \*********************************************************************************************/ -const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0.9.1.5{m}%u%s / %u{e}"; +const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0916{m}%u%s / %u{e}"; const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}"; const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u %%{e}"; @@ -1825,8 +2051,7 @@ void MI32Show(bool json) } } } - if (MI32.option.showRSSI && MI32.mode.triggeredTele) ResponseAppend_P(PSTR(",\"RSSI\":%d"), MIBLEsensors[i].rssi); - + if (MI32.option.showRSSI) 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("}")); @@ -1838,6 +2063,17 @@ void MI32Show(bool json) } } MI32.mode.triggeredTele = 0; +// add beacons + uint32_t _idx = 0; + for (auto _beacon : MIBLEbeacons){ + _idx++; + if(!_beacon.active) continue; + char _MAC[18]; + ToHex_P(_beacon.MAC,6,_MAC,18,':'); + ResponseAppend_P(PSTR(",\"Beacon%u\":{\"MAC\":\"%s\",\"CID\":\"0x%04x\",\"SVC\":\"0x%04x\"," + "\"UUID\":\"0x%04x\",\"Time\":%u,\"RSSI\":%d}"), + _idx,_MAC,_beacon.CID,_beacon.SVC,_beacon.UUID,_beacon.time,_beacon.RSSI); + } #ifdef USE_HOME_ASSISTANT if(hass_mode==2){ MI32.option.noSummary = _noSummarySave; @@ -1865,7 +2101,7 @@ void MI32Show(bool json) char _MAC[18]; ToHex_P(MIBLEsensors[i].MAC,6,_MAC,18,':'); WSContentSend_PD(HTTP_MI32_MAC, kMI32DeviceType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, _MAC); - WSContentSend_PD(HTTP_RSSI, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].rssi); + WSContentSend_PD(HTTP_RSSI, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].RSSI); if (MIBLEsensors[i].type==FLORA) { if (!isnan(MIBLEsensors[i].temp)) { char temperature[FLOATSZ]; @@ -1909,6 +2145,27 @@ void MI32Show(bool json) } if (MIBLEsensors.size()%MI32.perPage==0 && _page==MIBLEsensors.size()/MI32.perPage) { _page = 0; } if (_page>MIBLEsensors.size()/MI32.perPage) { _page = 0; } + + //always at the bottom of the page + uint32_t _idx=0; + if(MI32.mode.activeBeacon){ + WSContentSend_PD(HTTP_MI32_HL); + char _sbeacon[] = "Beacon1"; + for (auto &_beacon : MIBLEbeacons){ + _idx++; + if(!_beacon.active) continue; + WSContentSend_PD(HTTP_MI32_HL); + _sbeacon[6] = _idx + 0x30; + char _MAC[18]; + ToHex_P(_beacon.MAC,6,_MAC,18,':'); + WSContentSend_PD(HTTP_MI32_MAC, _sbeacon, D_MAC_ADDRESS, _MAC); + WSContentSend_PD(HTTP_RSSI, _sbeacon, _beacon.RSSI); + if(_beacon.CID!=0) WSContentSend_PD(PSTR("{s}Beacon%u CID{m}0x%04X{e}"),_idx, _beacon.CID); + if(_beacon.SVC!=0) WSContentSend_PD(PSTR("{s}Beacon%u SVC{m}0x%04X{e}"),_idx, _beacon.SVC); + if(_beacon.UUID!=0) WSContentSend_PD(PSTR("{s}Beacon%u UUID{m}0x%04X{e}"),_idx, _beacon.UUID); + WSContentSend_PD(PSTR("{s}Beacon%u Time{m}%u seconds{e}"),_idx, _beacon.time); + } + } #endif // USE_WEBSERVER } }