mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
commands take a set of flags, like NEED_ADMIN or HIDDEN
This commit is contained in:
@@ -28,57 +28,66 @@ std::vector<Command::CmdFunction> Command::cmdfunctions_;
|
|||||||
|
|
||||||
// calls a command
|
// calls a command
|
||||||
// id may be used to represent a heating circuit for example, it's optional
|
// id may be used to represent a heating circuit for example, it's optional
|
||||||
// returns false if error or not found
|
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
||||||
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
|
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id) {
|
||||||
int8_t id_new = id;
|
int8_t id_new = id;
|
||||||
char cmd_new[20] = {'\0'};
|
char cmd_new[20] = {'\0'};
|
||||||
strlcpy(cmd_new, cmd, 20);
|
strlcpy(cmd_new, cmd, 20);
|
||||||
|
|
||||||
|
// find the command
|
||||||
auto cf = find_command(device_type, cmd_new, id_new);
|
auto cf = find_command(device_type, cmd_new, id_new);
|
||||||
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
|
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
|
||||||
LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||||
return false; // command not found, or requires a json
|
return 2; // command not found
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we're allowed to call it
|
||||||
|
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) {
|
||||||
|
LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||||
|
return 4; // command not allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EMSESP_DEBUG
|
|
||||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s'"), dname.c_str(), cmd);
|
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
|
||||||
} else if (id == -1) {
|
} else if (id == -1) {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return ((cf->cmdfunction_)(value, id_new));
|
return ((cf->cmdfunction_)(value, id_new));
|
||||||
}
|
}
|
||||||
|
|
||||||
// calls a command. Takes a json object for output.
|
// calls a command. Takes a json object for output.
|
||||||
// id may be used to represent a heating circuit for example
|
// id may be used to represent a heating circuit for example
|
||||||
// returns false if error or not found
|
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
||||||
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) {
|
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json) {
|
||||||
int8_t id_new = id;
|
int8_t id_new = id;
|
||||||
char cmd_new[20] = {'\0'};
|
char cmd_new[20] = {'\0'};
|
||||||
strlcpy(cmd_new, cmd, 20);
|
strlcpy(cmd_new, cmd, 20);
|
||||||
|
|
||||||
auto cf = find_command(device_type, cmd_new, id_new);
|
auto cf = find_command(device_type, cmd_new, id_new);
|
||||||
|
|
||||||
#ifdef EMSESP_DEBUG
|
// check if we're allowed to call it
|
||||||
|
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) {
|
||||||
|
LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||||
|
return 4; // command not allowed
|
||||||
|
}
|
||||||
|
|
||||||
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
std::string dname = EMSdevice::device_type_2_device_name(device_type);
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s'"), dname.c_str(), cmd);
|
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
|
||||||
} else if (id == -1) {
|
} else if (id == -1) {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// check if json object is empty, if so quit
|
// check if json object is empty, if so quit
|
||||||
if (json.isNull()) {
|
if (json.isNull()) {
|
||||||
LOG_WARNING(F("Ignore call for command %s in %s because no json"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
LOG_WARNING(F("Ignore call for command %s in %s because it has no json body"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||||
return false;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress)
|
// this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress)
|
||||||
@@ -98,20 +107,24 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
|
|||||||
|
|
||||||
// strip prefixes, check, and find command
|
// strip prefixes, check, and find command
|
||||||
Command::CmdFunction * Command::find_command(const uint8_t device_type, char * cmd, int8_t & id) {
|
Command::CmdFunction * Command::find_command(const uint8_t device_type, char * cmd, int8_t & id) {
|
||||||
|
// TODO special cases for id=0 and id=-1 will be removed in V3 API
|
||||||
// no command for id0
|
// no command for id0
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty command is info with id0
|
// empty command is info with id0
|
||||||
if (cmd[0] == '\0') {
|
if (cmd[0] == '\0') {
|
||||||
strcpy(cmd, "info");
|
strcpy(cmd, "info");
|
||||||
id = 0;
|
id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert cmd to lowercase
|
// convert cmd to lowercase
|
||||||
for (char * p = cmd; *p; p++) {
|
for (char * p = cmd; *p; p++) {
|
||||||
*p = tolower(*p);
|
*p = tolower(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO hack for commands that could have hc or wwc prefixed. will be removed in new API V3 eventually
|
||||||
// scan for prefix hc.
|
// scan for prefix hc.
|
||||||
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
|
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
|
||||||
const char * tag = EMSdevice::tag_to_string(i).c_str();
|
const char * tag = EMSdevice::tag_to_string(i).c_str();
|
||||||
@@ -151,33 +164,40 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, char * c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add a command to the list, which does not return json
|
// add a command to the list, which does not return json
|
||||||
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flag) {
|
// these commands are not callable directly via MQTT subscriptions either
|
||||||
|
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flags) {
|
||||||
// if the command already exists for that device type don't add it
|
// if the command already exists for that device type don't add it
|
||||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the description is empty, it's hidden which means it will not show up in Web or Console as an available command
|
// if the description is empty, it's hidden which means it will not show up in Web API or Console as an available command
|
||||||
bool hidden = (description == nullptr);
|
// TODO check whether we still need this piece of code
|
||||||
|
if (description == nullptr) {
|
||||||
|
flags |= CommandFlag::HIDDEN;
|
||||||
|
}
|
||||||
|
|
||||||
cmdfunctions_.emplace_back(device_type, flag, cmd, cb, nullptr, description, hidden); // callback for json is nullptr
|
cmdfunctions_.emplace_back(device_type, flags, cmd, cb, nullptr, description); // callback for json is nullptr
|
||||||
|
|
||||||
// see if we need to subscribe
|
// see if we need to subscribe
|
||||||
if (Mqtt::enabled()) {
|
if (Mqtt::enabled()) {
|
||||||
Mqtt::register_command(device_type, cmd, cb, flag);
|
Mqtt::register_command(device_type, cmd, cb, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a command to the list, which does return json object as output
|
// add a command to the list, which does return a json object as output
|
||||||
// flag is fixed
|
// flag is fixed to MqttSubFlag::FLAG_NOSUB
|
||||||
// optional parameter hidden for commands that will not show up on the Console
|
void Command::add_returns_json(const uint8_t device_type,
|
||||||
void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, bool hidden) {
|
const __FlashStringHelper * cmd,
|
||||||
|
cmdfunction_json_p cb,
|
||||||
|
const __FlashStringHelper * description,
|
||||||
|
uint8_t flags) {
|
||||||
// if the command already exists for that device type don't add it
|
// if the command already exists for that device type don't add it
|
||||||
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdfunctions_.emplace_back(device_type, MqttSubFlag::FLAG_NOSUB, cmd, nullptr, cb, description, hidden); // callback for json is included
|
cmdfunctions_.emplace_back(device_type, CommandFlag::MQTT_SUB_FLAG_NOSUB | flags, cmd, nullptr, cb, description); // callback for json is included
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if a command exists for that device type
|
// see if a command exists for that device type
|
||||||
@@ -213,7 +233,7 @@ bool Command::list(const uint8_t device_type, JsonObject & json) {
|
|||||||
// create a list of commands, sort them
|
// create a list of commands, sort them
|
||||||
std::list<std::string> sorted_cmds;
|
std::list<std::string> sorted_cmds;
|
||||||
for (const auto & cf : cmdfunctions_) {
|
for (const auto & cf : cmdfunctions_) {
|
||||||
if ((cf.device_type_ == device_type) && !cf.hidden_) {
|
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
|
||||||
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,7 +241,7 @@ bool Command::list(const uint8_t device_type, JsonObject & json) {
|
|||||||
|
|
||||||
for (auto & cl : sorted_cmds) {
|
for (auto & cl : sorted_cmds) {
|
||||||
for (const auto & cf : cmdfunctions_) {
|
for (const auto & cf : cmdfunctions_) {
|
||||||
if ((cf.device_type_ == device_type) && !cf.hidden_ && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
||||||
json[cl] = cf.description_;
|
json[cl] = cf.description_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,7 +260,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
|||||||
// create a list of commands, sort them
|
// create a list of commands, sort them
|
||||||
std::list<std::string> sorted_cmds;
|
std::list<std::string> sorted_cmds;
|
||||||
for (const auto & cf : cmdfunctions_) {
|
for (const auto & cf : cmdfunctions_) {
|
||||||
if ((cf.device_type_ == device_type) && !cf.hidden_) {
|
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
|
||||||
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,13 +281,13 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
|||||||
for (auto & cl : sorted_cmds) {
|
for (auto & cl : sorted_cmds) {
|
||||||
// find and print the description
|
// find and print the description
|
||||||
for (const auto & cf : cmdfunctions_) {
|
for (const auto & cf : cmdfunctions_) {
|
||||||
if ((cf.device_type_ == device_type) && !cf.hidden_ && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) {
|
||||||
uint8_t i = cl.length();
|
uint8_t i = cl.length();
|
||||||
shell.print(" ");
|
shell.print(" ");
|
||||||
if (cf.flag_ == FLAG_HC) {
|
if (cf.has_flags(MQTT_SUB_FLAG_HC)) {
|
||||||
shell.print("[hc] ");
|
shell.print("[hc] ");
|
||||||
i += 5;
|
i += 5;
|
||||||
} else if (cf.flag_ == FLAG_WWC) {
|
} else if (cf.has_flags(MQTT_SUB_FLAG_WWC)) {
|
||||||
shell.print("[wwc] ");
|
shell.print("[wwc] ");
|
||||||
i += 6;
|
i += 6;
|
||||||
}
|
}
|
||||||
@@ -278,6 +298,11 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
|||||||
}
|
}
|
||||||
shell.print(COLOR_BRIGHT_CYAN);
|
shell.print(COLOR_BRIGHT_CYAN);
|
||||||
shell.print(uuid::read_flash_string(cf.description_));
|
shell.print(uuid::read_flash_string(cf.description_));
|
||||||
|
if (cf.has_flags(CommandFlag::ADMIN_ONLY)) {
|
||||||
|
shell.print(' ');
|
||||||
|
shell.print(COLOR_BRIGHT_RED);
|
||||||
|
shell.print('*');
|
||||||
|
}
|
||||||
shell.print(COLOR_RESET);
|
shell.print(COLOR_RESET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,7 +362,7 @@ void Command::show_devices(uuid::console::Shell & shell) {
|
|||||||
// output list of all commands to console
|
// output list of all commands to console
|
||||||
// calls show with verbose mode set
|
// calls show with verbose mode set
|
||||||
void Command::show_all(uuid::console::Shell & shell) {
|
void Command::show_all(uuid::console::Shell & shell) {
|
||||||
shell.println(F("Available commands per device: "));
|
shell.println(F("Available commands: "));
|
||||||
|
|
||||||
// show system first
|
// show system first
|
||||||
shell.print(COLOR_BOLD_ON);
|
shell.print(COLOR_BOLD_ON);
|
||||||
|
|||||||
@@ -34,6 +34,17 @@ using uuid::console::Shell;
|
|||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
|
// mqtt flags for command subscriptions
|
||||||
|
enum CommandFlag : uint8_t {
|
||||||
|
MQTT_SUB_FLAG_NORMAL = 0, // 0
|
||||||
|
MQTT_SUB_FLAG_HC = (1 << 0), // 1
|
||||||
|
MQTT_SUB_FLAG_WWC = (1 << 1), // 2
|
||||||
|
MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
|
||||||
|
HIDDEN = (1 << 3), // 8
|
||||||
|
ADMIN_ONLY = (1 << 4) // 16
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
using cmdfunction_p = std::function<bool(const char * data, const int8_t id)>;
|
using cmdfunction_p = std::function<bool(const char * data, const int8_t id)>;
|
||||||
using cmdfunction_json_p = std::function<bool(const char * data, const int8_t id, JsonObject & json)>;
|
using cmdfunction_json_p = std::function<bool(const char * data, const int8_t id, JsonObject & json)>;
|
||||||
|
|
||||||
@@ -41,27 +52,37 @@ class Command {
|
|||||||
public:
|
public:
|
||||||
struct CmdFunction {
|
struct CmdFunction {
|
||||||
uint8_t device_type_; // DeviceType::
|
uint8_t device_type_; // DeviceType::
|
||||||
uint8_t flag_; // mqtt flags for command subscriptions
|
uint8_t flags_; // mqtt flags for command subscriptions
|
||||||
const __FlashStringHelper * cmd_;
|
const __FlashStringHelper * cmd_;
|
||||||
cmdfunction_p cmdfunction_;
|
cmdfunction_p cmdfunction_;
|
||||||
cmdfunction_json_p cmdfunction_json_;
|
cmdfunction_json_p cmdfunction_json_;
|
||||||
const __FlashStringHelper * description_;
|
const __FlashStringHelper * description_;
|
||||||
bool hidden_; // if its command not to be shown on the Console
|
|
||||||
|
|
||||||
CmdFunction(const uint8_t device_type,
|
CmdFunction(const uint8_t device_type,
|
||||||
const uint8_t flag,
|
const uint8_t flags,
|
||||||
const __FlashStringHelper * cmd,
|
const __FlashStringHelper * cmd,
|
||||||
cmdfunction_p cmdfunction,
|
cmdfunction_p cmdfunction,
|
||||||
cmdfunction_json_p cmdfunction_json,
|
cmdfunction_json_p cmdfunction_json,
|
||||||
const __FlashStringHelper * description,
|
const __FlashStringHelper * description)
|
||||||
bool hidden = false)
|
|
||||||
: device_type_(device_type)
|
: device_type_(device_type)
|
||||||
, flag_(flag)
|
, flags_(flags)
|
||||||
, cmd_(cmd)
|
, cmd_(cmd)
|
||||||
, cmdfunction_(cmdfunction)
|
, cmdfunction_(cmdfunction)
|
||||||
, cmdfunction_json_(cmdfunction_json)
|
, cmdfunction_json_(cmdfunction_json)
|
||||||
, description_(description)
|
, description_(description) {
|
||||||
, hidden_(hidden) {
|
}
|
||||||
|
|
||||||
|
inline void add_flags(uint8_t flags) {
|
||||||
|
flags_ |= flags;
|
||||||
|
}
|
||||||
|
inline bool has_flags(uint8_t flags) const {
|
||||||
|
return (flags_ & flags) == flags;
|
||||||
|
}
|
||||||
|
inline void remove_flags(uint8_t flags) {
|
||||||
|
flags_ &= ~flags;
|
||||||
|
}
|
||||||
|
inline uint8_t flags() const {
|
||||||
|
return flags_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -69,11 +90,21 @@ class Command {
|
|||||||
return cmdfunctions_;
|
return cmdfunctions_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json);
|
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json);
|
||||||
static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = -1);
|
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id = -1);
|
||||||
static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flag = 0);
|
|
||||||
static void
|
static void add(const uint8_t device_type,
|
||||||
add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, bool hidden = false);
|
const __FlashStringHelper * cmd,
|
||||||
|
cmdfunction_p cb,
|
||||||
|
const __FlashStringHelper * description,
|
||||||
|
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL);
|
||||||
|
|
||||||
|
static void add_returns_json(const uint8_t device_type,
|
||||||
|
const __FlashStringHelper * cmd,
|
||||||
|
cmdfunction_json_p cb,
|
||||||
|
const __FlashStringHelper * description,
|
||||||
|
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL);
|
||||||
|
|
||||||
static void show_all(uuid::console::Shell & shell);
|
static void show_all(uuid::console::Shell & shell);
|
||||||
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
|
||||||
static Command::CmdFunction * find_command(const uint8_t device_type, char * cmd, int8_t & id);
|
static Command::CmdFunction * find_command(const uint8_t device_type, char * cmd, int8_t & id);
|
||||||
|
|||||||
@@ -376,15 +376,8 @@ void EMSESPShell::add_console_commands() {
|
|||||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||||
JsonObject json = doc.to<JsonObject>();
|
JsonObject json = doc.to<JsonObject>();
|
||||||
|
|
||||||
bool ok = false;
|
|
||||||
// validate that a command is present
|
// validate that a command is present
|
||||||
if (arguments.size() < 2) {
|
if (arguments.size() < 2) {
|
||||||
// // no cmd specified, default to empty command
|
|
||||||
// if (Command::call(device_type, "", "", -1, json)) {
|
|
||||||
// serializeJsonPretty(doc, shell);
|
|
||||||
// shell.println();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
shell.print(F("Missing command. Available commands are: "));
|
shell.print(F("Missing command. Available commands are: "));
|
||||||
Command::show(shell, device_type, false); // non-verbose mode
|
Command::show(shell, device_type, false); // non-verbose mode
|
||||||
return;
|
return;
|
||||||
@@ -392,30 +385,36 @@ void EMSESPShell::add_console_commands() {
|
|||||||
|
|
||||||
const char * cmd = arguments[1].c_str();
|
const char * cmd = arguments[1].c_str();
|
||||||
|
|
||||||
|
uint8_t cmd_return = 1; // OK
|
||||||
|
|
||||||
if (arguments.size() == 2) {
|
if (arguments.size() == 2) {
|
||||||
// no value specified, just the cmd
|
// no value specified, just the cmd
|
||||||
ok = Command::call(device_type, cmd, nullptr, -1, json);
|
cmd_return = Command::call(device_type, cmd, nullptr, true, -1, json);
|
||||||
} else if (arguments.size() == 3) {
|
} else if (arguments.size() == 3) {
|
||||||
if (strncmp(cmd, "info", 4) == 0) {
|
if (strncmp(cmd, "info", 4) == 0) {
|
||||||
// info has a id but no value
|
// info has a id but no value
|
||||||
ok = Command::call(device_type, cmd, nullptr, atoi(arguments.back().c_str()), json);
|
cmd_return = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json);
|
||||||
} else {
|
} else {
|
||||||
// has a value but no id
|
// has a value but no id so use -1
|
||||||
ok = Command::call(device_type, cmd, arguments.back().c_str(), -1, json);
|
cmd_return = Command::call(device_type, cmd, arguments.back().c_str(), true, -1, json);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// use value, which could be an id or hc
|
// use value, which could be an id or hc
|
||||||
ok = Command::call(device_type, cmd, arguments[2].c_str(), atoi(arguments[3].c_str()), json);
|
cmd_return = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok && json.size()) {
|
if (cmd_return == 1 && json.size()) {
|
||||||
serializeJsonPretty(doc, shell);
|
serializeJsonPretty(doc, shell);
|
||||||
shell.println();
|
shell.println();
|
||||||
return;
|
return;
|
||||||
} else if (!ok) {
|
}
|
||||||
shell.println(F("Unknown command, value, or id."));
|
|
||||||
|
if (cmd_return == 2) {
|
||||||
|
shell.println(F("Unknown command"));
|
||||||
shell.print(F("Available commands are: "));
|
shell.print(F("Available commands are: "));
|
||||||
Command::show(shell, device_type, false); // non-verbose mode
|
Command::show(shell, device_type, false); // non-verbose mode
|
||||||
|
} else if (cmd_return == 3) {
|
||||||
|
shell.println(F("Bad syntax"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
|
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
|
||||||
|
|||||||
@@ -120,9 +120,6 @@ enum DeviceValueTAG : uint8_t {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// mqtt flags for command subscriptions
|
|
||||||
enum MqttSubFlag : uint8_t { FLAG_NORMAL = 0, FLAG_HC, FLAG_WWC, FLAG_NOSUB };
|
|
||||||
|
|
||||||
// mqtt-HA flags
|
// mqtt-HA flags
|
||||||
enum DeviceValueHA : uint8_t { HA_NONE = 0, HA_VALUE, HA_DONE };
|
enum DeviceValueHA : uint8_t { HA_NONE = 0, HA_VALUE, HA_DONE };
|
||||||
|
|
||||||
@@ -170,6 +167,7 @@ class EMSdevice {
|
|||||||
return ((device_id & 0x7F) == (device_id_ & 0x7F));
|
return ((device_id & 0x7F) == (device_id_ & 0x7F));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flags
|
||||||
inline void add_flags(uint8_t flags) {
|
inline void add_flags(uint8_t flags) {
|
||||||
flags_ |= flags;
|
flags_ |= flags;
|
||||||
}
|
}
|
||||||
@@ -281,15 +279,14 @@ class EMSdevice {
|
|||||||
const __FlashStringHelper * const * options,
|
const __FlashStringHelper * const * options,
|
||||||
const __FlashStringHelper * const * name,
|
const __FlashStringHelper * const * name,
|
||||||
uint8_t uom);
|
uint8_t uom);
|
||||||
// void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max);
|
|
||||||
|
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value);
|
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value);
|
||||||
|
|
||||||
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0);
|
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0);
|
||||||
|
|
||||||
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
|
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
|
||||||
// void register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0);
|
|
||||||
|
|
||||||
void publish_mqtt_ha_sensor();
|
void publish_mqtt_ha_sensor();
|
||||||
|
|
||||||
|
|||||||
42
src/mqtt.cpp
42
src/mqtt.cpp
@@ -83,7 +83,7 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// subscribe to the command topic if it doesn't exist yet
|
// subscribe to the command topic if it doesn't exist yet
|
||||||
void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
|
void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flags) {
|
||||||
std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type); // thermostat, boiler, etc...
|
std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type); // thermostat, boiler, etc...
|
||||||
|
|
||||||
// see if we have already a handler for the device type (boiler, thermostat). If not add it
|
// see if we have already a handler for the device type (boiler, thermostat). If not add it
|
||||||
@@ -108,7 +108,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper
|
|||||||
// register the individual commands too (e.g. ems-esp/boiler/wwonetime)
|
// register the individual commands too (e.g. ems-esp/boiler/wwonetime)
|
||||||
// https://github.com/emsesp/EMS-ESP32/issues/31
|
// https://github.com/emsesp/EMS-ESP32/issues/31
|
||||||
std::string topic(MQTT_TOPIC_MAX_SIZE, '\0');
|
std::string topic(MQTT_TOPIC_MAX_SIZE, '\0');
|
||||||
if (subscribe_format_ == 2 && flag == MqttSubFlag::FLAG_HC) {
|
if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_MAIN_HC && ((flags & CommandFlag::MQTT_SUB_FLAG_HC) == CommandFlag::MQTT_SUB_FLAG_HC)) {
|
||||||
topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd);
|
topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd);
|
||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd);
|
topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd);
|
||||||
@@ -117,7 +117,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper
|
|||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
topic = cmd_topic + "/hc4/" + uuid::read_flash_string(cmd);
|
topic = cmd_topic + "/hc4/" + uuid::read_flash_string(cmd);
|
||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
} else if (subscribe_format_ && flag != MqttSubFlag::FLAG_NOSUB) {
|
} else if (subscribe_format_ != Subscribe_Format::GENERAL && ((flags & CommandFlag::MQTT_SUB_FLAG_NOSUB) == CommandFlag::MQTT_SUB_FLAG_NOSUB)) {
|
||||||
topic = cmd_topic + "/" + uuid::read_flash_string(cmd);
|
topic = cmd_topic + "/" + uuid::read_flash_string(cmd);
|
||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ void Mqtt::resubscribe() {
|
|||||||
}
|
}
|
||||||
for (const auto & cf : Command::commands()) {
|
for (const auto & cf : Command::commands()) {
|
||||||
std::string topic(MQTT_TOPIC_MAX_SIZE, '\0');
|
std::string topic(MQTT_TOPIC_MAX_SIZE, '\0');
|
||||||
if (subscribe_format_ == 2 && cf.flag_ == MqttSubFlag::FLAG_HC) {
|
if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_MAIN_HC && cf.has_flags(CommandFlag::MQTT_SUB_FLAG_HC)) {
|
||||||
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc1/" + uuid::read_flash_string(cf.cmd_);
|
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc1/" + uuid::read_flash_string(cf.cmd_);
|
||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc2/" + uuid::read_flash_string(cf.cmd_);
|
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc2/" + uuid::read_flash_string(cf.cmd_);
|
||||||
@@ -149,7 +149,7 @@ void Mqtt::resubscribe() {
|
|||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc4/" + uuid::read_flash_string(cf.cmd_);
|
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc4/" + uuid::read_flash_string(cf.cmd_);
|
||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
} else if (subscribe_format_ && cf.flag_ != MqttSubFlag::FLAG_NOSUB) {
|
} else if (subscribe_format_ != Subscribe_Format::GENERAL && !cf.has_flags(CommandFlag::MQTT_SUB_FLAG_NOSUB)) {
|
||||||
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/" + uuid::read_flash_string(cf.cmd_);
|
topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/" + uuid::read_flash_string(cf.cmd_);
|
||||||
queue_subscribe_message(topic);
|
queue_subscribe_message(topic);
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
|||||||
shell.printfln(F(" %s/%s"), mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str());
|
shell.printfln(F(" %s/%s"), mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str());
|
||||||
}
|
}
|
||||||
for (const auto & cf : Command::commands()) {
|
for (const auto & cf : Command::commands()) {
|
||||||
if (subscribe_format_ == 2 && cf.flag_ == MqttSubFlag::FLAG_HC) {
|
if (subscribe_format_ == 2 && cf.has_flags(CommandFlag::MQTT_SUB_FLAG_HC)) {
|
||||||
shell.printfln(F(" %s/%s/hc1/%s"),
|
shell.printfln(F(" %s/%s/hc1/%s"),
|
||||||
mqtt_base_.c_str(),
|
mqtt_base_.c_str(),
|
||||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||||
@@ -242,7 +242,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
|||||||
mqtt_base_.c_str(),
|
mqtt_base_.c_str(),
|
||||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||||
uuid::read_flash_string(cf.cmd_).c_str());
|
uuid::read_flash_string(cf.cmd_).c_str());
|
||||||
} else if (subscribe_format_ && cf.flag_ != MqttSubFlag::FLAG_NOSUB) {
|
} else if (subscribe_format_ == 1 && !cf.has_flags(CommandFlag::MQTT_SUB_FLAG_NOSUB)) {
|
||||||
shell.printfln(F(" %s/%s/%s"),
|
shell.printfln(F(" %s/%s/%s"),
|
||||||
mqtt_base_.c_str(),
|
mqtt_base_.c_str(),
|
||||||
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
EMSdevice::device_type_2_device_name(cf.device_type_).c_str(),
|
||||||
@@ -346,8 +346,13 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
|||||||
}
|
}
|
||||||
cmd_only++; // skip the /
|
cmd_only++; // skip the /
|
||||||
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s), mf.device_type_, topic, cmd_only, message);
|
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s), mf.device_type_, topic, cmd_only, message);
|
||||||
if (!Command::call(mf.device_type_, cmd_only, message)) {
|
// call command, assume admin authentication is allowed
|
||||||
LOG_ERROR(F("No matching cmd (%s) in topic %s, or invalid data"), cmd_only, topic);
|
uint8_t cmd_return = Command::call(mf.device_type_, cmd_only, message, true);
|
||||||
|
if (cmd_return == 2) {
|
||||||
|
LOG_ERROR(F("No matching cmd (%s) in topic %s"), cmd_only, topic);
|
||||||
|
Mqtt::publish(F_(response), "unknown");
|
||||||
|
} else if (cmd_return == 3) {
|
||||||
|
LOG_ERROR(F("Invalid data with cmd (%s) in topic %s"), cmd_only, topic);
|
||||||
Mqtt::publish(F_(response), "unknown");
|
Mqtt::publish(F_(response), "unknown");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -376,29 +381,32 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
|
|||||||
n = doc["id"];
|
n = doc["id"];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cmd_known = false;
|
uint8_t cmd_return = 1; // OK
|
||||||
JsonVariant data = doc["data"];
|
JsonVariant data = doc["data"];
|
||||||
|
|
||||||
if (data.is<const char *>()) {
|
if (data.is<const char *>()) {
|
||||||
cmd_known = Command::call(mf.device_type_, command, data.as<const char *>(), n);
|
cmd_return = Command::call(mf.device_type_, command, data.as<const char *>(), true, n);
|
||||||
} else if (data.is<int>()) {
|
} else if (data.is<int>()) {
|
||||||
char data_str[10];
|
char data_str[10];
|
||||||
cmd_known = Command::call(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as<int>()), n);
|
cmd_return = Command::call(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as<int>()), true, n);
|
||||||
} else if (data.is<float>()) {
|
} else if (data.is<float>()) {
|
||||||
char data_str[10];
|
char data_str[10];
|
||||||
cmd_known = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as<float>(), 2), n);
|
cmd_return = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as<float>(), 2), true, n);
|
||||||
} else if (data.isNull()) {
|
} else if (data.isNull()) {
|
||||||
DynamicJsonDocument resp(EMSESP_JSON_SIZE_XLARGE_DYN);
|
DynamicJsonDocument resp(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||||
JsonObject json = resp.to<JsonObject>();
|
JsonObject json = resp.to<JsonObject>();
|
||||||
cmd_known = Command::call(mf.device_type_, command, "", n, json);
|
cmd_return = Command::call(mf.device_type_, command, "", true, n, json);
|
||||||
if (cmd_known && json.size()) {
|
if (json.size()) {
|
||||||
Mqtt::publish(F_(response), resp.as<JsonObject>());
|
Mqtt::publish(F_(response), resp.as<JsonObject>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cmd_known) {
|
if (cmd_return == 2) {
|
||||||
LOG_ERROR(F("No matching cmd (%s) or invalid data"), command);
|
LOG_ERROR(F("No matching cmd (%s)"), command);
|
||||||
|
Mqtt::publish(F_(response), "unknown");
|
||||||
|
} else if (cmd_return == 3) {
|
||||||
|
LOG_ERROR(F("Invalid data for cmd (%s)"), command);
|
||||||
Mqtt::publish(F_(response), "unknown");
|
Mqtt::publish(F_(response), "unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/mqtt.h
17
src/mqtt.h
@@ -81,7 +81,20 @@ class Mqtt {
|
|||||||
|
|
||||||
enum Operation { PUBLISH, SUBSCRIBE };
|
enum Operation { PUBLISH, SUBSCRIBE };
|
||||||
|
|
||||||
enum HA_Climate_Format : uint8_t { CURRENT = 1, SETPOINT, ZERO };
|
enum HA_Climate_Format : uint8_t {
|
||||||
|
CURRENT = 1, // 1
|
||||||
|
SETPOINT, // 2
|
||||||
|
ZERO // 3
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// subscribe_format
|
||||||
|
enum Subscribe_Format : uint8_t {
|
||||||
|
GENERAL = 0, // 0
|
||||||
|
INDIVIDUAL_MAIN_HC, // 1
|
||||||
|
INDIVIDUAL_ALL_HC // 2
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // note this should really match the user setting in mqttSettings.maxTopicLength
|
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // note this should really match the user setting in mqttSettings.maxTopicLength
|
||||||
|
|
||||||
@@ -109,7 +122,7 @@ class Mqtt {
|
|||||||
const uint8_t device_type,
|
const uint8_t device_type,
|
||||||
const __FlashStringHelper * entity,
|
const __FlashStringHelper * entity,
|
||||||
const uint8_t uom = 0);
|
const uint8_t uom = 0);
|
||||||
static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t tag = 0);
|
static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flags = 0);
|
||||||
|
|
||||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
||||||
static void show_mqtt(uuid::console::Shell & shell);
|
static void show_mqtt(uuid::console::Shell & shell);
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ void Shower::send_mqtt_stat(bool state, bool force) {
|
|||||||
void Shower::shower_alert_stop() {
|
void Shower::shower_alert_stop() {
|
||||||
if (doing_cold_shot_) {
|
if (doing_cold_shot_) {
|
||||||
LOG_DEBUG(F("Shower Alert stopped"));
|
LOG_DEBUG(F("Shower Alert stopped"));
|
||||||
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true");
|
(void) Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true", true); // no need to check authentication
|
||||||
doing_cold_shot_ = false;
|
doing_cold_shot_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ void Shower::shower_alert_stop() {
|
|||||||
void Shower::shower_alert_start() {
|
void Shower::shower_alert_start() {
|
||||||
if (shower_alert_) {
|
if (shower_alert_) {
|
||||||
LOG_DEBUG(F("Shower Alert started"));
|
LOG_DEBUG(F("Shower Alert started"));
|
||||||
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
|
(void) Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false", true); // no need to check authentication
|
||||||
doing_cold_shot_ = true;
|
doing_cold_shot_ = true;
|
||||||
alert_timer_start_ = uuid::get_uptime(); // timer starts now
|
alert_timer_start_ = uuid::get_uptime(); // timer starts now
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -624,13 +624,14 @@ void System::system_check() {
|
|||||||
// these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
|
// these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
|
||||||
// no individual subscribe for pin command because id is needed
|
// no individual subscribe for pin command because id is needed
|
||||||
void System::commands_init() {
|
void System::commands_init() {
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set GPIO"), MqttSubFlag::FLAG_NOSUB);
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set GPIO"), CommandFlag::MQTT_SUB_FLAG_NOSUB | CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status"));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restarts EMS-ESP"), CommandFlag::ADMIN_ONLY);
|
||||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings"));
|
Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status"));
|
||||||
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands"));
|
Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings"));
|
||||||
|
Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands"));
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run tests"));
|
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run tests"));
|
||||||
#endif
|
#endif
|
||||||
@@ -795,11 +796,12 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
|||||||
node = json.createNestedObject("System");
|
node = json.createNestedObject("System");
|
||||||
node["version"] = EMSESP_APP_VERSION;
|
node["version"] = EMSESP_APP_VERSION;
|
||||||
|
|
||||||
|
// hide ssid from this list
|
||||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
||||||
node = json.createNestedObject("Network");
|
node = json.createNestedObject("Network");
|
||||||
// node["ssid"] = settings.ssid; // commented out - people don't like others to see this
|
|
||||||
node["hostname"] = settings.hostname;
|
node["hostname"] = settings.hostname;
|
||||||
node["static_ip_config"] = settings.staticIPConfig;
|
node["static_ip_config"] = settings.staticIPConfig;
|
||||||
|
node["enableIPv6"] = settings.enableIPv6;
|
||||||
JsonUtils::writeIP(node, "local_ip", settings.localIP);
|
JsonUtils::writeIP(node, "local_ip", settings.localIP);
|
||||||
JsonUtils::writeIP(node, "gateway_ip", settings.gatewayIP);
|
JsonUtils::writeIP(node, "gateway_ip", settings.gatewayIP);
|
||||||
JsonUtils::writeIP(node, "subnet_mask", settings.subnetMask);
|
JsonUtils::writeIP(node, "subnet_mask", settings.subnetMask);
|
||||||
@@ -839,6 +841,7 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
|||||||
node["ha_enabled"] = settings.ha_enabled;
|
node["ha_enabled"] = settings.ha_enabled;
|
||||||
node["mqtt_qos"] = settings.mqtt_qos;
|
node["mqtt_qos"] = settings.mqtt_qos;
|
||||||
node["mqtt_retain"] = settings.mqtt_retain;
|
node["mqtt_retain"] = settings.mqtt_retain;
|
||||||
|
node["subscribe_format"] = settings.subscribe_format;
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
@@ -996,4 +999,12 @@ bool System::load_board_profile(std::vector<uint8_t> & data, const std::string &
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restart command - perform a hard reset
|
||||||
|
bool System::command_restart(const char * value, const int8_t id) {
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
ESP.restart();
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -52,13 +52,15 @@ class System {
|
|||||||
static bool command_send(const char * value, const int8_t id);
|
static bool command_send(const char * value, const int8_t id);
|
||||||
static bool command_publish(const char * value, const int8_t id);
|
static bool command_publish(const char * value, const int8_t id);
|
||||||
static bool command_fetch(const char * value, const int8_t id);
|
static bool command_fetch(const char * value, const int8_t id);
|
||||||
static bool command_info(const char * value, const int8_t id, JsonObject & json);
|
static bool command_restart(const char * value, const int8_t id);
|
||||||
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
|
|
||||||
static bool command_commands(const char * value, const int8_t id, JsonObject & json);
|
|
||||||
#if defined(EMSESP_DEBUG)
|
#if defined(EMSESP_DEBUG)
|
||||||
static bool command_test(const char * value, const int8_t id);
|
static bool command_test(const char * value, const int8_t id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static bool command_info(const char * value, const int8_t id, JsonObject & json);
|
||||||
|
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
|
||||||
|
static bool command_commands(const char * value, const int8_t id, JsonObject & json);
|
||||||
|
|
||||||
void restart();
|
void restart();
|
||||||
void format(uuid::console::Shell & shell);
|
void format(uuid::console::Shell & shell);
|
||||||
void upload_status(bool in_progress);
|
void upload_status(bool in_progress);
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
if (emsdevice) {
|
if (emsdevice) {
|
||||||
doc.clear();
|
doc.clear();
|
||||||
JsonObject json = doc.to<JsonObject>();
|
JsonObject json = doc.to<JsonObject>();
|
||||||
Command::call(emsdevice->device_type(), "info", nullptr, -1, json);
|
Command::call(emsdevice->device_type(), "info", nullptr, true, -1, json);
|
||||||
|
|
||||||
Serial.print(COLOR_YELLOW);
|
Serial.print(COLOR_YELLOW);
|
||||||
if (json.size() != 0) {
|
if (json.size() != 0) {
|
||||||
@@ -424,7 +424,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
run_test("boiler");
|
run_test("boiler");
|
||||||
|
|
||||||
// device type, command, data
|
// device type, command, data
|
||||||
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
|
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command == "fr120") {
|
if (command == "fr120") {
|
||||||
@@ -935,12 +935,23 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
|||||||
Mqtt::ha_enabled(false);
|
Mqtt::ha_enabled(false);
|
||||||
run_test("general");
|
run_test("general");
|
||||||
AsyncWebServerRequest request;
|
AsyncWebServerRequest request;
|
||||||
|
|
||||||
|
// GET
|
||||||
request.method(HTTP_GET);
|
request.method(HTTP_GET);
|
||||||
request.url("/api/thermostat/seltemp");
|
request.url("/api/thermostat/seltemp");
|
||||||
EMSESP::webAPIService.webAPIService_get(&request);
|
EMSESP::webAPIService.webAPIService_get(&request);
|
||||||
|
|
||||||
request.url("/api/boiler/syspress");
|
request.url("/api/boiler/syspress");
|
||||||
EMSESP::webAPIService.webAPIService_get(&request);
|
EMSESP::webAPIService.webAPIService_get(&request);
|
||||||
|
|
||||||
|
request.url("/api/system/commands");
|
||||||
|
EMSESP::webAPIService.webAPIService_get(&request);
|
||||||
|
|
||||||
|
// POST
|
||||||
|
request.method(HTTP_POST);
|
||||||
|
request.url("/api/system/commands");
|
||||||
|
EMSESP::webAPIService.webAPIService_get(&request);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ namespace emsesp {
|
|||||||
// #define EMSESP_DEBUG_DEFAULT "board_profile"
|
// #define EMSESP_DEBUG_DEFAULT "board_profile"
|
||||||
// #define EMSESP_DEBUG_DEFAULT "shower_alert"
|
// #define EMSESP_DEBUG_DEFAULT "shower_alert"
|
||||||
// #define EMSESP_DEBUG_DEFAULT "310"
|
// #define EMSESP_DEBUG_DEFAULT "310"
|
||||||
// #define EMSESP_DEBUG_DEFAULT "api"
|
#define EMSESP_DEBUG_DEFAULT "api"
|
||||||
#define EMSESP_DEBUG_DEFAULT "crash"
|
// #define EMSESP_DEBUG_DEFAULT "crash"
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace emsesp {
|
|||||||
|
|
||||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||||
: _securityManager(securityManager)
|
: _securityManager(securityManager)
|
||||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS
|
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS, must use 'Content-Type: application/json' in header
|
||||||
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS
|
server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS
|
||||||
server->addHandler(&_apiHandler);
|
server->addHandler(&_apiHandler);
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
|
|||||||
// HTTP_POST | HTTP_PUT | HTTP_PATCH
|
// HTTP_POST | HTTP_PUT | HTTP_PATCH
|
||||||
// POST/PUT /{device}[/{hc}][/{name}]
|
// POST/PUT /{device}[/{hc}][/{name}]
|
||||||
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
|
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||||
// extra the params from the json body
|
// if no body then treat it as a secure GET
|
||||||
if (not json.is<JsonObject>()) {
|
if (not json.is<JsonObject>()) {
|
||||||
webAPIService_get(request);
|
webAPIService_get(request);
|
||||||
return;
|
return;
|
||||||
@@ -158,33 +158,42 @@ void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_
|
|||||||
|
|
||||||
// check that we have permissions first. We require authenticating on 1 or more of these conditions:
|
// check that we have permissions first. We require authenticating on 1 or more of these conditions:
|
||||||
// 1. any HTTP POSTs or PUTs
|
// 1. any HTTP POSTs or PUTs
|
||||||
// 2. a HTTP GET which has a 'data' parameter which is not empty (to keep v2 compatibility)
|
// 2. an HTTP GET which has a 'data' parameter which is not empty (to keep v2 compatibility)
|
||||||
auto method = request->method();
|
auto method = request->method();
|
||||||
bool have_data = !value_s.empty();
|
bool have_data = !value_s.empty();
|
||||||
bool admin_allowed;
|
bool authenticated = false;
|
||||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||||
admin_allowed = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
authenticated = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((method != HTTP_GET) || ((method == HTTP_GET) && have_data)) {
|
if ((method != HTTP_GET) || ((method == HTTP_GET) && have_data)) {
|
||||||
if (!admin_allowed) {
|
if (!authenticated) {
|
||||||
send_message_response(request, 401, "Bad credentials"); // Unauthorized
|
send_message_response(request, 401, "Bad credentials"); // Unauthorized
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we have all the parameters go and execute the command
|
|
||||||
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||||
JsonObject json = response->getRoot();
|
JsonObject json = response->getRoot();
|
||||||
|
|
||||||
bool ok = Command::call(device_type, cmd_s.c_str(), (have_data ? value_s.c_str() : nullptr), id_n, json);
|
// now we have all the parameters go and execute the command
|
||||||
|
// the function will also determine if authentication is needed to execute its command
|
||||||
|
uint8_t cmd_reply = Command::call(device_type, cmd_s.c_str(), (have_data ? value_s.c_str() : nullptr), authenticated, id_n, json);
|
||||||
|
|
||||||
// check for errors
|
// check for errors
|
||||||
if (!ok) {
|
if (cmd_reply == 2) {
|
||||||
|
delete response;
|
||||||
|
send_message_response(request, 400, "Command not found"); // Bad Request
|
||||||
|
return;
|
||||||
|
} else if (cmd_reply == 3) {
|
||||||
delete response;
|
delete response;
|
||||||
send_message_response(request, 400, "Problems parsing elements"); // Bad Request
|
send_message_response(request, 400, "Problems parsing elements"); // Bad Request
|
||||||
return;
|
return;
|
||||||
|
} else if (cmd_reply == 4) {
|
||||||
|
delete response;
|
||||||
|
send_message_response(request, 401, "Bad credentials"); // Unauthorized
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!json.size()) {
|
if (!json.size()) {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// takes a command and its data value from a specific Device, from the Web
|
// takes a command and its data value from a specific Device, from the Web
|
||||||
|
// assumes the service has been checked for admin authentication
|
||||||
void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||||
if (json.is<JsonObject>()) {
|
if (json.is<JsonObject>()) {
|
||||||
JsonObject dv = json["devicevalue"];
|
JsonObject dv = json["devicevalue"];
|
||||||
@@ -129,22 +130,22 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
|
|||||||
if (emsdevice->unique_id() == id) {
|
if (emsdevice->unique_id() == id) {
|
||||||
const char * cmd = dv["c"];
|
const char * cmd = dv["c"];
|
||||||
uint8_t device_type = emsdevice->device_type();
|
uint8_t device_type = emsdevice->device_type();
|
||||||
bool ok = false;
|
uint8_t cmd_return = 1; // OK
|
||||||
char s[10];
|
char s[10];
|
||||||
// the data could be in any format, but we need string
|
// the data could be in any format, but we need string
|
||||||
JsonVariant data = dv["v"];
|
JsonVariant data = dv["v"];
|
||||||
if (data.is<const char *>()) {
|
if (data.is<const char *>()) {
|
||||||
ok = Command::call(device_type, cmd, data.as<const char *>());
|
cmd_return = Command::call(device_type, cmd, data.as<const char *>(), true);
|
||||||
} else if (data.is<int>()) {
|
} else if (data.is<int>()) {
|
||||||
ok = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0));
|
cmd_return = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0), true);
|
||||||
} else if (data.is<float>()) {
|
} else if (data.is<float>()) {
|
||||||
ok = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as<float>(), 1));
|
cmd_return = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as<float>(), 1), true);
|
||||||
} else if (data.is<bool>()) {
|
} else if (data.is<bool>()) {
|
||||||
ok = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false");
|
cmd_return = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send "Write command sent to device" or "Write command failed"
|
// send "Write command sent to device" or "Write command failed"
|
||||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
AsyncWebServerResponse * response = request->beginResponse((cmd_return == 1) ? 200 : 204);
|
||||||
request->send(response);
|
request->send(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user