diff --git a/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp b/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp new file mode 100644 index 000000000..a9554a55a --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/JsonGenerator.cpp @@ -0,0 +1,185 @@ +/* + JsonGenerator.cpp - lightweight JSON parser + + Copyright (C) 2020 Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "JsonGenerator.h" + +/*********************************************************************************************\ + * JSON Generator for Arrays +\*********************************************************************************************/ +void JsonGeneratorArray::pre(void) { + // remove trailing ']' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } +} + +// void JsonGeneratorArray::post(void) { +// val += ']'; +// } + +// Add signed int (32 bits) +void JsonGeneratorArray::add(int32_t uval32) { + pre(); + val += uval32; + post(); +} + +// Add unsigned int (32 bits) +void JsonGeneratorArray::add(uint32_t uval32) { + pre(); + val += uval32; + post(); +} + +// Add a raw string, that will not be escaped. +// Can be used to add a sub-array, sub-object or 'null', 'true', 'false' values +void JsonGeneratorArray::addStrRaw(const char * sval) { + pre(); + val += sval; + post(); +} + +// Add a JSON String (will be escaped) +void JsonGeneratorArray::addStr(const char * sval) { + pre(); + val += '"'; + val += EscapeJSONString(sval).c_str(); + val += '"'; + post(); +} + +/*********************************************************************************************\ + * JSON Generator for Objects +\*********************************************************************************************/ +void JsonGeneratorObject::pre(const char * key) { + // remove trailing '}' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } + val += '"'; + val += EscapeJSONString(key); + val += '"'; + val += ':'; +} + +// void JsonGeneratorObject::post(void) { +// val += '}'; +// } + +// Add signed int (32 bits) +void JsonGeneratorObject::add(const char* key, int32_t uval32) { + pre(key); + val += uval32; + post(); +} + +// Add unsigned int (32 bits) +void JsonGeneratorObject::add(const char* key, uint32_t uval32) { + pre(key); + val += uval32; + post(); +} + +void JsonGeneratorObject::add(const char* key, const String & str) { + pre(key); + val += '"'; + val += EscapeJSONString(str.c_str()).c_str(); + val += '"'; + post(); +} + +// Add a raw string, that will not be escaped. +// Can be used to add a sub-array, sub-object or 'null', 'true', 'false' values +void JsonGeneratorObject::addStrRaw(const char* key, const char * sval) { + pre(key); + val += sval; + post(); +} + +// Add a JSON String (will be escaped) +void JsonGeneratorObject::addStr(const char* key, const char * sval) { + pre(key); + val += '"'; + val += EscapeJSONString(sval).c_str(); + val += '"'; + post(); +} + +/*********************************************************************************************\ + * JSON Generator for Arrays +\*********************************************************************************************/ +// does the character needs to be escaped, and if so with which character +static char EscapeJSONChar(char c) { + if ((c == '\"') || (c == '\\')) { + return c; + } + if (c == '\n') { return 'n'; } + if (c == '\t') { return 't'; } + if (c == '\r') { return 'r'; } + if (c == '\f') { return 'f'; } + if (c == '\b') { return 'b'; } + return 0; +} + +String EscapeJSONString(const char *str) { + // As this function is used in ResponseCmndChar() and ResponseCmndIdxChar() + // it needs to be PROGMEM safe! + String r(""); + if (nullptr == str) { return r; } + + bool needs_escape = false; + size_t len_out = 1; + const char* c = str; + char ch = '.'; + while (ch != '\0') { + ch = pgm_read_byte(c++); + if (EscapeJSONChar(ch)) { + len_out++; + needs_escape = true; + } + len_out++; + } + + if (needs_escape) { + // we need to escape some chars + // allocate target buffer + r.reserve(len_out); + c = str; + char *d = r.begin(); + char ch = '.'; + while (ch != '\0') { + ch = pgm_read_byte(c++); + char c2 = EscapeJSONChar(ch); + if (c2) { + *d++ = '\\'; + *d++ = c2; + } else { + *d++ = ch; + } + } + *d = 0; // add NULL terminator + r = (char*) r.begin(); // assign the buffer to the string + } else { + r = FPSTR(str); + } + + return r; +} \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/src/JsonGenerator.h b/lib/jsmn-shadinger-1.0/src/JsonGenerator.h new file mode 100644 index 000000000..8670aabbd --- /dev/null +++ b/lib/jsmn-shadinger-1.0/src/JsonGenerator.h @@ -0,0 +1,72 @@ +/* + JsonGenerator.h - lightweight JSON generator + + Copyright (C) 2020 Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __JSON_GENERATOR__ +#define __JSON_GENERATOR__ + +#include +#include +#include + +extern String EscapeJSONString(const char *str); + +/*********************************************************************************************\ + * JSON Generator for Arrays +\*********************************************************************************************/ +class JsonGeneratorArray { +public: + + JsonGeneratorArray(): val("[]") {} // start with empty array + + void add(uint32_t uval32); + void add(int32_t uval32); + void addStrRaw(const char * sval); + void addStr(const char * sval); + + inline String &toString(void) { return val; } + +protected: + void pre(void); + void post(void) { val += ']'; } + String val; +}; + +/*********************************************************************************************\ + * JSON Generator for Objects +\*********************************************************************************************/ +class JsonGeneratorObject { +public: + + JsonGeneratorObject(): val("{}") {} // start with empty object + + void add(const char* key, uint32_t uval32); + void add(const char* key, int32_t uval32); + void add(const char* key, const String & str); + void addStrRaw(const char* key, const char * sval); + void addStr(const char* key, const char * sval); + + inline String &toString(void) { return val; } + +protected: + void pre(const char * key); + void post(void) { val += '}'; } + String val; +}; + +#endif // __JSON_PARSER__ \ No newline at end of file diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.cpp b/lib/jsmn-shadinger-1.0/src/JsonParser.cpp index ab2a905f4..04ff03fe8 100644 --- a/lib/jsmn-shadinger-1.0/src/JsonParser.cpp +++ b/lib/jsmn-shadinger-1.0/src/JsonParser.cpp @@ -1,5 +1,5 @@ /* - JsonParser.h - lightweight JSON parser + JsonParser.cpp - lightweight JSON parser Copyright (C) 2020 Stephan Hadinger @@ -366,6 +366,9 @@ float JsonParserObject::getFloat(const char * needle, float val) const { const char * JsonParserObject::getStr(const char * needle, const char * val) const { return (*this)[needle].getStr(val); } +const char * JsonParserObject::getStr(const char * needle) const { + return getStr(needle, ""); +} void JsonParser::parse(char * json_in) { k_current_json_buffer = ""; diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.h b/lib/jsmn-shadinger-1.0/src/JsonParser.h index 42886d628..a59227ec4 100644 --- a/lib/jsmn-shadinger-1.0/src/JsonParser.h +++ b/lib/jsmn-shadinger-1.0/src/JsonParser.h @@ -67,6 +67,7 @@ public: JsonParserToken(const jsmntok_t * token) : t(token) { if (nullptr == t) { t = &token_bad; } } + JsonParserToken() : t(&token_bad) { } // no explicit destructor (not needed) inline bool isValid(void) const { return t->type != JSMN_INVALID; } @@ -160,6 +161,7 @@ public: uint64_t getULong(const char *, uint64_t) const; float getFloat(const char *, float) const; const char * getStr(const char *, const char *) const; + const char * getStr(const char *) const; // get first element (key) JsonParserKey getFirstElement(void) const; diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch b/lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch deleted file mode 100644 index e3d9ed686..000000000 Binary files a/lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch and /dev/null differ diff --git a/tasmota/support.ino b/tasmota/support.ino index 23b8780fa..0751b2b95 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -29,7 +29,6 @@ extern struct rst_info resetInfo; \*********************************************************************************************/ #include -#include "JsonParser.h" Ticker tickerOSWatch; @@ -1427,48 +1426,6 @@ bool GetUsedInModule(uint32_t val, uint16_t *arr) bool JsonTemplate(char* dataBuf) { -#if 0 - // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} - - if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks - -#ifdef ESP8266 - StaticJsonBuffer<400> jb; // 331 from https://arduinojson.org/v5/assistant/ -#else - StaticJsonBuffer<999> jb; // 654 from https://arduinojson.org/v5/assistant/ -#endif - JsonObject& obj = jb.parseObject(dataBuf); - if (!obj.success()) { return false; } - - // All parameters are optional allowing for partial changes - const char* name = obj[D_JSON_NAME]; - if (name != nullptr) { - SettingsUpdateText(SET_TEMPLATE_NAME, name); - } - if (obj[D_JSON_GPIO].success()) { - for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { -#ifdef ESP8266 - Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; -#else // ESP32 - uint16_t gpio = obj[D_JSON_GPIO][i] | 0; - if (gpio == (AGPIO(GPIO_NONE) +1)) { - gpio = AGPIO(GPIO_USER); - } - Settings.user_template.gp.io[i] = gpio; -#endif - } - } - if (obj[D_JSON_FLAG].success()) { - uint32_t flag = obj[D_JSON_FLAG] | 0; - memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); - } - if (obj[D_JSON_BASE].success()) { - uint32_t base = obj[D_JSON_BASE]; - if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } - Settings.user_template_base = base -1; // Default WEMOS - } - return true; -#else // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks @@ -1508,7 +1465,6 @@ bool JsonTemplate(char* dataBuf) Settings.user_template_base = base -1; // Default WEMOS } return true; -#endif } void TemplateJson(void) diff --git a/tasmota/support_json.ino b/tasmota/support_json.ino deleted file mode 100644 index f1b2d1fd2..000000000 --- a/tasmota/support_json.ino +++ /dev/null @@ -1,117 +0,0 @@ -/* - support_json.ino - JSON support functions - - Copyright (C) 2020 Theo Arends and Stephan Hadinger - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/*********************************************************************************************\ - * JSON parsing -\*********************************************************************************************/ - -// does the character needs to be escaped, and if so with which character -char EscapeJSONChar(char c) { - if ((c == '\"') || (c == '\\')) { - return c; - } - if (c == '\n') { return 'n'; } - if (c == '\t') { return 't'; } - if (c == '\r') { return 'r'; } - if (c == '\f') { return 'f'; } - if (c == '\b') { return 'b'; } - return 0; -} - -String EscapeJSONString(const char *str) { - // As this function is used in ResponseCmndChar() and ResponseCmndIdxChar() - // it needs to be PROGMEM safe! - String r(""); - if (nullptr == str) { return r; } - - bool needs_escape = false; - size_t len_out = 1; - const char* c = str; - char ch = '.'; - while (ch != '\0') { - ch = pgm_read_byte(c++); - if (EscapeJSONChar(ch)) { - len_out++; - needs_escape = true; - } - len_out++; - } - - if (needs_escape) { - // we need to escape some chars - // allocate target buffer - r.reserve(len_out); - c = str; - char *d = r.begin(); - char ch = '.'; - while (ch != '\0') { - ch = pgm_read_byte(c++); - char c2 = EscapeJSONChar(ch); - if (c2) { - *d++ = '\\'; - *d++ = c2; - } else { - *d++ = ch; - } - } - *d = 0; // add NULL terminator - r = (char*) r.begin(); // assign the buffer to the string - } else { - r = FPSTR(str); - } - - return r; -} - -/*********************************************************************************************\ - * Find key - case insensitive -\*********************************************************************************************/ - -// Given a JsonObject, finds the value as JsonVariant for the key needle. -// The search is case-insensitive, and will find the first match in the order of keys in JSON -// -// If the key is not found, returns a nullptr -// Input: needle cannot be NULL but may be PROGMEM -#if 0 -const JsonVariant &GetCaseInsensitive(const JsonObject &json, const char *needle) { - // key can be in PROGMEM - // if needle == "?" then we return the first valid key - bool wildcard = strcmp_P("?", needle) == 0; - if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle)) || (!json.success())) { - return *(JsonVariant*)nullptr; - } - - for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) { - const char *key = it->key; - const JsonVariant &value = it->value; - - if (wildcard || (0 == strcasecmp_P(key, needle))) { - return value; - } - } - // if not found - return *(JsonVariant*)nullptr; -} - -// This function returns true if the JsonObject contains the specified key -// It's just a wrapper to the previous function but it can be tricky to test nullptr on an object ref -bool HasKeyCaseInsensitive(const JsonObject &json, const char *needle) { - return &GetCaseInsensitive(json, needle) != nullptr; -} -#endif \ No newline at end of file diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index b8d3293da..2ae32bfe6 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -50,6 +50,8 @@ #include // Ota #include // Ota #include // Webserver, Updater +#include +#include #include // WemoHue, IRremote, Domoticz #ifdef USE_ARDUINO_OTA #include // Arduino OTA diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index f1aff262b..48abbb490 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -3344,26 +3344,6 @@ bool JsonWebColor(const char* dataBuf) // Default pre v7 (Light theme) // {"WebColor":["#000","#fff","#f2f2f2","#000","#fff","#000","#fff","#f00","#008000","#fff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#fff","#999","#000"]} // {"WebColor":["#000000","#ffffff","#f2f2f2","#000000","#ffffff","#000000","#ffffff","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#000000"]} -#if 0 - char dataBufLc[strlen(dataBuf) +1]; - LowerCase(dataBufLc, dataBuf); - RemoveSpace(dataBufLc); - if (strlen(dataBufLc) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks - - StaticJsonBuffer<450> jb; // 421 from https://arduinojson.org/v5/assistant/ - JsonObject& obj = jb.parseObject(dataBufLc); - if (!obj.success()) { return false; } - - char parm_lc[10]; - if (obj[LowerCase(parm_lc, D_CMND_WEBCOLOR)].success()) { - for (uint32_t i = 0; i < COL_LAST; i++) { - const char* color = obj[parm_lc][i]; - if (color != nullptr) { - WebHexCode(i, color); - } - } - } -#else JsonParser parser((char*) dataBuf); JsonParserObject root = parser.getRootObject(); JsonParserArray arr = root[PSTR(D_CMND_WEBCOLOR)].getArray(); @@ -3378,7 +3358,6 @@ bool JsonWebColor(const char* dataBuf) i++; } } -#endif return true; } diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino index 0c96e7d2a..690dc0c61 100644 --- a/tasmota/xdrv_05_irremote.ino +++ b/tasmota/xdrv_05_irremote.ino @@ -176,32 +176,9 @@ void IrReceiveCheck(void) uint32_t IrRemoteCmndIrSendJson(void) { - // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } -#if 0 - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - return IE_INVALID_JSON; - } - - StaticJsonBuffer<140> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - return IE_INVALID_JSON; - } - - // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } - char parm_uc[10]; - const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; - uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; - uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); - uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; -#else RemoveSpace(XdrvMailbox.data); // TODO is this really needed? JsonParser parser(XdrvMailbox.data); JsonParserObject root = parser.getRootObject(); @@ -213,7 +190,7 @@ uint32_t IrRemoteCmndIrSendJson(void) uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); uint64_t data = root.getULong(PSTR(D_JSON_IR_DATA), 0); uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); -#endif + // check if the IRSend is great than repeat if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; @@ -257,7 +234,6 @@ void CmndIrSend(void) uint8_t error = IE_SYNTAX_IRSEND; if (XdrvMailbox.data_len) { -// error = (strstr(XdrvMailbox.data, "{") == nullptr) ? IrRemoteCmndIrSendRaw() : IrRemoteCmndIrSendJson(); if (strstr(XdrvMailbox.data, "{") == nullptr) { error = IE_INVALID_JSON; } else { diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino index 275112bab..0b5f09ca3 100644 --- a/tasmota/xdrv_05_irremote_full.ino +++ b/tasmota/xdrv_05_irremote_full.ino @@ -111,38 +111,39 @@ void IrReceiveInit(void) } String sendACJsonState(const stdAc::state_t &state) { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); - json[D_JSON_IRHVAC_MODEL] = state.model; - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); + JsonGeneratorObject json; + json.add(PSTR(D_JSON_IRHVAC_VENDOR), typeToString(state.protocol)); + json.add(PSTR(D_JSON_IRHVAC_MODEL), state.model); + // Home Assistant wants mode to be off if power is also off & vice-versa. if (state.mode == stdAc::opmode_t::kOff || !state.power) { - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); - } - json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); - if (floorf(state.degrees) == state.degrees) { - json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); // integer + json.add(PSTR(D_JSON_IRHVAC_MODE), IRac::opmodeToString(stdAc::opmode_t::kOff)); + json.add(PSTR(D_JSON_IRHVAC_POWER), IRac::boolToString(false)); } else { - json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); // non-integer, limit to only 1 sub-digit + json.add(PSTR(D_JSON_IRHVAC_MODE), IRac::opmodeToString(state.mode)); + json.add(PSTR(D_JSON_IRHVAC_POWER), IRac::boolToString(state.power)); + } + json.add(PSTR(D_JSON_IRHVAC_CELSIUS), IRac::boolToString(state.celsius)); + if (floorf(state.degrees) == state.degrees) { + json.add(PSTR(D_JSON_IRHVAC_TEMP), (int32_t) floorf(state.degrees)); // integer + } else { + // TODO can do better here + json.addStrRaw(PSTR(D_JSON_IRHVAC_TEMP), String(state.degrees, 1).c_str()); // non-integer, limit to only 1 sub-digit } - json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); - json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); - json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); - json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); - json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); - json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); - json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); - json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); - json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); - json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); - json[D_JSON_IRHVAC_SLEEP] = state.sleep; - String payload = ""; - payload.reserve(200); - json.printTo(payload); + json.add(PSTR(D_JSON_IRHVAC_FANSPEED), IRac::fanspeedToString(state.fanspeed)); + json.add(PSTR(D_JSON_IRHVAC_SWINGV), IRac::swingvToString(state.swingv)); + json.add(PSTR(D_JSON_IRHVAC_SWINGH), IRac::swinghToString(state.swingh)); + json.add(PSTR(D_JSON_IRHVAC_QUIET), IRac::boolToString(state.quiet)); + json.add(PSTR(D_JSON_IRHVAC_TURBO), IRac::boolToString(state.turbo)); + json.add(PSTR(D_JSON_IRHVAC_ECONO), IRac::boolToString(state.econo)); + json.add(PSTR(D_JSON_IRHVAC_LIGHT), IRac::boolToString(state.light)); + json.add(PSTR(D_JSON_IRHVAC_FILTER), IRac::boolToString(state.filter)); + json.add(PSTR(D_JSON_IRHVAC_CLEAN), IRac::boolToString(state.clean)); + json.add(PSTR(D_JSON_IRHVAC_BEEP), IRac::boolToString(state.beep)); + json.add(PSTR(D_JSON_IRHVAC_SLEEP), state.sleep); + + String payload = json.toString(); // copy string before returning, the original is on the stack return payload; } @@ -311,12 +312,9 @@ uint32_t IrRemoteCmndIrHvacJson(void) state.clean = false; // Turn off any Cleaning options if we can. state.clock = -1; // Don't set any current time if we can avoid it. - if (root[PSTR(D_JSON_IRHVAC_VENDOR)]) { state.protocol = strToDecodeType(root.getStr(PSTR(D_JSON_IRHVAC_VENDOR), "")); } - if (root[PSTR(D_JSON_IRHVAC_PROTOCOL)]) { state.protocol = strToDecodeType(root.getStr(PSTR(D_JSON_IRHVAC_PROTOCOL), "")); } - // UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - // if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } - // UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - // if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } // also support 'protocol' + JsonParserToken val; + if (val = root[PSTR(D_JSON_IRHVAC_VENDOR)]) { state.protocol = strToDecodeType(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_PROTOCOL)]) { state.protocol = strToDecodeType(val.getStr()); } if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } @@ -331,10 +329,10 @@ uint32_t IrRemoteCmndIrHvacJson(void) } } - if (root[PSTR(D_JSON_IRHVAC_MODEL)]) { state.model = IRac::strToModel(PSTR(D_JSON_IRHVAC_MODEL)); } - if (root[PSTR(D_JSON_IRHVAC_MODE)]) { state.mode = IRac::strToOpmode(PSTR(D_JSON_IRHVAC_MODE)); } - if (root[PSTR(D_JSON_IRHVAC_SWINGV)]) { state.swingv = IRac::strToSwingV(PSTR(D_JSON_IRHVAC_SWINGV)); } - if (root[PSTR(D_JSON_IRHVAC_SWINGH)]) { state.swingh = IRac::strToSwingH(PSTR(D_JSON_IRHVAC_SWINGH)); } + if (val = root[PSTR(D_JSON_IRHVAC_MODEL)]) { state.model = IRac::strToModel(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_MODE)]) { state.mode = IRac::strToOpmode(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_SWINGV)]) { state.swingv = IRac::strToSwingV(val.getStr()); } + if (val = root[PSTR(D_JSON_IRHVAC_SWINGH)]) { state.swingh = IRac::strToSwingH(val.getStr()); } state.degrees = root.getFloat(PSTR(D_JSON_IRHVAC_TEMP), state.degrees); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("model %d, mode %d, fanspeed %d, swingv %d, swingh %d"), // state.model, state.mode, state.fanspeed, state.swingv, state.swingh); @@ -378,40 +376,32 @@ void CmndIrHvac(void) uint32_t IrRemoteCmndIrSendJson(void) { - char parm_uc[12]; // used to convert JSON keys to uppercase - // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } + RemoveSpace(XdrvMailbox.data); // TODO is this really needed? + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) { return IE_INVALID_JSON; } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } - decode_type_t protocol = decode_type_t::UNKNOWN; - uint16_t bits = 0; - uint64_t data; - uint8_t repeat = 0; + JsonParserToken value; - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } // also support 'protocol' + decode_type_t protocol = decode_type_t::UNKNOWN; + value = root[PSTR(D_JSON_IRHVAC_VENDOR)]; + if (root) { protocol = strToDecodeType(value.getStr()); } + value = root[PSTR(D_JSON_IRHVAC_PROTOCOL)]; + if (root) { protocol = strToDecodeType(value.getStr()); } if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); - if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); - if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); // accept LSB values - if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); // or classical MSB (takes priority) - if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } + uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); + uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); + + uint64_t data; + value = root[PSTR(D_JSON_IR_DATALSB)]; + if (root) { data = reverseBitsInBytes64(value.getULong()); } // accept LSB values + value = root[PSTR(D_JSON_IR_DATA)]; + if (value) { data = value.getULong(); } // or classical MSB (takes priority) if (0 == bits) { return IE_SYNTAX_IRSEND; } // check if the IRSend is greater than repeat, but can be overriden with JSON diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index b9d6d57c7..35503836f 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -337,90 +337,6 @@ void CmndTimer(void) Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; // Copy timer } } else { -#if 0 -//#ifndef USE_RULES -#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 - if (devices_present) { -#endif - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<256> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(dataBufUc); - if (!root.success()) { - Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed - error = 1; - } - else { - char parm_uc[10]; - index--; - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { - Settings.timer[index].arm = (root[parm_uc] != 0); - } -#ifdef USE_SUNRISE - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_MODE))].success()) { - Settings.timer[index].mode = (uint8_t)root[parm_uc] & 0x03; - } -#endif - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { - uint16_t itime = 0; - int8_t value = 0; - uint8_t sign = 0; - char time_str[10]; - - strlcpy(time_str, root[parm_uc], sizeof(time_str)); - const char *substr = strtok(time_str, ":"); - if (substr != nullptr) { - if (strchr(substr, '-')) { - sign = 1; - substr++; - } - value = atoi(substr); - if (sign) { value += 12; } // Allow entering timer offset from -11:59 to -00:01 converted to 12:01 to 23:59 - if (value > 23) { value = 23; } - itime = value * 60; - substr = strtok(nullptr, ":"); - if (substr != nullptr) { - value = atoi(substr); - if (value < 0) { value = 0; } - if (value > 59) { value = 59; } - itime += value; - } - } - Settings.timer[index].time = itime; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_WINDOW))].success()) { - Settings.timer[index].window = (uint8_t)root[parm_uc] & 0x0F; - TimerSetRandomWindow(index); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { - // SMTWTFS = 1234567 = 0011001 = 00TW00S = --TW--S - Settings.timer[index].days = 0; - const char *tday = root[parm_uc]; - uint8_t i = 0; - char ch = *tday++; - while ((ch != '\0') && (i < 7)) { - if (ch == '-') { ch = '0'; } - uint8_t mask = 1 << i++; - Settings.timer[index].days |= (ch == '0') ? 0 : mask; - ch = *tday++; - } - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { - Settings.timer[index].repeat = (root[parm_uc] != 0); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_OUTPUT))].success()) { - uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; - Settings.timer[index].device = (device < devices_present) ? device : 0; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ACTION))].success()) { - uint8_t action = (uint8_t)root[parm_uc] & 0x03; - Settings.timer[index].power = (devices_present) ? action : 3; // If no devices than only allow rules - } - - index++; - } -//#ifndef USE_RULES -#else //#ifndef USE_RULES #if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 if (devices_present) { @@ -509,7 +425,6 @@ void CmndTimer(void) index++; } //#ifndef USE_RULES -#endif #if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 } else { Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); // No outputs defined so nothing to control diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 7377585c5..2938e2d41 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -497,40 +497,6 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) rule_name = rule_name.substring(0, pos); // "SUBTYPE1#CURRENT" } -#if 0 -// StaticJsonBuffer<1280> jsonBuf; // Was 1024 until 20200811 - DynamicJsonBuffer jsonBuf; // Was static until 20200812 - JsonObject &root = jsonBuf.parseObject(event); - if (!root.success()) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event too long (%d)"), event.length()); - return false; - } // No valid JSON data - JsonObject *obj = &root; - String subtype; - uint32_t i = 0; - while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT" - subtype = rule_name.substring(0, pos); - const JsonVariant & val = GetCaseInsensitive(*obj, subtype.c_str()); - if (nullptr == &val) { return false; } // not found - obj = &(val.as()); - if (!obj->success()) { return false; } // not a JsonObject - - rule_name = rule_name.substring(pos +1); - if (i++ > 10) { return false; } // Abandon possible loop - - yield(); - } - - const JsonVariant & val = GetCaseInsensitive(*obj, rule_name.c_str()); - if (nullptr == &val) { return false; } // last level not found - const char* str_value; - if (rule_name_idx) { - str_value = (*obj)[rule_name][rule_name_idx -1]; // "CURRENT[1]" - } else { - str_value = (*obj)[rule_name]; // "CURRENT" - } -#else - String buf = event; // copy the string into a new buffer that will be modified JsonParser parser((char*)buf.c_str()); JsonParserObject obj = parser.getRootObject(); @@ -563,8 +529,6 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) } else { str_value = val.getStr(); // "CURRENT" } -#endif - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), // rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");