Move IR from ArduinoJson to JSMN

This commit is contained in:
Stephan Hadinger 2020-09-24 08:51:43 +02:00
parent 1174479a70
commit 019e402fe7
13 changed files with 319 additions and 392 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __JSON_GENERATOR__
#define __JSON_GENERATOR__
#include <Arduino.h>
#include <string.h>
#include <stdlib.h>
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__

View File

@ -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 = "";

View File

@ -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;

View File

@ -29,7 +29,6 @@ extern struct rst_info resetInfo;
\*********************************************************************************************/
#include <Ticker.h>
#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)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/*********************************************************************************************\
* 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

View File

@ -50,6 +50,8 @@
#include <ESP8266HTTPClient.h> // Ota
#include <ESP8266httpUpdate.h> // Ota
#include <StreamString.h> // Webserver, Updater
#include <JsonParser.h>
#include <JsonGenerator.h>
#include <ArduinoJson.h> // WemoHue, IRremote, Domoticz
#ifdef USE_ARDUINO_OTA
#include <ArduinoOTA.h> // Arduino OTA

View File

@ -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;
}

View File

@ -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<x> 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 {

View File

@ -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<x> is greater than repeat, but can be overriden with JSON

View File

@ -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

View File

@ -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<JsonObject>());
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");