feat: expose cmd's via MQTT directly #31

This commit is contained in:
proddy
2021-03-21 12:47:47 +01:00
parent fd11a09882
commit f2dbc26491
4 changed files with 46 additions and 17 deletions

View File

@@ -64,11 +64,11 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
if (!mqtt_subfunctions_.empty()) { if (!mqtt_subfunctions_.empty()) {
for (auto & mqtt_subfunction : mqtt_subfunctions_) { for (auto & mqtt_subfunction : mqtt_subfunctions_) {
if ((mqtt_subfunction.device_type_ == device_type) && (strcmp(mqtt_subfunction.topic_.c_str(), topic.c_str()) == 0)) { if ((mqtt_subfunction.device_type_ == device_type) && (strcmp(mqtt_subfunction.topic_.c_str(), topic.c_str()) == 0)) {
// add the function, in case its not there // add the function (in case its not there) and quit because it already exists
if (cb) { if (cb) {
mqtt_subfunction.mqtt_subfunction_ = cb; mqtt_subfunction.mqtt_subfunction_ = cb;
} }
return; // it exists, exit return;
} }
} }
} }
@@ -89,8 +89,9 @@ 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) { void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb) {
std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type); 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
bool exists = false; bool exists = false;
if (!mqtt_subfunctions_.empty()) { if (!mqtt_subfunctions_.empty()) {
for (const auto & mqtt_subfunction : mqtt_subfunctions_) { for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
@@ -101,15 +102,21 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper
} }
if (!exists) { if (!exists) {
Mqtt::subscribe(device_type, cmd_topic, nullptr); // use an empty function handler to signal this is a command function Mqtt::subscribe(device_type, cmd_topic, nullptr); // use an empty function handler to signal this is a command function only (e.g. ems-esp/boiler)
LOG_DEBUG(F("Registering MQTT cmd %s with topic %s"), uuid::read_flash_string(cmd).c_str(), EMSdevice::device_type_2_device_name(device_type).c_str());
} }
LOG_DEBUG(F("Registering MQTT cmd %s with topic %s"), uuid::read_flash_string(cmd).c_str(), EMSdevice::device_type_2_device_name(device_type).c_str()); // register the individual commands too (e.g. ems-esp/boiler/wwonetime)
// https://github.com/emsesp/EMS-ESP32/issues/31
std::string topic(100, '\0');
topic = cmd_topic + "/" + uuid::read_flash_string(cmd);
Mqtt::subscribe(device_type, topic, nullptr);
} }
// subscribe to an MQTT topic, and store the associated callback function. For generic functions not tied to a specific device // subscribe to an MQTT topic, and store the associated callback function
// For generic functions not tied to a specific device
void Mqtt::subscribe(const std::string & topic, mqtt_subfunction_p cb) { void Mqtt::subscribe(const std::string & topic, mqtt_subfunction_p cb) {
subscribe(0, topic, cb); // no device_id needed, if generic to EMS-ESP subscribe(0, topic, cb); // no device_id needed if generic to EMS-ESP
} }
// resubscribe to all MQTT topics // resubscribe to all MQTT topics
@@ -189,7 +196,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
// show subscriptions // show subscriptions
shell.printfln(F("MQTT topic subscriptions:")); shell.printfln(F("MQTT topic subscriptions:"));
for (const auto & mqtt_subfunction : mqtt_subfunctions_) { for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
shell.printfln(F(" %s/%s"), mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str()); shell.printfln(F(" %s/%s (%s)"), mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str(), EMSdevice::device_type_2_device_name(mqtt_subfunction.device_type_).c_str());
} }
shell.println(); shell.println();
@@ -266,6 +273,20 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
return; return;
} }
// check if it's not json, then try and extract the command from the topic name
if (message[0] != '{') {
char * cmd_only = strrchr(topic, '/');
if (cmd_only == NULL) {
return; // invalid topic name
}
cmd_only++; // skip the /
// 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)) {
LOG_ERROR(F("No matching cmd (%s) in topic %s, or invalid data"), cmd_only, topic);
}
return;
}
// It's a command then with the payload being JSON like {"cmd":"<cmd>", "data":<data>, "id":<n>} // It's a command then with the payload being JSON like {"cmd":"<cmd>", "data":<data>, "id":<n>}
// Find the command from the json and call it directly // Find the command from the json and call it directly
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc; StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
@@ -305,7 +326,7 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
} }
if (!cmd_known) { if (!cmd_known) {
LOG_ERROR(F("No matching cmd (%s), invalid data or command failed"), command); LOG_ERROR(F("No matching cmd (%s) or invalid data"), command);
} }
return; return;

View File

@@ -21,8 +21,6 @@
#include "test.h" #include "test.h"
// create some fake test data
namespace emsesp { namespace emsesp {
// no shell // no shell
@@ -368,12 +366,22 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
if (command == "boiler") { if (command == "boiler") {
shell.printfln(F("Testing boiler...")); shell.printfln(F("Testing boiler..."));
Mqtt::ha_enabled(false);
Mqtt::nested_format(true);
run_test("boiler"); run_test("boiler");
shell.invoke_command("show devices"); shell.invoke_command("show devices");
shell.invoke_command("show"); shell.invoke_command("show");
// shell.invoke_command("call boiler info"); shell.invoke_command("call boiler info");
// shell.invoke_command("call system publish"); shell.invoke_command("call system publish");
// shell.invoke_command("show mqtt");
EMSESP::mqtt_.incoming("ems-esp/boiler/wwonetime", "1");
EMSESP::mqtt_.incoming("ems-esp/boiler/wwonetime", "0");
EMSESP::mqtt_.incoming("ems-esp/boiler/heatingtemp", "24");
EMSESP::mqtt_.incoming("ems-esp/boiler/wwonetime", "test"); // should fail
EMSESP::mqtt_.incoming("ems-esp/boiler", "{\"cmd\":\"flowtemp\",\"id\":0,\"data\":22}");
shell.invoke_command("show mqtt");
} }
if (command == "fr120") { if (command == "fr120") {

View File

@@ -30,9 +30,9 @@ namespace emsesp {
// #define EMSESP_TEST_DEFAULT "mixer" // #define EMSESP_TEST_DEFAULT "mixer"
// #define EMSESP_TEST_DEFAULT "web" // #define EMSESP_TEST_DEFAULT "web"
// #define EMSESP_TEST_DEFAULT "general" // #define EMSESP_TEST_DEFAULT "general"
// #define EMSESP_TEST_DEFAULT "boiler" #define EMSESP_TEST_DEFAULT "boiler"
// #define EMSESP_TEST_DEFAULT "mqtt2" // #define EMSESP_TEST_DEFAULT "mqtt2"
#define EMSESP_TEST_DEFAULT "mqtt_nested" // #define EMSESP_TEST_DEFAULT "mqtt_nested"
// #define EMSESP_TEST_DEFAULT "ha" // #define EMSESP_TEST_DEFAULT "ha"
class Test { class Test {

View File

@@ -1,2 +1,2 @@
#define EMSESP_APP_VERSION "3.0.1b0" #define EMSESP_APP_VERSION "3.0.1b1"
#define EMSESP_PLATFORM "ESP32" #define EMSESP_PLATFORM "ESP32"