This commit is contained in:
MichaelDvP
2022-05-02 15:42:02 +02:00
13 changed files with 107 additions and 54 deletions

View File

@@ -37,6 +37,7 @@
- Junkers ISM2 and IPM in warm water mode [#437](https://github.com/emsesp/EMS-ESP32/issues/437) - Junkers ISM2 and IPM in warm water mode [#437](https://github.com/emsesp/EMS-ESP32/issues/437)
- Added Shower Alert trigger time and cold shot time [#436](https://github.com/emsesp/EMS-ESP32/issues/436) - Added Shower Alert trigger time and cold shot time [#436](https://github.com/emsesp/EMS-ESP32/issues/436)
- Improved Table layout in Web UI (searching, filtering, sorting, exporting to CSV) - Improved Table layout in Web UI (searching, filtering, sorting, exporting to CSV)
- API fetch individual attributes from an entity [#462](https://github.com/emsesp/EMS-ESP32/issues/462)
### Fixed ### Fixed

View File

@@ -12,10 +12,10 @@
"@emotion/styled": "^11.8.1", "@emotion/styled": "^11.8.1",
"@msgpack/msgpack": "^2.7.2", "@msgpack/msgpack": "^2.7.2",
"@mui/icons-material": "^5.6.2", "@mui/icons-material": "^5.6.2",
"@mui/material": "^5.6.3", "@mui/material": "^5.6.4",
"@table-library/react-table-library": "^3.1.0", "@table-library/react-table-library": "^3.1.0",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.14.182",
"@types/node": "^17.0.30", "@types/node": "^17.0.31",
"@types/react": "^17.0.43", "@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14", "@types/react-dom": "^17.0.14",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
@@ -29,7 +29,7 @@
"react": "^17.0.2", "react": "^17.0.2",
"react-app-rewired": "^2.2.1", "react-app-rewired": "^2.2.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-dropzone": "^14.0.1", "react-dropzone": "^14.1.1",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@@ -2888,9 +2888,9 @@
} }
}, },
"node_modules/@mui/base": { "node_modules/@mui/base": {
"version": "5.0.0-alpha.78", "version": "5.0.0-alpha.79",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.78.tgz", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.79.tgz",
"integrity": "sha512-5L+GNe2M9/tFjQpjK2r837+kzRg/l6D5R9SQbG1wmSWejw5Ei8P+KXIgS/NLNi9g7dUT8bnCyzz9AZKQX1Jsfg==", "integrity": "sha512-/lZLF027BkiEjM8MIYoeS/FEhTKf+41ePU9SOijMGrCin1Y0Igucw+IHa1fF8HXD7wDbFKqHuso3J1jMG8wyNw==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@emotion/is-prop-valid": "^1.1.2", "@emotion/is-prop-valid": "^1.1.2",
@@ -2945,13 +2945,13 @@
} }
}, },
"node_modules/@mui/material": { "node_modules/@mui/material": {
"version": "5.6.3", "version": "5.6.4",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.3.tgz", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.4.tgz",
"integrity": "sha512-2VovFsbCEhic29NYoBF7zFrpH2sEOlKXXDhGjzxmWiI9OnC3SX63hapWunjaVsiRINVnjuMHuW1MOs4UtV8Gfg==", "integrity": "sha512-7TD+u/SExZK2a55w6reX56oPk37gKr/M/XGt156X+m0d9LhzOsW864nkErIX/H8oSkX/6kCimxu1FDsO+gjiVw==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@mui/base": "5.0.0-alpha.78", "@mui/base": "5.0.0-alpha.79",
"@mui/system": "^5.6.3", "@mui/system": "^5.6.4",
"@mui/types": "^7.1.3", "@mui/types": "^7.1.3",
"@mui/utils": "^5.6.1", "@mui/utils": "^5.6.1",
"@types/react-transition-group": "^4.4.4", "@types/react-transition-group": "^4.4.4",
@@ -3045,9 +3045,9 @@
} }
}, },
"node_modules/@mui/system": { "node_modules/@mui/system": {
"version": "5.6.3", "version": "5.6.4",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.6.3.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.6.4.tgz",
"integrity": "sha512-4SRi52a4ttZ2S4EHEDE8arVNuKqyQLTYUTF80WAZ0tQwnG20qwlBtzcrywCGItmVAMl7RUaYopyWOx3yVPvrmQ==", "integrity": "sha512-7rsWED1wMFMePySJobsBerFZNu7ga580QSi3Zd6sJR8nVj12qD3yIdfvxA70/PxJ/805KbIT0GX7edKI+hpyhA==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@mui/private-theming": "^5.6.2", "@mui/private-theming": "^5.6.2",
@@ -19904,9 +19904,9 @@
"integrity": "sha512-rYEi46+gIzufyYUAoHDnRzkWGxajpD9vVXFQ3g1vbjrBm6P7MBmm+s/fqPa46sxa+8FOUdEuRQKaugo5a4JWpw==" "integrity": "sha512-rYEi46+gIzufyYUAoHDnRzkWGxajpD9vVXFQ3g1vbjrBm6P7MBmm+s/fqPa46sxa+8FOUdEuRQKaugo5a4JWpw=="
}, },
"@mui/base": { "@mui/base": {
"version": "5.0.0-alpha.78", "version": "5.0.0-alpha.79",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.78.tgz", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.79.tgz",
"integrity": "sha512-5L+GNe2M9/tFjQpjK2r837+kzRg/l6D5R9SQbG1wmSWejw5Ei8P+KXIgS/NLNi9g7dUT8bnCyzz9AZKQX1Jsfg==", "integrity": "sha512-/lZLF027BkiEjM8MIYoeS/FEhTKf+41ePU9SOijMGrCin1Y0Igucw+IHa1fF8HXD7wDbFKqHuso3J1jMG8wyNw==",
"requires": { "requires": {
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@emotion/is-prop-valid": "^1.1.2", "@emotion/is-prop-valid": "^1.1.2",
@@ -19927,13 +19927,13 @@
} }
}, },
"@mui/material": { "@mui/material": {
"version": "5.6.3", "version": "5.6.4",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.3.tgz", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.4.tgz",
"integrity": "sha512-2VovFsbCEhic29NYoBF7zFrpH2sEOlKXXDhGjzxmWiI9OnC3SX63hapWunjaVsiRINVnjuMHuW1MOs4UtV8Gfg==", "integrity": "sha512-7TD+u/SExZK2a55w6reX56oPk37gKr/M/XGt156X+m0d9LhzOsW864nkErIX/H8oSkX/6kCimxu1FDsO+gjiVw==",
"requires": { "requires": {
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@mui/base": "5.0.0-alpha.78", "@mui/base": "5.0.0-alpha.79",
"@mui/system": "^5.6.3", "@mui/system": "^5.6.4",
"@mui/types": "^7.1.3", "@mui/types": "^7.1.3",
"@mui/utils": "^5.6.1", "@mui/utils": "^5.6.1",
"@types/react-transition-group": "^4.4.4", "@types/react-transition-group": "^4.4.4",
@@ -19966,9 +19966,9 @@
} }
}, },
"@mui/system": { "@mui/system": {
"version": "5.6.3", "version": "5.6.4",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.6.3.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.6.4.tgz",
"integrity": "sha512-4SRi52a4ttZ2S4EHEDE8arVNuKqyQLTYUTF80WAZ0tQwnG20qwlBtzcrywCGItmVAMl7RUaYopyWOx3yVPvrmQ==", "integrity": "sha512-7rsWED1wMFMePySJobsBerFZNu7ga580QSi3Zd6sJR8nVj12qD3yIdfvxA70/PxJ/805KbIT0GX7edKI+hpyhA==",
"requires": { "requires": {
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@mui/private-theming": "^5.6.2", "@mui/private-theming": "^5.6.2",

View File

@@ -8,10 +8,10 @@
"@emotion/styled": "^11.8.1", "@emotion/styled": "^11.8.1",
"@msgpack/msgpack": "^2.7.2", "@msgpack/msgpack": "^2.7.2",
"@mui/icons-material": "^5.6.2", "@mui/icons-material": "^5.6.2",
"@mui/material": "^5.6.3", "@mui/material": "^5.6.4",
"@table-library/react-table-library": "^3.1.0", "@table-library/react-table-library": "^3.1.0",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.14.182",
"@types/node": "^17.0.30", "@types/node": "^17.0.31",
"@types/react": "^17.0.43", "@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14", "@types/react-dom": "^17.0.14",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
@@ -25,7 +25,7 @@
"react": "^17.0.2", "react": "^17.0.2",
"react-app-rewired": "^2.2.1", "react-app-rewired": "^2.2.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-dropzone": "^14.0.1", "react-dropzone": "^14.1.1",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",

View File

@@ -38,11 +38,13 @@ const ManageUsersForm: FC = () => {
BaseRow: ` BaseRow: `
font-size: 14px; font-size: 14px;
color: white; color: white;
padding-left: 8px;
`, `,
HeaderRow: ` HeaderRow: `
text-transform: uppercase; text-transform: uppercase;
background-color: black; background-color: black;
color: #90CAF9; color: #90CAF9;
font-weight: 500;
border-bottom: 1px solid #e0e0e0; border-bottom: 1px solid #e0e0e0;
`, `,
Row: ` Row: `

View File

@@ -97,6 +97,7 @@ const DashboardStatus: FC = () => {
text-transform: uppercase; text-transform: uppercase;
background-color: black; background-color: black;
color: #90CAF9; color: #90CAF9;
font-weight: 500;
border-bottom: 1px solid #e0e0e0; border-bottom: 1px solid #e0e0e0;
`, `,
Row: ` Row: `

View File

@@ -14,7 +14,7 @@
"express": "^4.18.1", "express": "^4.18.1",
"express-sse": "^0.5.3", "express-sse": "^0.5.3",
"nodemon": "^2.0.16", "nodemon": "^2.0.16",
"ws": "^8.5.0" "ws": "^8.6.0"
} }
}, },
"node_modules/@msgpack/msgpack": { "node_modules/@msgpack/msgpack": {
@@ -1811,9 +1811,9 @@
} }
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.5.0", "version": "8.6.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
@@ -3169,9 +3169,9 @@
} }
}, },
"ws": { "ws": {
"version": "8.5.0", "version": "8.6.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
"requires": {} "requires": {}
}, },
"xdg-basedir": { "xdg-basedir": {

View File

@@ -16,6 +16,6 @@
"express": "^4.18.1", "express": "^4.18.1",
"express-sse": "^0.5.3", "express-sse": "^0.5.3",
"nodemon": "^2.0.16", "nodemon": "^2.0.16",
"ws": "^8.5.0" "ws": "^8.6.0"
} }
} }

View File

@@ -86,7 +86,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
return message(CommandRet::ERROR, "unknown device", output); return message(CommandRet::ERROR, "unknown device", output);
} }
// the next value on the path should be the command // the next value on the path should be the command or entity name
const char * command_p = nullptr; const char * command_p = nullptr;
if (num_paths == 2) { if (num_paths == 2) {
command_p = p.paths()[1].c_str(); command_p = p.paths()[1].c_str();
@@ -104,7 +104,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
} }
} }
// some commands may be prefixed with hc. or wwc. so extract these if they exist // some commands may be prefixed with hc. wwc. or hc/ or wwc/ so extract these if they exist
// parse_command_string returns the extracted command // parse_command_string returns the extracted command
command_p = parse_command_string(command_p, id_n); command_p = parse_command_string(command_p, id_n);
if (command_p == nullptr) { if (command_p == nullptr) {
@@ -189,7 +189,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
} }
// make a copy of the string command for parsing // make a copy of the string command for parsing
char command_s[100]; char command_s[30];
strlcpy(command_s, command, sizeof(command_s)); strlcpy(command_s, command, sizeof(command_s));
// look for a delimeter and split the string // look for a delimeter and split the string
@@ -202,7 +202,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
p = command_s; // reset and look for _ p = command_s; // reset and look for _
breakp = strchr(p, '_'); breakp = strchr(p, '_');
if (!breakp) { if (!breakp) {
return command; return command; // no delimeter found, return the whole string
} }
} }
} }
@@ -214,10 +214,8 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
} else if (!strncmp(command, "wwc", 3) && start_pos == 5) { } else if (!strncmp(command, "wwc", 3) && start_pos == 5) {
id = command[start_pos - 2] - '0' + 8; // wwc1 has id 9 id = command[start_pos - 2] - '0' + 8; // wwc1 has id 9
} else { } else {
#if defined(EMSESP_DEBUG) id = 0; // special case for extracting the attributes
LOG_DEBUG(F("[DEBUG] Command parse error, unknown hc/wwc in %s"), command_s); return command;
#endif
return nullptr;
} }
return (command + start_pos); return (command + start_pos);
@@ -244,13 +242,12 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
// see if there is a command registered // see if there is a command registered
auto cf = find_command(device_type, cmd); auto cf = find_command(device_type, cmd);
// check if its a call to and end-point to a device, i.e. has no value // check if its a call to and end-point to a device
// except for system commands as this is a special device without any queryable entities (device values) // except for system commands as this is a special device without any queryable entities (device values)
// exclude SYSTEM
if ((device_type > EMSdevice::DeviceType::SYSTEM) && (!value || !strlen(value))) { if ((device_type > EMSdevice::DeviceType::SYSTEM) && (!value || !strlen(value))) {
if (!cf || !cf->cmdfunction_json_) { if (!cf || !cf->cmdfunction_json_) {
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Calling %s command '%s' to retrieve values"), dname.c_str(), cmd); LOG_DEBUG(F("[DEBUG] Calling %s command '%s' to retrieve attributes"), dname.c_str(), cmd);
#endif #endif
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
} }

View File

@@ -915,13 +915,26 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
tag = DeviceValueTAG::TAG_HC1 + id - 1; tag = DeviceValueTAG::TAG_HC1 + id - 1;
} else if (id >= 9 && id <= 19) { } else if (id >= 9 && id <= 19) {
tag = DeviceValueTAG::TAG_WWC1 + id - 9; tag = DeviceValueTAG::TAG_WWC1 + id - 9;
} else if (id != -1) { }
return false; // error
// make a copy of the string command for parsing
char command_s[30];
strlcpy(command_s, cmd, sizeof(command_s));
char * attribute_s = command_s;
// if id=0 then we have a specific attribute to fetch instead of the complete record
if (id == 0) {
char * p = command_s;
char * breakp = strchr(p, '/');
if (breakp) {
*breakp = '\0';
attribute_s = breakp + 1;
}
} }
// search device value with this tag // search device value with this tag
for (auto & dv : devicevalues_) { for (auto & dv : devicevalues_) {
if (strcmp(cmd, Helpers::toLower(read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) { if (strcmp(command_s, Helpers::toLower(read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) {
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0; int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0;
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0; uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
@@ -1060,6 +1073,25 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
json[value] = "not set"; json[value] = "not set";
} }
// if id is 0 then we're filtering on an attribute, go find it
if (id == 0) {
#if defined(EMSESP_DEBUG)
EMSESP::logger().debug(F("[DEBUG] Attribute '%s'"), attribute_s);
#endif
if (json.containsKey(attribute_s)) {
JsonVariant data = json[attribute_s];
output.clear();
output["api_data"] = data;
return true;
} else {
char error[100];
snprintf(error, sizeof(error), "cannot find attribute %s in entity %s", attribute_s, command_s);
output.clear();
output["message"] = error;
return false;
}
}
return true; return true;
} }
} }

View File

@@ -660,9 +660,20 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
DynamicJsonDocument doc(2000); DynamicJsonDocument doc(2000);
JsonVariant json; JsonVariant json;
request.method(HTTP_GET); request.method(HTTP_GET);
request.url("/api/boiler/values");
EMSESP::webAPIService.webAPIService_get(&request);
request.url("/api/boiler/wwcirc"); request.url("/api/boiler/wwcirc");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService_get(&request);
request.url("/api/boiler/values"); request.url("/api/boiler/wwcirc/fullname");
EMSESP::webAPIService.webAPIService_get(&request);
request.url("/api/boiler/selburnpow/value");
EMSESP::webAPIService.webAPIService_get(&request);
request.url("/api/boiler/wwchargetype/writeable");
EMSESP::webAPIService.webAPIService_get(&request);
request.url("/api/boiler/flamecurr/value");
EMSESP::webAPIService.webAPIService_get(&request);
request.url("/api/boiler/flamecurr/bad");
EMSESP::webAPIService.webAPIService_get(&request); EMSESP::webAPIService.webAPIService_get(&request);
#endif #endif
} }

View File

@@ -31,7 +31,7 @@ namespace emsesp {
// #define EMSESP_DEBUG_DEFAULT "mixer" // #define EMSESP_DEBUG_DEFAULT "mixer"
// #define EMSESP_DEBUG_DEFAULT "web" // #define EMSESP_DEBUG_DEFAULT "web"
// #define EMSESP_DEBUG_DEFAULT "mqtt" // #define EMSESP_DEBUG_DEFAULT "mqtt"
#define EMSESP_DEBUG_DEFAULT "general" // #define EMSESP_DEBUG_DEFAULT "general"
// #define EMSESP_DEBUG_DEFAULT "boiler" // #define EMSESP_DEBUG_DEFAULT "boiler"
// #define EMSESP_DEBUG_DEFAULT "mqtt2" // #define EMSESP_DEBUG_DEFAULT "mqtt2"
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested" // #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
@@ -48,7 +48,7 @@ namespace emsesp {
// #define EMSESP_DEBUG_DEFAULT "2thermostats" // #define EMSESP_DEBUG_DEFAULT "2thermostats"
// #define EMSESP_DEBUG_DEFAULT "dallas" // #define EMSESP_DEBUG_DEFAULT "dallas"
// #define EMSESP_DEBUG_DEFAULT "analog" // #define EMSESP_DEBUG_DEFAULT "analog"
// #define EMSESP_DEBUG_DEFAULT "api_values" #define EMSESP_DEBUG_DEFAULT "api_values"
// #define EMSESP_DEBUG_DEFAULT "mqtt_post" // #define EMSESP_DEBUG_DEFAULT "mqtt_post"
class Test { class Test {

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.4.0b15idf4" #define EMSESP_APP_VERSION "3.4.0b16"

View File

@@ -120,6 +120,15 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
} }
} }
// if we're returning single values, just sent as plain text
// https://github.com/emsesp/EMS-ESP32/issues/462#issuecomment-1093877210
if (output.containsKey("api_data")) {
JsonVariant data = output["api_data"];
request->send(200, "text/plain", data.as<String>());
api_count_++;
return;
}
// send the json that came back from the command call // send the json that came back from the command call
// FAIL, OK, NOT_FOUND, ERROR, NOT_ALLOWED = 400 (bad request), 200 (OK), 400 (not found), 400 (bad request), 401 (unauthorized) // FAIL, OK, NOT_FOUND, ERROR, NOT_ALLOWED = 400 (bad request), 200 (OK), 400 (not found), 400 (bad request), 401 (unauthorized)
int ret_codes[5] = {400, 200, 400, 400, 401}; int ret_codes[5] = {400, 200, 400, 400, 401};