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");