diff --git a/src/driver/drv_tuyaMCU.c b/src/driver/drv_tuyaMCU.c index ab994d8fb..a027b59c0 100644 --- a/src/driver/drv_tuyaMCU.c +++ b/src/driver/drv_tuyaMCU.c @@ -159,6 +159,10 @@ typedef struct tuyaMCUMapping_s { byte bDPCache; // store last channel value to avoid sending it again int prevValue; + // allow storing raw data for later usage + byte *rawData; + int rawBufferSize; + int rawDataLen; // TODO //int mode; // list @@ -326,6 +330,9 @@ void TuyaMCU_MapIDToChannel(int fnId, int dpType, int channel, int bDPCache) { cur->bDPCache = bDPCache; cur->prevValue = 0; cur->next = g_tuyaMappings; + cur->rawData = 0; + cur->rawDataLen = 0; + cur->rawBufferSize = 0; g_tuyaMappings = cur; } @@ -1273,6 +1280,64 @@ void TuyaMCU_ParseWeatherData(const byte* data, int len) { ofs += stringLen; } } + + + +int http_obk_json_dps(int id, void* request, jsonCb_t printer) { + int i; + int iCnt = 0; + char tmp[8]; + tuyaMCUMapping_t* cur; + + printer(request, "["); + + + cur = g_tuyaMappings; + while (cur) { + if (id == -1 || id == cur->fnId) { + if (iCnt) { + printer(request, ","); + } + iCnt++; + printer(request, "{\"id\":%i,\"type\":%i,\"data\":", cur->fnId, cur->dpType); + if (cur->rawData == 0) { + printer(request, "0\"}", cur->rawData); + } + else { + if (cur->dpType == DP_TYPE_BOOL || cur->dpType == DP_TYPE_ENUM + || cur->dpType == DP_TYPE_VALUE) { + if (cur->rawDataLen == 1) { + i = cur->rawData[0]; + } + else if (cur->rawDataLen == 4) { + i = cur->rawData[0] << 24 | cur->rawData[1] << 16 | cur->rawData[2] << 8 | cur->rawData[3]; + } + else { + i = 0; + } + printer(request, "%i}", i); + } + else if (cur->dpType == DP_TYPE_STRING) { + printer(request, "\"%s\"}", cur->rawData); + } + else { + printer(request, "\""); + for (i = 0; i < cur->rawDataLen; i++) { + sprintf(tmp, "%02X", cur->rawData[i]); + printer(request, "%s", tmp); + } + printer(request, "\"}"); + } + } + } + cur = cur->next; + } + + + printer(request, "]"); + return 0; +} + // Protocol version - 0x00 (not 0x03) // Used for battery powered devices, eg. door sensor. // Packet ID: 0x08 @@ -1448,6 +1513,17 @@ void TuyaMCU_ParseStateMessage(const byte* data, int len) { TuyaMCU_PublishDPToMQTT(data, ofs); } + if (CFG_HasFlag(OBK_FLAG_TUYAMCU_STORE_RAW_DATA)) { + if (mapping) { + if (mapping->rawBufferSize < sectorLen) { + mapping->rawData = realloc(mapping->rawData, sectorLen); + mapping->rawBufferSize = sectorLen; + } + mapping->rawDataLen = sectorLen; + memcpy(mapping->rawData, data + ofs + 4, sectorLen); + } + } + if (sectorLen == 1) { iVal = (int)data[ofs + 4]; addLogAdv(LOG_INFO, LOG_FEATURE_TUYAMCU, "TuyaMCU_ParseStateMessage: raw data 1 byte: %i\n", iVal); diff --git a/src/httpserver/http_fns.c b/src/httpserver/http_fns.c index d2f2b5600..712388997 100644 --- a/src/httpserver/http_fns.c +++ b/src/httpserver/http_fns.c @@ -2573,8 +2573,11 @@ const char* g_obk_flagNames[] = { "[TuyaMCU] Use queue", "[HTTP] Disable authentication in safe mode (not recommended)", "[MQTT Discovery] Don't merge toggles and dimmers into lights", + "[TuyaMCU] Store raw data", "error", -}; + "error", +}; + int http_fn_cfg_generic(http_request_t* request) { int i; char tmpA[64]; @@ -2611,7 +2614,10 @@ int http_fn_cfg_generic(http_request_t* request) { CFG_Save_IfThereArePendingChanges(); + // 32 bit type hprintf255(request, "

Flags (Current value=%i)

", CFG_GetFlags()); + // 64 bit - TODO fixme + //hprintf255(request, "

Flags (Current value=%lu)

", CFG_GetFlags64()); poststr(request, "
"); for (i = 0; i < OBK_TOTAL_FLAGS; i++) { diff --git a/src/httpserver/json_interface.c b/src/httpserver/json_interface.c index 5b1142d6c..93057fc57 100644 --- a/src/httpserver/json_interface.c +++ b/src/httpserver/json_interface.c @@ -744,6 +744,9 @@ static int http_tasmota_json_status_generic(void* request, jsonCb_t printer) { return 0; } +// drv_tuyaMCU.c +int http_obk_json_dps(int id, void* request, jsonCb_t printer); + int JSON_ProcessCommandReply(const char* cmd, const char* arg, void* request, jsonCb_t printer, int flags) { int i; @@ -1027,6 +1030,20 @@ int JSON_ProcessCommandReply(const char* cmd, const char* arg, void* request, js else if (!wal_strnicmp(cmd, "Ch", 2)) { http_obk_json_channels(request, printer); } +#ifndef OBK_DISABLE_ALL_DRIVERS +#if ENABLE_DRIVER_TUYAMCU + else if (!wal_strnicmp(cmd, "Dp", 2)) { + int id = -1; + if (isdigit(cmd[2])) { + sscanf(cmd + 2, "%i", &id); + } + http_obk_json_dps(id,request, printer); + if (flags == COMMAND_FLAG_SOURCE_MQTT) { + MQTT_PublishPrinterContentsToStat((struct obk_mqtt_publishReplyPrinter_s*)request, "DP"); + } + } +#endif +#endif else { printer(request, "{"); printer(request, "}"); diff --git a/src/new_cfg.c b/src/new_cfg.c index 8531f471f..d89bb19de 100644 --- a/src/new_cfg.c +++ b/src/new_cfg.c @@ -557,6 +557,9 @@ bool CFG_HasLoggerFlag(int flag) { int CFG_GetFlags() { return g_cfg.genericFlags; } +unsigned long CFG_GetFlags64() { + return *((unsigned long*)&g_cfg.genericFlags); +} bool CFG_HasFlag(int flag) { if (flag >= 32) { flag -= 32; diff --git a/src/new_cfg.h b/src/new_cfg.h index 2efc41485..ec1bcc12a 100644 --- a/src/new_cfg.h +++ b/src/new_cfg.h @@ -76,6 +76,7 @@ void CFG_SetLoggerFlag(int flag, bool bValue); bool CFG_HasLoggerFlag(int flag); void CFG_SetMac(char *mac); int CFG_GetFlags(); +unsigned long CFG_GetFlags64(); const char* CFG_GetNTPServer(); void CFG_SetNTPServer(const char *s); // BL0937, BL0942, etc constants diff --git a/src/new_pins.h b/src/new_pins.h index 5dad681f0..edabec0e8 100644 --- a/src/new_pins.h +++ b/src/new_pins.h @@ -1045,8 +1045,9 @@ typedef struct pinsState_s { #define OBK_FLAG_TUYAMCU_USE_QUEUE 43 #define OBK_FLAG_HTTP_DISABLE_AUTH_IN_SAFE_MODE 44 #define OBK_FLAG_DISCOVERY_DONT_MERGE_LIGHTS 45 +#define OBK_FLAG_TUYAMCU_STORE_RAW_DATA 46 -#define OBK_TOTAL_FLAGS 46 +#define OBK_TOTAL_FLAGS 47 #define LOGGER_FLAG_MQTT_DEDUPER 1 #define LOGGER_FLAG_POWER_SAVE 2 diff --git a/src/selftest/selftest_http.c b/src/selftest/selftest_http.c index 9e48bd6fd..a92831dde 100644 --- a/src/selftest/selftest_http.c +++ b/src/selftest/selftest_http.c @@ -208,6 +208,33 @@ int Test_GetJSONValue_Integer(const char *keyword, const char *obj) { printf("Test_GetJSONValue_Integer will return %i for %s\n", tmp->valueint, keyword); return tmp->valueint; } +int Test_GetJSONValue_IntFromArray(int index, const char *key) { + cJSON *tmp; + cJSON *parent; + + parent = cJSON_GetArrayItem(g_json, index); + + if (parent == 0) + return 0; + tmp = cJSON_GetObjectItemCaseSensitive(parent, key); + if (tmp == 0) + return 0; + return tmp->valueint; +} +const char *Test_GetJSONValue_StrFromArray(int index, const char *key) { + cJSON *tmp; + cJSON *parent; + + parent = cJSON_GetArrayItem(g_json, index); + + if (parent == 0) + return ""; + tmp = cJSON_GetObjectItemCaseSensitive(parent, key); + if (tmp == 0) + return ""; + return tmp->valuestring; +} + const char *Test_GetJSONValue_String(const char *keyword, const char *obj) { cJSON *tmp; tmp = Test_GetJSONValue_Generic(keyword, obj); diff --git a/src/selftest/selftest_local.h b/src/selftest/selftest_local.h index 4bf9d9dbc..64968f0ac 100644 --- a/src/selftest/selftest_local.h +++ b/src/selftest/selftest_local.h @@ -29,6 +29,9 @@ void SelfTest_Failed(const char *file, const char *function, int line, const cha #define SELFTEST_ASSERT_JSON_VALUE_INTEGER_NESTED2(par1, par2, varName, res) SELFTEST_ASSERT((Test_GetJSONValue_Integer_Nested2(par1, par2,varName) == res)); #define SELFTEST_ASSERT_JSON_VALUE_FLOAT_NESTED2(par1, par2, varName, res) SELFTEST_ASSERT((Float_Equals(Test_GetJSONValue_Float_Nested2(par1, par2,varName),res))); #define SELFTEST_ASSERT_JSON_VALUE_STRING_NESTED2(par1, par2, varName, res) SELFTEST_ASSERT((!strcmp(Test_GetJSONValue_String_Nested2(par1, par2,varName),res))); +#define SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(index, key, valInt) SELFTEST_ASSERT((Test_GetJSONValue_IntFromArray(index,key)==valInt)); +#define SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_STR(index, key, valInt) SELFTEST_ASSERT((!strcmp(Test_GetJSONValue_StrFromArray(index,key),valInt))); + #define SELFTEST_ASSERT_STRING(current,expected) SELFTEST_ASSERT((strcmp(expected,current) == 0)); #define SELFTEST_ASSERT_INTEGER(current,expected) SELFTEST_ASSERT((expected==current)); #define SELFTEST_ASSERT_HTML_REPLY(expected) SELFTEST_ASSERT((strcmp(Test_GetLastHTMLReply(),expected) == 0)); @@ -77,6 +80,7 @@ void Test_ClockEvents(); void Test_Commands_Channels(); void Test_LEDDriver(); void Test_TuyaMCU_Basic(); +void Test_TuyaMCU_RawAccess(); void Test_Command_If(); void Test_Command_If_Else(); void Test_LFS(); @@ -134,6 +138,8 @@ int Test_GetJSONValue_Integer(const char *keyword, const char *obj); const char *Test_GetJSONValue_String(const char *keyword, const char *obj); const char *Test_GetJSONValue_String_Nested(const char *par1, const char *keyword); const char *Test_GetJSONValue_String_Nested2(const char *par1, const char *par2, const char *keyword); +int Test_GetJSONValue_IntFromArray(int index, const char *obj); +const char *Test_GetJSONValue_StrFromArray(int index, const char *obj); void SIM_SendFakeMQTT(const char *text, const char *arguments); void SIM_SendFakeMQTTAndRunSimFrame_CMND(const char *command, const char *arguments); diff --git a/src/selftest/selftest_tuyaMCU.c b/src/selftest/selftest_tuyaMCU.c index 98f6fe555..60e10e823 100644 --- a/src/selftest/selftest_tuyaMCU.c +++ b/src/selftest/selftest_tuyaMCU.c @@ -3,6 +3,84 @@ #include "selftest_local.h" +void Test_TuyaMCU_RawAccess() { + // reset whole device + SIM_ClearOBK(0); + SIM_ClearAndPrepareForMQTTTesting("TuyaMCU", "bekens"); + + SIM_UART_InitReceiveRingBuffer(2048); + + CMD_ExecuteCommand("startDriver TuyaMCU", 0); + + CFG_SetFlag(OBK_FLAG_TUYAMCU_STORE_RAW_DATA, 1); + + // This will map TuyaMCU fnID 2 of type Value to channel 15 + CMD_ExecuteCommand("linkTuyaMCUOutputToChannel 2 val 15", 0); + SELFTEST_ASSERT_CHANNEL(15, 0); + // This packet sets fnID 2 of type Value to 100 + CMD_ExecuteCommand("uartFakeHex 55AA0307000802020004000000647D", 0); + + + SIM_SendFakeMQTTAndRunSimFrame_CMND("Dp", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/TuyaMCU/DP", false); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "id", 2); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "type", 0x02); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "data", 100); + + SIM_ClearMQTTHistory(); + + // This packet sets fnID 2 of type Value to 90 + CMD_ExecuteCommand("uartFakeHex 55AA03070008020200040000005A73", 0); + // above command will just put into buffer - need at least a frame to parse it + Sim_RunFrames(1000, false); + + SIM_SendFakeMQTTAndRunSimFrame_CMND("Dp", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/TuyaMCU/DP", false); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "id", 2); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "type", 0x02); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "data", 90); + + SIM_ClearMQTTHistory(); + + + // This packet sets dpID 18 of type RAW + // dpID 18 + CMD_ExecuteCommand("linkTuyaMCUOutputToChannel 18 Raw", 0); + CMD_ExecuteCommand("uartFakeHex 55AA030700101200000C0101003F030100FA040100AA25", 0); + + SIM_SendFakeMQTTAndRunSimFrame_CMND("Dp", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/TuyaMCU/DP", false); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "id", 18); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "type", 0x00); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_STR(0, "data", "0101003F030100FA040100AA"); + + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(1, "id", 2); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(1, "type", 0x02); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(1, "data", 90); + + SIM_ClearMQTTHistory(); + + // This packet sets dpID 104 of type RAW + CMD_ExecuteCommand("linkTuyaMCUOutputToChannel 104 Raw", 0); + CMD_ExecuteCommand("uartFakeHex 55AA03070008680200040000000180", 0); + + SIM_SendFakeMQTTAndRunSimFrame_CMND("Dp", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/TuyaMCU/DP", false); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "id", 104); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(0, "type", 0x00); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_STR(0, "data", "00000001"); + + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(1, "id", 18); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(1, "type", 0x00); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_STR(1, "data", "0101003F030100FA040100AA"); + + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(2, "id", 2); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(2, "type", 0x02); + SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(2, "data", 90); + + SIM_ClearMQTTHistory(); + +} void Test_TuyaMCU_Basic() { // reset whole device SIM_ClearOBK(0); diff --git a/src/win_main.c b/src/win_main.c index 76d61ad75..f5901798c 100644 --- a/src/win_main.c +++ b/src/win_main.c @@ -136,6 +136,7 @@ void Win_DoUnitTests() { Test_ChargeLimitDriver(); // this is slowest Test_TuyaMCU_Basic(); + Test_TuyaMCU_RawAccess(); Test_Battery(); Test_TuyaMCU_BatteryPowered(); Test_JSON_Lib();