diff --git a/CHANGELOG.md b/CHANGELOG.md
index db5451e12..990236540 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- support for uploading compressed firmware binaries
- add wWType to MQTT publish
- option to set the MQTT retain flag
+- HTTP REST API, e.g. http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
+- `show commands` command
### Fixed
- fix wwontime readback
@@ -21,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- renamed wWCircPumpType to wWChargeType
- Installation doc moved to wiki
+- Removed the _cmd from the MQTT topic names
### Removed
-
diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h
index f6007fd40..7183c4a36 100644
--- a/lib_standalone/ESP8266React.h
+++ b/lib_standalone/ESP8266React.h
@@ -31,6 +31,12 @@ class DummySettings {
String jwtSecret = "ems-esp";
String ssid = "ems-esp";
String password = "ems-esp";
+ String localIP;
+ String gatewayIP;
+ String subnetMask;
+ String staticIPConfig;
+ String dnsIP1;
+ String dnsIP2;
uint16_t publish_time_boiler;
uint16_t publish_time_thermostat;
uint16_t publish_time_solar;
@@ -105,4 +111,11 @@ class EMSESPSettingsService {
private:
};
+class JsonUtils {
+ public:
+ static void writeIP(JsonObject & root, const String & key, const String & ip) {
+ root[key] = ip;
+ }
+};
+
#endif
diff --git a/lib_standalone/ESPAsyncWebServer.h b/lib_standalone/ESPAsyncWebServer.h
index 7b4a188e2..b5472799e 100644
--- a/lib_standalone/ESPAsyncWebServer.h
+++ b/lib_standalone/ESPAsyncWebServer.h
@@ -12,6 +12,39 @@ class AsyncWebServerRequest;
class AsyncWebServerResponse;
class AsyncJsonResponse;
+class AsyncWebParameter {
+ private:
+ String _name;
+ String _value;
+ size_t _size;
+ bool _isForm;
+ bool _isFile;
+
+ public:
+ AsyncWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0)
+ : _name(name)
+ , _value(value)
+ , _size(size)
+ , _isForm(form)
+ , _isFile(file) {
+ }
+ const String & name() const {
+ return _name;
+ }
+ const String & value() const {
+ return _value;
+ }
+ size_t size() const {
+ return _size;
+ }
+ bool isPost() const {
+ return _isForm;
+ }
+ bool isFile() const {
+ return _isFile;
+ }
+};
+
typedef enum {
HTTP_GET = 0b00000001,
HTTP_POST = 0b00000010,
@@ -54,9 +87,41 @@ class AsyncWebServerRequest {
void send(AsyncWebServerResponse * response){};
void send(AsyncJsonResponse * response){};
void send(int code, const String & contentType = String(), const String & content = String()){};
+ void send(int code, const String & contentType, const __FlashStringHelper *){};
+
+ bool hasParam(const String & name, bool post, bool file) const {
+ return false;
+ }
+
+ bool hasParam(const char * name, bool post, bool file) const {
+ return false;
+ }
+
+ bool hasParam(const __FlashStringHelper * data) const {
+ return false;
+ }
+
+ bool hasParam(const __FlashStringHelper * data, bool post, bool file) const {
+ return false;
+ }
+
+ AsyncWebParameter * getParam(const String & name, bool post, bool file) const {
+ return nullptr;
+ }
+
+ AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const {
+ return nullptr;
+ }
+
+ AsyncWebParameter * getParam(const __FlashStringHelper * data) const {
+ return nullptr;
+ }
+
+ AsyncWebParameter * getParam(size_t num) const {
+ return nullptr;
+ }
AsyncWebServerResponse * beginResponse(int code, const String & contentType = String(), const String & content = String()) {
- // AsyncWebServerResponse *a = new AsyncWebServerResponse()
return nullptr;
}
diff --git a/lib_standalone/WString.h b/lib_standalone/WString.h
index c7b481bf2..fe4b2dfe0 100644
--- a/lib_standalone/WString.h
+++ b/lib_standalone/WString.h
@@ -33,14 +33,13 @@ class String {
return lhs;
}
- ///
bool isEmpty() {
return _str.empty();
}
- // long toInt() const {
- // return std::stol(_str);
- // }
+ long toInt() const {
+ return std::stol(_str);
+ }
bool equals(const char * s) {
return _str == s;
diff --git a/src/EMSESPAPIService.cpp b/src/EMSESPAPIService.cpp
new file mode 100644
index 000000000..c1df17702
--- /dev/null
+++ b/src/EMSESPAPIService.cpp
@@ -0,0 +1,103 @@
+/*
+ * EMS-ESP - https://github.com/proddy/EMS-ESP
+ * Copyright 2019 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "EMSESPAPIService.h"
+#include "emsesp.h"
+#include "command.h"
+#include "emsdevice.h"
+#include "system.h"
+
+namespace emsesp {
+
+EMSESPAPIService::EMSESPAPIService(AsyncWebServer * server) {
+ server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, std::bind(&EMSESPAPIService::emsespAPIService, this, std::placeholders::_1));
+}
+
+// http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
+void EMSESPAPIService::emsespAPIService(AsyncWebServerRequest * request) {
+ // must have device and cmd parameters
+ if ((!request->hasParam(F_(device))) || (!request->hasParam(F_(cmd)))) {
+ request->send(400, "text/plain", F("invalid syntax"));
+ return;
+ }
+
+ // get device
+ String device = request->getParam(F_(device))->value();
+ uint8_t device_type = EMSdevice::device_name_2_device_type(device.c_str());
+ if (device_type == emsesp::EMSdevice::DeviceType::UNKNOWN) {
+ request->send(400, "text/plain", F("invalid device"));
+ return;
+ }
+
+ // get cmd, we know we have one
+ String cmd = request->getParam(F_(cmd))->value();
+
+ // first test for special service commands
+ // e.g. http://ems-esp/api?device=system&cmd=info
+ if (device.equals("system")) {
+ if (cmd.equals("info")) {
+ request->send(200, "text/plain", System::export_settings());
+ EMSESP::logger().info(F("Sent settings json to web UI"));
+ return;
+ }
+ }
+
+ // look up command in our list
+ if (!Command::find(device_type, cmd.c_str())) {
+ request->send(400, "text/plain", F("invalid cmd"));
+ return;
+ }
+
+ String data;
+ if (request->hasParam(F_(data))) {
+ data = request->getParam(F_(data))->value();
+ }
+
+ String id;
+ if (request->hasParam(F_(id))) {
+ id = request->getParam(F_(id))->value();
+ }
+
+ // execute the command
+ bool ok = false;
+ if (data.isEmpty()) {
+ ok = Command::call_command(device_type, cmd.c_str(), nullptr, -1); // command only
+ } else if (id.isEmpty()) {
+ ok = Command::call_command(device_type, cmd.c_str(), data.c_str(), -1); // only ID
+ } else {
+ ok = Command::call_command(device_type, cmd.c_str(), data.c_str(), id.toInt()); // has cmd, data and id
+ }
+
+// debug
+#if defined(EMSESP_DEBUG)
+ std::string output(200, '\0');
+ snprintf_P(&output[0],
+ output.capacity() + 1,
+ PSTR("API: device=%s cmd=%s data=%s id=%s [%s]"),
+ device.c_str(),
+ cmd.c_str(),
+ data.c_str(),
+ id.c_str(),
+ ok ? F("OK") : F("Failed"));
+ EMSESP::logger().info(output.c_str());
+#endif
+
+ request->send(200, "text/plain", ok ? F("OK") : F("Failed"));
+}
+
+} // namespace emsesp
\ No newline at end of file
diff --git a/src/EMSESPAPIService.h b/src/EMSESPAPIService.h
new file mode 100644
index 000000000..90d45c11d
--- /dev/null
+++ b/src/EMSESPAPIService.h
@@ -0,0 +1,41 @@
+/*
+ * EMS-ESP - https://github.com/proddy/EMS-ESP
+ * Copyright 2019 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef EMSESPAPIService_h
+#define EMSESPAPIService_h
+
+#include
+#include
+#include
+
+#define EMSESP_API_SERVICE_PATH "/api"
+
+namespace emsesp {
+
+class EMSESPAPIService {
+ public:
+ EMSESPAPIService(AsyncWebServer * server);
+
+ private:
+ void emsespAPIService(AsyncWebServerRequest * request);
+
+};
+
+} // namespace emsesp
+
+#endif
diff --git a/src/EMSESPDevicesService.cpp b/src/EMSESPDevicesService.cpp
index a914d89c0..8bfd0cb66 100644
--- a/src/EMSESPDevicesService.cpp
+++ b/src/EMSESPDevicesService.cpp
@@ -18,7 +18,6 @@
#include "EMSESPDevicesService.h"
#include "emsesp.h"
-#include "mqtt.h"
namespace emsesp {
@@ -57,7 +56,7 @@ void EMSESPDevicesService::all_devices(AsyncWebServerRequest * request) {
obj["type"] = emsdevice->device_type_name();
obj["brand"] = emsdevice->brand_to_string();
obj["name"] = emsdevice->name();
- obj["deviceid"] = emsdevice->get_device_id();
+ obj["deviceid"] = emsdevice->device_id();
obj["productid"] = emsdevice->product_id();
obj["version"] = emsdevice->version();
}
diff --git a/src/EMSESPStatusService.cpp b/src/EMSESPStatusService.cpp
index c0f6252b9..ca54c0393 100644
--- a/src/EMSESPStatusService.cpp
+++ b/src/EMSESPStatusService.cpp
@@ -18,8 +18,6 @@
#include "EMSESPStatusService.h"
#include "emsesp.h"
-#include "mqtt.h"
-#include "version.h"
namespace emsesp {
diff --git a/src/command.cpp b/src/command.cpp
new file mode 100644
index 000000000..7694caf29
--- /dev/null
+++ b/src/command.cpp
@@ -0,0 +1,186 @@
+/*
+ * EMS-ESP - https://github.com/proddy/EMS-ESP
+ * Copyright 2019 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "command.h"
+#include "emsdevice.h"
+#include "emsesp.h"
+
+namespace emsesp {
+
+uuid::log::Logger Command::logger_{F_(command), uuid::log::Facility::DAEMON};
+
+std::vector Command::cmdfunctions_;
+
+// calls a command, context is the device_type
+// id may be used to represent a heating circuit for example
+// returns false if error or not found
+bool Command::call_command(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
+#ifdef EMSESP_DEBUG
+ if (id == -1) {
+ LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is default"), cmd, value);
+ } else {
+ LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is %d"), cmd, value, id);
+ }
+#endif
+
+ if (!cmdfunctions_.empty()) {
+ for (const auto & cf : cmdfunctions_) {
+ if (cf.device_type_ == device_type) {
+ const char * cf_cmd = uuid::read_flash_string(cf.cmd_).c_str();
+ if (strcmp(cf_cmd, cmd) == 0) {
+ return ((cf.cmdfunction_)(value, id)); // call function, data needs to be a string and can be null
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// add a command to the list
+void Command::add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb) {
+ cmdfunctions_.emplace_back(device_type, device_id, cmd, cb);
+
+ // see if we need to subscribe
+ Mqtt::register_command(device_type, device_id, cmd, cb);
+}
+
+// see if a command exists for that device type
+bool Command::find(const uint8_t device_type, const char * cmd) {
+ for (const auto & cf : cmdfunctions_) {
+ if (strcmp(cmd, uuid::read_flash_string(cf.cmd_).c_str()) == 0) {
+ return true;
+ }
+ }
+ return false; // not found
+}
+
+// output list of all commands to console for a specific DeviceType
+void Command::show_commands(uuid::console::Shell & shell, uint8_t device_type) {
+ for (const auto & cf : Command::commands()) {
+ if (cf.device_type_ == device_type) {
+ shell.printf("%s ", uuid::read_flash_string(cf.cmd_).c_str());
+ }
+ }
+ shell.println();
+}
+
+// determines the device_type from the shell context we're in
+uint8_t Command::context_2_device_type(unsigned int context) {
+ if (context == ShellContext::MAIN) {
+ return EMSdevice::DeviceType::SERVICEKEY;
+ }
+ if (context == ShellContext::BOILER) {
+ return EMSdevice::DeviceType::BOILER;
+ }
+ if (context == ShellContext::MIXING) {
+ return EMSdevice::DeviceType::MIXING;
+ }
+ if (context == ShellContext::SOLAR) {
+ return EMSdevice::DeviceType::SOLAR;
+ }
+ if (context == ShellContext::SYSTEM) {
+ return EMSdevice::DeviceType::SERVICEKEY;
+ }
+ if (context == ShellContext::THERMOSTAT) {
+ return EMSdevice::DeviceType::THERMOSTAT;
+ }
+
+ return EMSdevice::DeviceType::UNKNOWN; // unknown type
+}
+
+// show command per current context
+void Command::show_commands(uuid::console::Shell & shell) {
+ show_commands(shell, context_2_device_type(shell.context()));
+}
+
+
+// output list of all commands to console
+void Command::show_all_commands(uuid::console::Shell & shell) {
+ // show system first
+ shell.printf("%s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SERVICEKEY).c_str());
+ show_commands(shell, EMSdevice::DeviceType::SERVICEKEY);
+
+ // do this in the order of factory classes to keep a consistent order when displaying
+ for (const auto & device_class : EMSFactory::device_handlers()) {
+ for (const auto & emsdevice : EMSESP::emsdevices) {
+ if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
+ shell.printf("%s: ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
+ show_commands(shell, device_class.first);
+ }
+ }
+ }
+}
+
+// given a context, automatically add the commands to the console
+void Command::add_context_commands(unsigned int context) {
+ // if we're adding commands for a thermostat or mixing, then include an additional optional paramter called heating circuit
+ flash_string_vector params;
+ if ((context == ShellContext::THERMOSTAT) || (context == ShellContext::MIXING)) {
+ params = flash_string_vector{F_(cmd_optional), F_(data_optional), F_(hc_optional)};
+ } else if ((context == ShellContext::MAIN) || (context == ShellContext::SYSTEM)) {
+ params = flash_string_vector{F_(cmd_optional), F_(data_optional), F_(n_optional)};
+ } else {
+ params = flash_string_vector{F_(cmd_optional), F_(data_optional)};
+ }
+
+ EMSESPShell::commands->add_command(
+ context,
+ CommandFlags::ADMIN,
+ flash_string_vector{F_(call)},
+ params,
+ [&](Shell & shell, const std::vector & arguments) {
+ if (arguments.empty()) {
+ // list options
+ shell.print("Available commands: ");
+ show_commands(shell);
+ shell.println();
+ return;
+ }
+
+ // determine the device_type from the shell context
+ uint8_t device_type = context_2_device_type(shell.context());
+
+ const char * cmd = arguments[0].c_str();
+ if (arguments.size() == 1) {
+ // no value specified
+ (void)Command::call_command(device_type, cmd, nullptr, -1);
+ } else if (arguments.size() == 2) {
+ // has a value but no id
+ (void)Command::call_command(device_type, cmd, arguments.back().c_str(), -1);
+ } else {
+ // use value, which could be an id or hc
+ (void)Command::call_command(device_type, cmd, arguments[1].c_str(), atoi(arguments[2].c_str()));
+ }
+ },
+ [&](Shell & shell __attribute__((unused)), const std::vector & arguments) -> std::vector {
+ if (arguments.size() > 0) {
+ return {};
+ }
+ std::vector commands;
+ uint8_t device_type = context_2_device_type(shell.context());
+ for (const auto & cf : Command::commands()) {
+ if (cf.device_type_ == device_type) {
+ commands.emplace_back(uuid::read_flash_string(cf.cmd_));
+ }
+ }
+ return commands;
+ });
+}
+
+
+} // namespace emsesp
\ No newline at end of file
diff --git a/src/command.h b/src/command.h
new file mode 100644
index 000000000..f020ff2d5
--- /dev/null
+++ b/src/command.h
@@ -0,0 +1,77 @@
+/*
+ * EMS-ESP - https://github.com/proddy/EMS-ESP
+ * Copyright 2019 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef EMSESP_COMMAND_H_
+#define EMSESP_COMMAND_H_
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "console.h"
+
+#include
+
+using uuid::console::Shell;
+
+namespace emsesp {
+
+using cmdfunction_p = std::function;
+
+class Command {
+ public:
+ struct CmdFunction {
+ uint8_t device_type_; // DeviceType::
+ uint8_t device_id_;
+ const __FlashStringHelper * cmd_;
+ cmdfunction_p cmdfunction_;
+
+ CmdFunction(uint8_t device_type, uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cmdfunction)
+ : device_type_(device_type)
+ , device_id_(device_id)
+ , cmd_(cmd)
+ , cmdfunction_(cmdfunction) {
+ }
+ };
+
+ static std::vector commands() {
+ return cmdfunctions_;
+ }
+
+ static bool call_command(const uint8_t device_type, const char * cmd, const char * value, const int8_t id);
+ static void add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb);
+ static void show_all_commands(uuid::console::Shell & shell);
+ static void show_commands(uuid::console::Shell & shell);
+ static void add_context_commands(unsigned int context);
+ static bool find(const uint8_t device_type, const char * cmd);
+
+ static std::vector cmdfunctions_; // list of commands
+
+ private:
+ static uuid::log::Logger logger_;
+
+ static void show_commands(uuid::console::Shell & shell, uint8_t device_type);
+ static uint8_t context_2_device_type(unsigned int context);
+};
+
+} // namespace emsesp
+
+#endif
\ No newline at end of file
diff --git a/src/console.cpp b/src/console.cpp
index 5809ff19f..288fa599c 100644
--- a/src/console.cpp
+++ b/src/console.cpp
@@ -149,6 +149,12 @@ void EMSESPShell::add_console_commands() {
flash_string_vector{F_(show), F_(mqtt)},
[](Shell & shell, const std::vector & arguments __attribute__((unused))) { Mqtt::show_mqtt(shell); });
+
+ commands->add_command(ShellContext::MAIN,
+ CommandFlags::USER,
+ flash_string_vector{F_(show), F_(commands)},
+ [](Shell & shell, const std::vector & arguments __attribute__((unused))) { Command::show_all_commands(shell); });
+
commands->add_command(
ShellContext::MAIN,
CommandFlags::ADMIN,
@@ -457,6 +463,11 @@ void Console::load_standard_commands(unsigned int context) {
shell.printfln(F("Filtering only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
}
});
+
+
+ // load the commands (console & mqtt topics) for this specific context
+ Command::add_context_commands(context);
+
}
// prompt, change per context
diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp
index f10e875dd..1daa9e6b5 100644
--- a/src/devices/boiler.cpp
+++ b/src/devices/boiler.cpp
@@ -52,22 +52,22 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_telegram_type(0xE9, F("UBADHWStatus"), false, [&](std::shared_ptr t) { process_UBADHWStatus(t); });
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, [&](std::shared_ptr t) { process_UBAParameterWWPlus(t); });
- // MQTT commands for boiler_cmd topic
- register_mqtt_cmd(F("comfort"), [&](const char * value, const int8_t id) { set_warmwater_mode(value, id); });
- register_mqtt_cmd(F("wwactivated"), [&](const char * value, const int8_t id) { set_warmwater_activated(value, id); });
- register_mqtt_cmd(F("wwtapactivated"), [&](const char * value, const int8_t id) { set_tapwarmwater_activated(value, id); });
- register_mqtt_cmd(F("wwonetime"), [&](const char * value, const int8_t id) { set_warmwater_onetime(value, id); });
- register_mqtt_cmd(F("wwcirculation"), [&](const char * value, const int8_t id) { set_warmwater_circulation(value, id); });
- register_mqtt_cmd(F("flowtemp"), [&](const char * value, const int8_t id) { set_flow_temp(value, id); });
- register_mqtt_cmd(F("wwtemp"), [&](const char * value, const int8_t id) { set_warmwater_temp(value, id); });
- register_mqtt_cmd(F("heatingactivated"), [&](const char * value, const int8_t id) { set_heating_activated(value, id); });
- register_mqtt_cmd(F("heatingtemp"), [&](const char * value, const int8_t id) { set_heating_temp(value, id); });
- register_mqtt_cmd(F("burnmaxpower"), [&](const char * value, const int8_t id) { set_max_power(value, id); });
- register_mqtt_cmd(F("burnminpower"), [&](const char * value, const int8_t id) { set_min_power(value, id); });
- register_mqtt_cmd(F("boilhyston"), [&](const char * value, const int8_t id) { set_hyst_on(value, id); });
- register_mqtt_cmd(F("boilhystoff"), [&](const char * value, const int8_t id) { set_hyst_off(value, id); });
- register_mqtt_cmd(F("burnperiod"), [&](const char * value, const int8_t id) { set_burn_period(value, id); });
- register_mqtt_cmd(F("pumpdelay"), [&](const char * value, const int8_t id) { set_pump_delay(value, id); });
+ // MQTT commands for boiler topic
+ register_mqtt_cmd(F("comfort"), [&](const char * value, const int8_t id) { return set_warmwater_mode(value, id); });
+ register_mqtt_cmd(F("wwactivated"), [&](const char * value, const int8_t id) { return set_warmwater_activated(value, id); });
+ register_mqtt_cmd(F("wwtapactivated"), [&](const char * value, const int8_t id) { return set_tapwarmwater_activated(value, id); });
+ register_mqtt_cmd(F("wwonetime"), [&](const char * value, const int8_t id) { return set_warmwater_onetime(value, id); });
+ register_mqtt_cmd(F("wwcirculation"), [&](const char * value, const int8_t id) { return set_warmwater_circulation(value, id); });
+ register_mqtt_cmd(F("flowtemp"), [&](const char * value, const int8_t id) { return set_flow_temp(value, id); });
+ register_mqtt_cmd(F("wwtemp"), [&](const char * value, const int8_t id) { return set_warmwater_temp(value, id); });
+ register_mqtt_cmd(F("heatingactivated"), [&](const char * value, const int8_t id) { return set_heating_activated(value, id); });
+ register_mqtt_cmd(F("heatingtemp"), [&](const char * value, const int8_t id) { return set_heating_temp(value, id); });
+ register_mqtt_cmd(F("burnmaxpower"), [&](const char * value, const int8_t id) { return set_max_power(value, id); });
+ register_mqtt_cmd(F("burnminpower"), [&](const char * value, const int8_t id) { return set_min_power(value, id); });
+ register_mqtt_cmd(F("boilhyston"), [&](const char * value, const int8_t id) { return set_hyst_on(value, id); });
+ register_mqtt_cmd(F("boilhystoff"), [&](const char * value, const int8_t id) { return set_hyst_off(value, id); });
+ register_mqtt_cmd(F("burnperiod"), [&](const char * value, const int8_t id) { return set_burn_period(value, id); });
+ register_mqtt_cmd(F("pumpdelay"), [&](const char * value, const int8_t id) { return set_pump_delay(value, id); });
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
mqtt_format_ = settings.mqtt_format; // single, nested or ha
@@ -85,7 +85,6 @@ void Boiler::add_context_menu() {
flash_string_vector{F_(boiler)},
[&](Shell & shell, const std::vector & arguments __attribute__((unused))) {
Boiler::console_commands(shell, ShellContext::BOILER);
- add_context_commands(ShellContext::BOILER);
});
}
@@ -765,16 +764,11 @@ void Boiler::process_UBAMaintenanceData(std::shared_ptr telegram
}
}
-/*
- * Commands
- */
-
-
// Set the warm water temperature 0x33
-void Boiler::set_warmwater_temp(const char * value, const int8_t id) {
+bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler warm water temperature to %d C"), v);
@@ -784,24 +778,28 @@ void Boiler::set_warmwater_temp(const char * value, const int8_t id) {
write_command(EMS_TYPE_UBAParameterWW, 2, v, EMS_TYPE_UBAParameterWW);
write_command(EMS_TYPE_UBAFlags, 3, v, EMS_TYPE_UBAParameterWW); // for i9000, see #397
}
+
+ return true;
}
// flow temp
-void Boiler::set_flow_temp(const char * value, const int8_t id) {
+bool Boiler::set_flow_temp(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler flow temperature to %d C"), v);
write_command(EMS_TYPE_UBASetPoints, 0, v, EMS_TYPE_UBASetPoints);
+
+ return true;
}
// set min boiler output
-void Boiler::set_heating_activated(const char * value, const int8_t id) {
+bool Boiler::set_heating_activated(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler heating "), v ? "on" : "off");
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
@@ -809,41 +807,49 @@ void Boiler::set_heating_activated(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAParameters, 0, v ? 0xFF : 0, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set heating maximum temperature
-void Boiler::set_heating_temp(const char * value, const int8_t id) {
+bool Boiler::set_heating_temp(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
+
LOG_INFO(F("Setting boiler heating temperature to "), v);
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
write_command(EMS_TYPE_UBAParametersPlus, 1, v, EMS_TYPE_UBAParametersPlus);
} else {
write_command(EMS_TYPE_UBAParameters, 1, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set min boiler output
-void Boiler::set_min_power(const char * value, const int8_t id) {
+bool Boiler::set_min_power(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
+
LOG_INFO(F("Setting boiler min power to "), v);
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
write_command(EMS_TYPE_UBAParametersPlus, 7, v, EMS_TYPE_UBAParametersPlus);
} else {
write_command(EMS_TYPE_UBAParameters, 3, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set max temp
-void Boiler::set_max_power(const char * value, const int8_t id) {
+bool Boiler::set_max_power(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler max power to %d C"), v);
@@ -852,13 +858,15 @@ void Boiler::set_max_power(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAParameters, 2, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set boiler on hysteresis
-void Boiler::set_hyst_on(const char * value, const int8_t id) {
+bool Boiler::set_hyst_on(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler hysteresis on to %d C"), v);
@@ -867,13 +875,15 @@ void Boiler::set_hyst_on(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAParameters, 5, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set boiler off hysteresis
-void Boiler::set_hyst_off(const char * value, const int8_t id) {
+bool Boiler::set_hyst_off(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler hysteresis off to %d C"), v);
@@ -882,13 +892,15 @@ void Boiler::set_hyst_off(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAParameters, 4, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set min burner period
-void Boiler::set_burn_period(const char * value, const int8_t id) {
+bool Boiler::set_burn_period(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting burner min. period to %d min"), v);
@@ -897,30 +909,36 @@ void Boiler::set_burn_period(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAParameters, 6, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// set pump delay
-void Boiler::set_pump_delay(const char * value, const int8_t id) {
+bool Boiler::set_pump_delay(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
- return;
+ return false;
}
if (get_toggle_fetch(EMS_TYPE_UBAParameters)) {
LOG_INFO(F("Setting boiler pump delay to %d min"), v);
write_command(EMS_TYPE_UBAParameters, 8, v, EMS_TYPE_UBAParameters);
}
+
+ return true;
}
// note some boilers do not have this setting, than it's done by thermostat
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3) 1=hot, 2=eco, 3=intelligent
-void Boiler::set_warmwater_mode(const char * value, const int8_t id) {
+bool Boiler::set_warmwater_mode(const char * value, const int8_t id) {
if (value == nullptr) {
- return;
+ return false;
}
+
if (!get_toggle_fetch(EMS_TYPE_UBAParameterWW)) {
- return;
+ return false;
}
+
uint8_t set;
if (strcmp(value, "hot") == 0) {
LOG_INFO(F("Setting boiler warm water to Hot"));
@@ -932,16 +950,18 @@ void Boiler::set_warmwater_mode(const char * value, const int8_t id) {
LOG_INFO(F("Setting boiler warm water to Intelligent"));
set = 0xEC;
} else {
- return; // do nothing
+ return false; // do nothing
}
+
write_command(EMS_TYPE_UBAParameterWW, 9, set, EMS_TYPE_UBAParameterWW);
+ return true;
}
// turn on/off warm water
-void Boiler::set_warmwater_activated(const char * value, const int8_t id) {
+bool Boiler::set_warmwater_activated(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler warm water %s"), v ? "on" : "off");
@@ -953,19 +973,22 @@ void Boiler::set_warmwater_activated(const char * value, const int8_t id) {
} else {
n = (v ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
}
+
if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) {
write_command(EMS_TYPE_UBAParameterWWPlus, 1, v ? 1 : 0, EMS_TYPE_UBAParameterWWPlus);
} else {
write_command(EMS_TYPE_UBAParameterWW, 1, n, EMS_TYPE_UBAParameterWW);
}
+
+ return true;
}
// Activate / De-activate the Warm Tap Water
// Note: Using the type 0x1D to put the boiler into Test mode. This may be shown on the boiler with a flashing 'T'
-void Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) {
+bool Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting tap warm tap water %s"), v ? "on" : "off");
@@ -990,15 +1013,17 @@ void Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) {
}
write_command(EMS_TYPE_UBAFunctionTest, 0, message_data, sizeof(message_data), 0);
+
+ return true;
}
// Activate / De-activate One Time warm water 0x35
// true = on, false = off
// See also https://github.com/proddy/EMS-ESP/issues/341#issuecomment-596245458 for Junkers
-void Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
+bool Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler warm water OneTime loading %s"), v ? "on" : "off");
@@ -1007,14 +1032,16 @@ void Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02), 0x34);
}
+
+ return true;
}
// Activate / De-activate circulation of warm water 0x35
// true = on, false = off
-void Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
+bool Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
- return;
+ return false;
}
LOG_INFO(F("Setting boiler warm water circulation %s"), v ? "on" : "off");
@@ -1023,6 +1050,8 @@ void Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
} else {
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02), 0x34);
}
+
+ return true;
}
// add console commands
@@ -1034,7 +1063,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
[=](Shell & shell __attribute__((unused)), const std::vector & arguments) {
uint16_t type_id = Helpers::hextoint(arguments.front().c_str());
EMSESP::set_read_id(type_id);
- EMSESP::send_read_request(type_id, get_device_id());
+ EMSESP::send_read_request(type_id, device_id());
});
EMSESPShell::commands->add_command(ShellContext::BOILER,
diff --git a/src/devices/boiler.h b/src/devices/boiler.h
index 49ab43024..d1c63d244 100644
--- a/src/devices/boiler.h
+++ b/src/devices/boiler.h
@@ -167,21 +167,21 @@ class Boiler : public EMSdevice {
void process_UBADHWStatus(std::shared_ptr telegram);
// commands - none of these use the additional id parameter
- void set_warmwater_mode(const char * value, const int8_t id);
- void set_warmwater_activated(const char * value, const int8_t id);
- void set_tapwarmwater_activated(const char * value, const int8_t id);
- void set_warmwater_onetime(const char * value, const int8_t id);
- void set_warmwater_circulation(const char * value, const int8_t id);
- void set_warmwater_temp(const char * value, const int8_t id);
- void set_flow_temp(const char * value, const int8_t id);
- void set_heating_activated(const char * value, const int8_t id);
- void set_heating_temp(const char * value, const int8_t id);
- void set_min_power(const char * value, const int8_t id);
- void set_max_power(const char * value, const int8_t id);
- void set_hyst_on(const char * value, const int8_t id);
- void set_hyst_off(const char * value, const int8_t id);
- void set_burn_period(const char * value, const int8_t id);
- void set_pump_delay(const char * value, const int8_t id);
+ bool set_warmwater_mode(const char * value, const int8_t id);
+ bool set_warmwater_activated(const char * value, const int8_t id);
+ bool set_tapwarmwater_activated(const char * value, const int8_t id);
+ bool set_warmwater_onetime(const char * value, const int8_t id);
+ bool set_warmwater_circulation(const char * value, const int8_t id);
+ bool set_warmwater_temp(const char * value, const int8_t id);
+ bool set_flow_temp(const char * value, const int8_t id);
+ bool set_heating_activated(const char * value, const int8_t id);
+ bool set_heating_temp(const char * value, const int8_t id);
+ bool set_min_power(const char * value, const int8_t id);
+ bool set_max_power(const char * value, const int8_t id);
+ bool set_hyst_on(const char * value, const int8_t id);
+ bool set_hyst_off(const char * value, const int8_t id);
+ bool set_burn_period(const char * value, const int8_t id);
+ bool set_pump_delay(const char * value, const int8_t id);
};
} // namespace emsesp
diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp
index ee8077761..2026c158f 100644
--- a/src/devices/mixing.cpp
+++ b/src/devices/mixing.cpp
@@ -158,7 +158,7 @@ void Mixing::publish_values() {
char topic[30];
strlcpy(topic, "mixing_data", 30);
- strlcat(topic, Helpers::itoa(s, get_device_id() - 0x20 + 1), 30); // append hc to topic
+ strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic
Mqtt::publish(topic, doc);
}
@@ -190,7 +190,7 @@ void Mixing::process_MMPLUSStatusMessage_WWC(std::shared_ptr tel
// A1 00 FF 00 00 0C 02 04 00 01 1D 00 82
void Mixing::process_IPMStatusMessage(std::shared_ptr telegram) {
type_ = Type::HC;
- hc_ = get_device_id() - 0x20 + 1;
+ hc_ = device_id() - 0x20 + 1;
uint8_t ismixed = 0;
changed_ |= telegram->read_value(ismixed, 0); // check if circuit is active, 0-off, 1-unmixed, 2-mixed
if (ismixed == 0) {
@@ -213,7 +213,7 @@ void Mixing::process_MMStatusMessage(std::shared_ptr telegram) {
// the heating circuit is determine by which device_id it is, 0x20 - 0x23
// 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module
// see https://github.com/proddy/EMS-ESP/issues/270 and https://github.com/proddy/EMS-ESP/issues/386#issuecomment-629610918
- hc_ = get_device_id() - 0x20 + 1;
+ hc_ = device_id() - 0x20 + 1;
changed_ |= telegram->read_value(flowTemp_, 1); // is * 10
changed_ |= telegram->read_value(pump_, 3);
changed_ |= telegram->read_value(flowSetTemp_, 0);
@@ -226,7 +226,7 @@ void Mixing::process_MMStatusMessage(std::shared_ptr telegram) {
// Mixing on a MM10 - 0xAA
// e.g. Thermostat -> Mixing Module, type 0xAA, telegram: 10 21 AA 00 FF 0C 0A 11 0A 32 xx
void Mixing::process_MMConfigMessage(std::shared_ptr telegram) {
- hc_ = get_device_id() - 0x20 + 1;
+ hc_ = device_id() - 0x20 + 1;
// pos 0: active FF = on
// pos 1: valve runtime 0C = 120 sec in units of 10 sec
}
@@ -234,7 +234,7 @@ void Mixing::process_MMConfigMessage(std::shared_ptr telegram) {
// Mixing on a MM10 - 0xAC
// e.g. Thermostat -> Mixing Module, type 0xAC, telegram: 10 21 AC 00 1E 64 01 AB
void Mixing::process_MMSetMessage(std::shared_ptr telegram) {
- hc_ = get_device_id() - 0x20 + 1;
+ hc_ = device_id() - 0x20 + 1;
// pos 0: flowtemp setpoint 1E = 30°C
// pos 1: position in %
}
diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp
index 834c824d7..fbb205de9 100644
--- a/src/devices/thermostat.cpp
+++ b/src/devices/thermostat.cpp
@@ -222,7 +222,7 @@ void Thermostat::device_info_web(JsonArray & root) {
// context menu "thermostat"
void Thermostat::add_context_menu() {
// only add it once, to prevent conflicts when there are multiple thermostats
- if (this->get_device_id() != EMSESP::actual_master_thermostat()) {
+ if (this->device_id() != EMSESP::actual_master_thermostat()) {
return;
}
@@ -231,7 +231,6 @@ void Thermostat::add_context_menu() {
flash_string_vector{F_(thermostat)},
[&](Shell & shell, const std::vector & arguments __attribute__((unused))) {
Thermostat::console_commands(shell, ShellContext::THERMOSTAT);
- add_context_commands(ShellContext::THERMOSTAT);
});
}
@@ -239,7 +238,7 @@ void Thermostat::add_context_menu() {
// we check if any of the thermostat values have changed and then republish if necessary
bool Thermostat::updated_values() {
// only publish on the master thermostat
- if (EMSESP::actual_master_thermostat() != this->get_device_id()) {
+ if (EMSESP::actual_master_thermostat() != this->device_id()) {
return false;
}
if (changed_) {
@@ -252,7 +251,7 @@ bool Thermostat::updated_values() {
// publish values via MQTT
void Thermostat::publish_values() {
// only publish on the master thermostat
- if (EMSESP::actual_master_thermostat() != this->get_device_id()) {
+ if (EMSESP::actual_master_thermostat() != this->device_id()) {
return;
}
@@ -1191,7 +1190,7 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
[=](Shell & shell __attribute__((unused)), const std::vector & arguments) {
uint16_t type_id = Helpers::hextoint(arguments.front().c_str());
EMSESP::set_read_id(type_id);
- EMSESP::send_read_request(type_id, this->get_device_id());
+ EMSESP::send_read_request(type_id, this->device_id());
});
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
@@ -1217,50 +1216,61 @@ void Thermostat::console_commands(Shell & shell, unsigned int context) {
}
// 0xA5 - Set minimum external temperature
-void Thermostat::set_minexttemp(const char * value, const int8_t id) {
+bool Thermostat::set_minexttemp(const char * value, const int8_t id) {
int mt = 0;
if (!Helpers::value2number(value, mt)) {
- return;
+ return false;
}
+
LOG_INFO(F("Setting min external temperature to %d"), mt);
write_command(EMS_TYPE_IBASettings, 5, mt, EMS_TYPE_IBASettings);
+
+ return true;
}
// 0xA5 - Clock offset
-void Thermostat::set_clockoffset(const char * value, const int8_t id) {
+bool Thermostat::set_clockoffset(const char * value, const int8_t id) {
int co = 0;
if (!Helpers::value2number(value, co)) {
- return;
+ return false;
}
+
LOG_INFO(F("Setting clock offset to %d"), co);
write_command(EMS_TYPE_IBASettings, 12, co, EMS_TYPE_IBASettings);
+
+ return true;
}
// 0xA5 - Calibrate internal temperature
-void Thermostat::set_calinttemp(const char * value, const int8_t id) {
+bool Thermostat::set_calinttemp(const char * value, const int8_t id) {
int ct = 0;
if (!Helpers::value2number(value, ct)) {
- return;
+ return false;
}
- // does this value need to be multiple by 10?
+
LOG_INFO(F("Calibrating internal temperature to %d.%d"), ct / 10, ct < 0 ? -ct % 10 : ct % 10);
write_command(EMS_TYPE_IBASettings, 2, ct, EMS_TYPE_IBASettings);
+
+ return true;
}
// 0xA5 - Set the display settings
-void Thermostat::set_display(const char * value, const int8_t id) {
+bool Thermostat::set_display(const char * value, const int8_t id) {
int ds = 0;
if (!Helpers::value2number(value, ds)) {
- return;
+ return false;
}
+
LOG_INFO(F("Setting display to %d"), ds);
write_command(EMS_TYPE_IBASettings, 0, ds, EMS_TYPE_IBASettings);
+
+ return true;
}
-void Thermostat::set_remotetemp(const char * value, const int8_t id) {
+bool Thermostat::set_remotetemp(const char * value, const int8_t id) {
float f = 0;
if (!Helpers::value2float(value, f)) {
- return;
+ return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
@@ -1270,13 +1280,15 @@ void Thermostat::set_remotetemp(const char * value, const int8_t id) {
} else {
Roomctrl::set_remotetemp(hc_num - 1, (int16_t)(f * 10));
}
+
+ return true;
}
// 0xA5 - Set the building settings
-void Thermostat::set_building(const char * value, const int8_t id) {
+bool Thermostat::set_building(const char * value, const int8_t id) {
std::string bd(20, '\0');
if (!Helpers::value2string(value, bd)) {
- return;
+ return false;
}
uint8_t bg = 0;
@@ -1287,28 +1299,33 @@ void Thermostat::set_building(const char * value, const int8_t id) {
} else if (bd == "heavy") {
bg = 2;
} else {
- return; // invalid
+ return false; // invalid
}
LOG_INFO(F("Setting building to %d"), bg);
write_command(EMS_TYPE_IBASettings, 6, bg, EMS_TYPE_IBASettings);
+
+ return true;
}
// 0xA5 Set the language settings
-void Thermostat::set_language(const char * value, const int8_t id) {
+bool Thermostat::set_language(const char * value, const int8_t id) {
int lg = 0;
if (!Helpers::value2number(value, lg)) {
- return;
+ return false;
}
+
LOG_INFO(F("Setting language to %d"), lg);
write_command(EMS_TYPE_IBASettings, 1, lg, EMS_TYPE_IBASettings);
+
+ return true;
}
// Set the control-mode for hc 0-off, 1-RC20, 2-RC3x
-void Thermostat::set_control(const char * value, const int8_t id) {
+bool Thermostat::set_control(const char * value, const int8_t id) {
int ctrl = 0;
if (!Helpers::value2number(value, ctrl)) {
- return;
+ return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
@@ -1316,23 +1333,25 @@ void Thermostat::set_control(const char * value, const int8_t id) {
std::shared_ptr hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("Set control: Heating Circuit %d not found or activated"), hc_num);
- return;
+ return false;
}
if (ctrl > 2) {
LOG_WARNING(F("Set control: Invalid control mode: %d"), ctrl);
- return;
+ return false;
}
LOG_INFO(F("Setting circuit-control for hc%d to %d"), hc_num, ctrl);
write_command(set_typeids[hc->hc_num() - 1], 26, ctrl);
+
+ return true;
}
// sets the thermostat ww working mode, where mode is a string
-void Thermostat::set_wwmode(const char * value, const int8_t id) {
+bool Thermostat::set_wwmode(const char * value, const int8_t id) {
std::string v(10, '\0');
if (!Helpers::value2string(value, v)) {
- return;
+ return false;
}
uint8_t set = 0xFF; // some dummy value
@@ -1350,21 +1369,24 @@ void Thermostat::set_wwmode(const char * value, const int8_t id) {
} else {
LOG_WARNING(F("Set thermostat warm water mode: Invalid mode: %s"), v.c_str());
}
+
+ return true;
}
// set the holiday as string dd.mm.yyyy-dd.mm.yyyy
-void Thermostat::set_holiday(const char * value, const int8_t id) {
+bool Thermostat::set_holiday(const char * value, const int8_t id) {
std::string hd(30, '\0');
if (!Helpers::value2string(value, hd)) {
- return;
+ return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr hc = heating_circuit(hc_num);
if (hc == nullptr) {
- LOG_WARNING(F("Set holiday: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->get_device_id());
- return;
+ LOG_WARNING(F("Set holiday: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->device_id());
+ return false;
}
+
uint8_t data[6];
data[0] = (hd[0] - '0') * 10 + (hd[1] - '0');
data[1] = (hd[3] - '0') * 10 + (hd[4] - '0');
@@ -1375,49 +1397,56 @@ void Thermostat::set_holiday(const char * value, const int8_t id) {
LOG_INFO(F("Setting holiday for hc %d"), hc->hc_num());
write_command(timer_typeids[hc->hc_num() - 1], 87, data, 6, 0);
+
+ return true;
}
// set pause in hours
-void Thermostat::set_pause(const char * value, const int8_t id) {
+bool Thermostat::set_pause(const char * value, const int8_t id) {
int hrs = 0;
if (!Helpers::value2number(value, hrs)) {
- return;
+ return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr hc = heating_circuit(hc_num);
if (hc == nullptr) {
- LOG_WARNING(F("Set pause: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->get_device_id());
- return;
+ LOG_WARNING(F("Set pause: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->device_id());
+ return false;
}
+
LOG_INFO(F("Setting pause: %d hours, hc: %d"), hrs, hc->hc_num());
write_command(timer_typeids[hc->hc_num() - 1], 85, hrs);
+
+ return true;
}
// set partymode in hours
-void Thermostat::set_party(const char * value, const int8_t id) {
+bool Thermostat::set_party(const char * value, const int8_t id) {
int hrs = 0;
if (!Helpers::value2number(value, hrs)) {
- return;
+ return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr hc = heating_circuit(hc_num);
if (hc == nullptr) {
- LOG_WARNING(F("Set party: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->get_device_id());
- return;
+ LOG_WARNING(F("Set party: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->device_id());
+ return false;
}
LOG_INFO(F("Setting party: %d hours, hc: %d"), hrs, hc->hc_num());
write_command(timer_typeids[hc->hc_num() - 1], 86, hrs);
+
+ return true;
}
// set date&time as string hh:mm:ss-dd.mm.yyyy-dw-dst or "NTP" for setting to internet-time
// dw - day of week (0..6), dst- summertime (0/1)
// id is ignored
-void Thermostat::set_datetime(const char * value, const int8_t id) {
+bool Thermostat::set_datetime(const char * value, const int8_t id) {
std::string dt(30, '\0');
if (!Helpers::value2string(value, dt)) {
- return;
+ return false;
}
uint8_t data[9];
@@ -1426,8 +1455,9 @@ void Thermostat::set_datetime(const char * value, const int8_t id) {
tm * tm_ = localtime(&now);
if (tm_->tm_year < 110) { // no NTP time
LOG_WARNING(F("No NTP time. Cannot set RCtime"));
- return;
+ return false;
}
+
data[0] = tm_->tm_year - 100; // Bosch counts from 2000
data[1] = tm_->tm_mon;
data[2] = tm_->tm_hour;
@@ -1449,53 +1479,65 @@ void Thermostat::set_datetime(const char * value, const int8_t id) {
data[6] = (dt[20] - '0'); // day of week
data[7] = (dt[22] - '0') + 2; // DST and flag
}
+
LOG_INFO(F("Setting date and time"));
write_command(EMS_TYPE_time, 0, data, 8, EMS_TYPE_time);
+
+ return true;
}
// sets the thermostat working mode, where mode is a string
// converts string mode to HeatingCircuit::Mode
-void Thermostat::set_mode(const char * value, const int8_t id) {
+bool Thermostat::set_mode(const char * value, const int8_t id) {
std::string mode(10, '\0');
if (!Helpers::value2string(value, mode)) {
- return;
+ return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
-
if (mode_tostring(HeatingCircuit::Mode::OFF) == mode) {
- set_mode_n(HeatingCircuit::Mode::OFF, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::MANUAL) == mode) {
- set_mode_n(HeatingCircuit::Mode::MANUAL, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::AUTO) == mode) {
- set_mode_n(HeatingCircuit::Mode::AUTO, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::DAY) == mode) {
- set_mode_n(HeatingCircuit::Mode::DAY, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::NIGHT) == mode) {
- set_mode_n(HeatingCircuit::Mode::NIGHT, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::HEAT) == mode) {
- set_mode_n(HeatingCircuit::Mode::HEAT, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::NOFROST) == mode) {
- set_mode_n(HeatingCircuit::Mode::NOFROST, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::ECO) == mode) {
- set_mode_n(HeatingCircuit::Mode::ECO, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
- set_mode_n(HeatingCircuit::Mode::HOLIDAY, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::COMFORT) == mode) {
- set_mode_n(HeatingCircuit::Mode::COMFORT, hc_num);
- } else {
- LOG_WARNING(F("Invalid mode %s. Cannot set"), mode.c_str());
+ return set_mode_n(HeatingCircuit::Mode::OFF, hc_num);
}
+ if (mode_tostring(HeatingCircuit::Mode::MANUAL) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::MANUAL, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::AUTO) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::AUTO, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::DAY) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::DAY, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::NIGHT) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::NIGHT, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::HEAT) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::HEAT, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::NOFROST) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::NOFROST, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::ECO) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::ECO, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::HOLIDAY, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::COMFORT) == mode) {
+ return set_mode_n(HeatingCircuit::Mode::COMFORT, hc_num);
+ }
+
+ LOG_WARNING(F("Invalid mode %s. Cannot set"), mode.c_str());
+ return false;
}
// Set the thermostat working mode
// mode is HeatingCircuit::Mode
-void Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
+bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
// get hc based on number
std::shared_ptr hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("set mode: Heating Circuit %d not found or activated"), hc_num);
- return;
+ return false;
}
uint8_t set_mode_value, offset;
@@ -1578,47 +1620,61 @@ void Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
// add the write command to the Tx queue
// post validate is the corresponding monitor or set type IDs as they can differ per model
write_command(set_typeids[hc->hc_num() - 1], offset, set_mode_value, validate_typeid);
+
+ return true;
}
// sets the thermostat temp, where mode is a string
-void Thermostat::set_temperature(const float temperature, const std::string & mode, const uint8_t hc_num) {
+bool Thermostat::set_temperature(const float temperature, const std::string & mode, const uint8_t hc_num) {
if (mode_tostring(HeatingCircuit::Mode::MANUAL) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::MANUAL, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::AUTO) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::AUTO, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::DAY) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::DAY, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::NIGHT) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::NIGHT, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::COMFORT) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::COMFORT, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::HEAT) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::HEAT, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::ECO) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::ECO, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::NOFROST) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::NOFROST, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::SUMMER) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::SUMMER, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::HOLIDAY, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::OFFSET) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::OFFSET, hc_num);
- } else if (mode_tostring(HeatingCircuit::Mode::DESIGN) == mode) {
- set_temperature(temperature, HeatingCircuit::Mode::DESIGN, hc_num);
- } else {
- LOG_WARNING(F("Invalid mode %s."), mode.c_str());
+ return set_temperature(temperature, HeatingCircuit::Mode::MANUAL, hc_num);
}
+ if (mode_tostring(HeatingCircuit::Mode::AUTO) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::AUTO, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::DAY) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::DAY, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::NIGHT) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::NIGHT, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::COMFORT) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::COMFORT, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::HEAT) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::HEAT, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::ECO) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::ECO, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::NOFROST) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::NOFROST, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::SUMMER) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::SUMMER, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::HOLIDAY, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::OFFSET) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::OFFSET, hc_num);
+ }
+ if (mode_tostring(HeatingCircuit::Mode::DESIGN) == mode) {
+ return set_temperature(temperature, HeatingCircuit::Mode::DESIGN, hc_num);
+ }
+
+ LOG_WARNING(F("Invalid mode %s."), mode.c_str());
+ return false;
}
// Set the temperature of the thermostat
// the id passed into this function is the heating circuit number
-void Thermostat::set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num) {
+bool Thermostat::set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num) {
// get hc based on number
std::shared_ptr hc = heating_circuit(hc_num);
if (hc == nullptr) {
- LOG_WARNING(F("Set temperature: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->get_device_id());
- return;
+ LOG_WARNING(F("Set temperature: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, this->device_id());
+ return false;
}
uint8_t model = this->model();
@@ -1790,75 +1846,80 @@ void Thermostat::set_temperature(const float temperature, const uint8_t mode, co
// add the write command to the Tx queue. value is *2
// post validate is the corresponding monitor or set type IDs as they can differ per model
write_command(set_typeids[hc->hc_num() - 1], offset, (uint8_t)((float)temperature * (float)factor), validate_typeid);
+ return true;
}
+
+ return false;
}
// for HA specifically when receiving over MQTT
-void Thermostat::thermostat_cmd_temp(const char * message) {
+bool Thermostat::thermostat_cmd_temp(const char * message) {
float f = strtof((char *)message, 0);
- set_temperature(f, HeatingCircuit::Mode::AUTO, AUTO_HEATING_CIRCUIT);
+ return set_temperature(f, HeatingCircuit::Mode::AUTO, AUTO_HEATING_CIRCUIT);
}
// for HA specifically when receiving over MQTT
// message payload holds the text name of the mode e.g. "auto"
-void Thermostat::thermostat_cmd_mode(const char * message) {
- set_mode(message, AUTO_HEATING_CIRCUIT);
+bool Thermostat::thermostat_cmd_mode(const char * message) {
+ return set_mode(message, AUTO_HEATING_CIRCUIT);
}
-void Thermostat::set_temperature_value(const char * value, const int8_t id, const uint8_t mode) {
+bool Thermostat::set_temperature_value(const char * value, const int8_t id, const uint8_t mode) {
float f = 0;
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
if (Helpers::value2float(value, f)) {
- set_temperature(f, mode, hc_num);
+ return set_temperature(f, mode, hc_num);
+ } else {
+ return false;
}
}
-void Thermostat::set_temp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::AUTO);
+bool Thermostat::set_temp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::AUTO);
}
-void Thermostat::set_nighttemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::NIGHT);
+bool Thermostat::set_nighttemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::NIGHT);
}
-void Thermostat::set_daytemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::DAY);
+bool Thermostat::set_daytemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::DAY);
}
-void Thermostat::set_comforttemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::COMFORT);
+bool Thermostat::set_comforttemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::COMFORT);
}
-void Thermostat::set_nofrosttemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::NOFROST);
+bool Thermostat::set_nofrosttemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::NOFROST);
}
-void Thermostat::set_ecotemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::ECO);
+bool Thermostat::set_ecotemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::ECO);
}
-void Thermostat::set_heattemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::HEAT);
+bool Thermostat::set_heattemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::HEAT);
}
-void Thermostat::set_summertemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::SUMMER);
+bool Thermostat::set_summertemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::SUMMER);
}
-void Thermostat::set_designtemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::DESIGN);
+bool Thermostat::set_designtemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::DESIGN);
}
-void Thermostat::set_offsettemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::OFFSET);
+bool Thermostat::set_offsettemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::OFFSET);
}
-void Thermostat::set_holidaytemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::HOLIDAY);
+bool Thermostat::set_holidaytemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::HOLIDAY);
}
-void Thermostat::set_manualtemp(const char * value, const int8_t id) {
- set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL);
+bool Thermostat::set_manualtemp(const char * value, const int8_t id) {
+ return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL);
}
// commands for MQTT and Console
@@ -1869,48 +1930,48 @@ void Thermostat::add_commands() {
}
// common to all thermostats
- register_mqtt_cmd(F("wwmode"), [&](const char * value, const int8_t id) { set_wwmode(value, id); });
- register_mqtt_cmd(F("temp"), [&](const char * value, const int8_t id) { set_temp(value, id); });
- register_mqtt_cmd(F("mode"), [&](const char * value, const int8_t id) { set_mode(value, id); });
+ register_mqtt_cmd(F("wwmode"), [&](const char * value, const int8_t id) { return set_wwmode(value, id); });
+ register_mqtt_cmd(F("temp"), [&](const char * value, const int8_t id) { return set_temp(value, id); });
+ register_mqtt_cmd(F("mode"), [&](const char * value, const int8_t id) { return set_mode(value, id); });
uint8_t model = this->model();
switch (model) {
case EMS_DEVICE_FLAG_RC100:
case EMS_DEVICE_FLAG_RC300:
- register_mqtt_cmd(F("manualtemp"), [&](const char * value, const int8_t id) { set_manualtemp(value, id); });
- register_mqtt_cmd(F("ecotemp"), [&](const char * value, const int8_t id) { set_ecotemp(value, id); });
- register_mqtt_cmd(F("comforttemp"), [&](const char * value, const int8_t id) { set_comforttemp(value, id); });
+ register_mqtt_cmd(F("manualtemp"), [&](const char * value, const int8_t id) { return set_manualtemp(value, id); });
+ register_mqtt_cmd(F("ecotemp"), [&](const char * value, const int8_t id) { return set_ecotemp(value, id); });
+ register_mqtt_cmd(F("comforttemp"), [&](const char * value, const int8_t id) { return set_comforttemp(value, id); });
break;
case EMS_DEVICE_FLAG_RC20_2:
- register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { set_nighttemp(value, id); });
- register_mqtt_cmd(F("daytemp"), [&](const char * value, const int8_t id) { set_daytemp(value, id); });
+ register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { return set_nighttemp(value, id); });
+ register_mqtt_cmd(F("daytemp"), [&](const char * value, const int8_t id) { return set_daytemp(value, id); });
break;
case EMS_DEVICE_FLAG_RC30_1: // only RC30_1
- register_mqtt_cmd(F("clockoffset"), [&](const char * value, const int8_t id) { set_clockoffset(value, id); });
- register_mqtt_cmd(F("language"), [&](const char * value, const int8_t id) { set_language(value, id); });
- register_mqtt_cmd(F("display"), [&](const char * value, const int8_t id) { set_display(value, id); });
+ register_mqtt_cmd(F("clockoffset"), [&](const char * value, const int8_t id) { return set_clockoffset(value, id); });
+ register_mqtt_cmd(F("language"), [&](const char * value, const int8_t id) { return set_language(value, id); });
+ register_mqtt_cmd(F("display"), [&](const char * value, const int8_t id) { return set_display(value, id); });
case EMS_DEVICE_FLAG_RC35: // RC30 and RC35
- register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { set_nighttemp(value, id); });
- register_mqtt_cmd(F("daytemp"), [&](const char * value, const int8_t id) { set_daytemp(value, id); });
- register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { set_nofrosttemp(value, id); });
- register_mqtt_cmd(F("remotetemp"), [&](const char * value, const int8_t id) { set_remotetemp(value, id); });
- register_mqtt_cmd(F("datetime"), [&](const char * value, const int8_t id) { set_datetime(value, id); });
- register_mqtt_cmd(F("minexttemp"), [&](const char * value, const int8_t id) { set_minexttemp(value, id); });
- register_mqtt_cmd(F("calinttemp"), [&](const char * value, const int8_t id) { set_calinttemp(value, id); });
- register_mqtt_cmd(F("building"), [&](const char * value, const int8_t id) { set_building(value, id); });
- register_mqtt_cmd(F("control"), [&](const char * value, const int8_t id) { set_control(value, id); });
- register_mqtt_cmd(F("pause"), [&](const char * value, const int8_t id) { set_pause(value, id); });
- register_mqtt_cmd(F("party"), [&](const char * value, const int8_t id) { set_party(value, id); });
- register_mqtt_cmd(F("holiday"), [&](const char * value, const int8_t id) { set_holiday(value, id); });
- register_mqtt_cmd(F("summertemp"), [&](const char * value, const int8_t id) { set_summertemp(value, id); });
- register_mqtt_cmd(F("designtemp"), [&](const char * value, const int8_t id) { set_designtemp(value, id); });
- register_mqtt_cmd(F("offsettemp"), [&](const char * value, const int8_t id) { set_offsettemp(value, id); });
- register_mqtt_cmd(F("holidaytemp"), [&](const char * value, const int8_t id) { set_holidaytemp(value, id); });
+ register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { return set_nighttemp(value, id); });
+ register_mqtt_cmd(F("daytemp"), [&](const char * value, const int8_t id) { return set_daytemp(value, id); });
+ register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { return set_nofrosttemp(value, id); });
+ register_mqtt_cmd(F("remotetemp"), [&](const char * value, const int8_t id) { return set_remotetemp(value, id); });
+ register_mqtt_cmd(F("datetime"), [&](const char * value, const int8_t id) { return set_datetime(value, id); });
+ register_mqtt_cmd(F("minexttemp"), [&](const char * value, const int8_t id) { return set_minexttemp(value, id); });
+ register_mqtt_cmd(F("calinttemp"), [&](const char * value, const int8_t id) { return set_calinttemp(value, id); });
+ register_mqtt_cmd(F("building"), [&](const char * value, const int8_t id) { return set_building(value, id); });
+ register_mqtt_cmd(F("control"), [&](const char * value, const int8_t id) { return set_control(value, id); });
+ register_mqtt_cmd(F("pause"), [&](const char * value, const int8_t id) { return set_pause(value, id); });
+ register_mqtt_cmd(F("party"), [&](const char * value, const int8_t id) { return set_party(value, id); });
+ register_mqtt_cmd(F("holiday"), [&](const char * value, const int8_t id) { return set_holiday(value, id); });
+ register_mqtt_cmd(F("summertemp"), [&](const char * value, const int8_t id) { return set_summertemp(value, id); });
+ register_mqtt_cmd(F("designtemp"), [&](const char * value, const int8_t id) { return set_designtemp(value, id); });
+ register_mqtt_cmd(F("offsettemp"), [&](const char * value, const int8_t id) { return set_offsettemp(value, id); });
+ register_mqtt_cmd(F("holidaytemp"), [&](const char * value, const int8_t id) { return set_holidaytemp(value, id); });
break;
case EMS_DEVICE_FLAG_JUNKERS:
- register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { set_nofrosttemp(value, id); });
- register_mqtt_cmd(F("ecotemp"), [&](const char * value, const int8_t id) { set_ecotemp(value, id); });
- register_mqtt_cmd(F("heattemp"), [&](const char * value, const int8_t id) { set_heattemp(value, id); });
+ register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { return set_nofrosttemp(value, id); });
+ register_mqtt_cmd(F("ecotemp"), [&](const char * value, const int8_t id) { return set_ecotemp(value, id); });
+ register_mqtt_cmd(F("heattemp"), [&](const char * value, const int8_t id) { return set_heattemp(value, id); });
break;
default:
break;
diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h
index a2f13bbfb..39fa79b60 100644
--- a/src/devices/thermostat.h
+++ b/src/devices/thermostat.h
@@ -246,48 +246,47 @@ class Thermostat : public EMSdevice {
void process_RC300WWmode(std::shared_ptr telegram);
// internal helper functions
- void set_mode_n(const uint8_t mode, const uint8_t hc_num);
+ bool set_mode_n(const uint8_t mode, const uint8_t hc_num);
- void set_temperature_value(const char * value, const int8_t id, const uint8_t mode);
- void set_temperature(const float temperature, const std::string & mode, const uint8_t hc_num);
- void set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num);
+ bool set_temperature_value(const char * value, const int8_t id, const uint8_t mode);
+ bool set_temperature(const float temperature, const std::string & mode, const uint8_t hc_num);
+ bool set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num);
// for HA specifically. MQTT functions.
- void thermostat_cmd_temp(const char * message);
- void thermostat_cmd_mode(const char * message);
+ bool thermostat_cmd_temp(const char * message);
+ bool thermostat_cmd_mode(const char * message);
// set functions - these use the id/hc
- void set_mode(const char * value, const int8_t id);
- void set_control(const char * value, const int8_t id);
- void set_holiday(const char * value, const int8_t id);
- void set_pause(const char * value, const int8_t id);
- void set_party(const char * value, const int8_t id);
+ bool set_mode(const char * value, const int8_t id);
+ bool set_control(const char * value, const int8_t id);
+ bool set_holiday(const char * value, const int8_t id);
+ bool set_pause(const char * value, const int8_t id);
+ bool set_party(const char * value, const int8_t id);
- void set_temp(const char * value, const int8_t id);
- void set_nighttemp(const char * value, const int8_t id);
- void set_daytemp(const char * value, const int8_t id);
- void set_comforttemp(const char * value, const int8_t id);
- void set_nofrosttemp(const char * value, const int8_t id);
- void set_ecotemp(const char * value, const int8_t id);
- void set_heattemp(const char * value, const int8_t id);
- void set_summertemp(const char * value, const int8_t id);
- void set_designtemp(const char * value, const int8_t id);
- void set_offsettemp(const char * value, const int8_t id);
- void set_holidaytemp(const char * value, const int8_t id);
- void set_manualtemp(const char * value, const int8_t id);
-
- void set_remotetemp(const char * value, const int8_t id);
+ bool set_temp(const char * value, const int8_t id);
+ bool set_nighttemp(const char * value, const int8_t id);
+ bool set_daytemp(const char * value, const int8_t id);
+ bool set_comforttemp(const char * value, const int8_t id);
+ bool set_nofrosttemp(const char * value, const int8_t id);
+ bool set_ecotemp(const char * value, const int8_t id);
+ bool set_heattemp(const char * value, const int8_t id);
+ bool set_summertemp(const char * value, const int8_t id);
+ bool set_designtemp(const char * value, const int8_t id);
+ bool set_offsettemp(const char * value, const int8_t id);
+ bool set_holidaytemp(const char * value, const int8_t id);
+ bool set_manualtemp(const char * value, const int8_t id);
+ bool set_remotetemp(const char * value, const int8_t id);
// set functions - these don't use the id/hc, the parameters are ignored
- void set_wwmode(const char * value, const int8_t id);
- void set_datetime(const char * value, const int8_t id);
- void set_minexttemp(const char * value, const int8_t id);
- void set_clockoffset(const char * value, const int8_t id);
- void set_calinttemp(const char * value, const int8_t id);
- void set_display(const char * value, const int8_t id);
- void set_building(const char * value, const int8_t id);
- void set_language(const char * value, const int8_t id);
-}; // namespace emsesp
+ bool set_wwmode(const char * value, const int8_t id);
+ bool set_datetime(const char * value, const int8_t id);
+ bool set_minexttemp(const char * value, const int8_t id);
+ bool set_clockoffset(const char * value, const int8_t id);
+ bool set_calinttemp(const char * value, const int8_t id);
+ bool set_display(const char * value, const int8_t id);
+ bool set_building(const char * value, const int8_t id);
+ bool set_language(const char * value, const int8_t id);
+};
} // namespace emsesp
diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp
index fbdced66c..af6fb9180 100644
--- a/src/emsdevice.cpp
+++ b/src/emsdevice.cpp
@@ -18,7 +18,6 @@
#include "emsdevice.h"
#include "emsesp.h"
-#include "mqtt.h" // for the mqtt_function_p
namespace emsesp {
@@ -54,30 +53,30 @@ std::string EMSdevice::brand_to_string() const {
}
// returns the name of the MQTT topic to use for a specific device
-std::string EMSdevice::device_type_topic_name(const uint8_t device_type) {
+std::string EMSdevice::device_type_2_device_name(const uint8_t device_type) {
switch (device_type) {
case DeviceType::SERVICEKEY:
- return read_flash_string(F("system_cmd"));
+ return read_flash_string(F("system"));
break;
case DeviceType::BOILER:
- return read_flash_string(F("boiler_cmd"));
+ return read_flash_string(F("boiler"));
break;
case DeviceType::THERMOSTAT:
- return read_flash_string(F("thermostat_cmd"));
+ return read_flash_string(F("thermostat"));
break;
case DeviceType::HEATPUMP:
- return read_flash_string(F("heatpump_cmd"));
+ return read_flash_string(F("heatpump"));
break;
case DeviceType::SOLAR:
- return read_flash_string(F("solar_cmd"));
+ return read_flash_string(F("solar"));
break;
case DeviceType::MIXING:
- return read_flash_string(F("mixing_cmd"));
+ return read_flash_string(F("mixing"));
break;
default:
@@ -86,6 +85,35 @@ std::string EMSdevice::device_type_topic_name(const uint8_t device_type) {
}
}
+// returns device_type from a string
+uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
+ if (strcmp(topic, "boiler") == 0) {
+ return DeviceType::BOILER;
+ }
+
+ if (strcmp(topic, "thermostat") == 0) {
+ return DeviceType::THERMOSTAT;
+ }
+
+ if (strcmp(topic, "system") == 0) {
+ return DeviceType::SERVICEKEY;
+ }
+
+ if (strcmp(topic, "heatpump") == 0) {
+ return DeviceType::HEATPUMP;
+ }
+
+ if (strcmp(topic, "solar") == 0) {
+ return DeviceType::SOLAR;
+ }
+
+ if (strcmp(topic, "mixing") == 0) {
+ return DeviceType::MIXING;
+ }
+
+ return DeviceType::UNKNOWN;
+}
+
std::string EMSdevice::device_type_name() const {
switch (device_type_) {
case DeviceType::BOILER:
@@ -202,7 +230,7 @@ void EMSdevice::show_values(uuid::console::Shell & shell) {
// for each telegram that has the fetch value set (true) do a read request
void EMSdevice::fetch_values() {
- LOG_DEBUG(F("Fetching values for device ID 0x%02X"), get_device_id());
+ LOG_DEBUG(F("Fetching values for device ID 0x%02X"), device_id());
for (const auto & tf : telegram_functions_) {
if (tf.fetch_) {
@@ -213,7 +241,7 @@ void EMSdevice::fetch_values() {
// toggle on/off automatic fetch for a telegram id
void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
- LOG_DEBUG(F("Toggling fetch for device ID 0x%02X, telegram ID 0x%02X to %d"), get_device_id(), telegram_id, toggle);
+ LOG_DEBUG(F("Toggling fetch for device ID 0x%02X, telegram ID 0x%02X to %d"), device_id(), telegram_id, toggle);
for (auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == telegram_id) {
@@ -226,7 +254,7 @@ void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
bool EMSdevice::get_toggle_fetch(uint16_t telegram_id) {
for (auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == telegram_id) {
- return tf.fetch_ ;
+ return tf.fetch_;
}
}
return false;
@@ -256,8 +284,9 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_
Mqtt::subscribe(this->device_type_, topic, f);
}
-void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, mqtt_cmdfunction_p f) {
- Mqtt::add_command(this->device_type_, this->device_id_, cmd, f);
+// add command to library
+void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f) {
+ Command::add_command(this->device_type_, this->device_id_, cmd, f);
}
// register a call back function for a specific telegram type
@@ -306,22 +335,22 @@ bool EMSdevice::handle_telegram(std::shared_ptr telegram) {
// send Tx write with a data block
void EMSdevice::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) {
- EMSESP::send_write_request(type_id, this->get_device_id(), offset, message_data, message_length, validate_typeid);
+ EMSESP::send_write_request(type_id, this->device_id(), offset, message_data, message_length, validate_typeid);
}
// send Tx write with a single value
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) {
- EMSESP::send_write_request(type_id, this->get_device_id(), offset, value, validate_typeid);
+ EMSESP::send_write_request(type_id, this->device_id(), offset, value, validate_typeid);
}
// send Tx write with a single value, with no post validation
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value) {
- EMSESP::send_write_request(type_id, this->get_device_id(), offset, value, 0);
+ EMSESP::send_write_request(type_id, this->device_id(), offset, value, 0);
}
// send Tx read command to the device
void EMSdevice::read_command(const uint16_t type_id) {
- EMSESP::send_read_request(type_id, get_device_id());
+ EMSESP::send_read_request(type_id, device_id());
}
// prints a string value to the console
@@ -339,60 +368,4 @@ void EMSdevice::print_value(uuid::console::Shell & shell, uint8_t padding, const
shell.printfln(PSTR("%s: %s"), uuid::read_flash_string(name).c_str(), value);
}
-// given a context, automatically add the commands taken them from the MQTT registry for "_cmd" topics
-void EMSdevice::add_context_commands(unsigned int context) {
- // if we're adding commands for a thermostat or mixing, then include an additional optional paramter called heating circuit
- flash_string_vector params;
- if ((context == ShellContext::THERMOSTAT) || (context == ShellContext::MIXING)) {
- params = flash_string_vector{F_(cmd_optional), F_(data_optional), F_(hc_optional)};
- } else {
- params = flash_string_vector{F_(cmd_optional), F_(data_optional)};
- }
-
- EMSESPShell::commands->add_command(
- context,
- CommandFlags::ADMIN,
- flash_string_vector{F_(call)},
- params,
- [&](Shell & shell, const std::vector & arguments) {
- uint8_t device_type_ = device_type();
- if (arguments.empty()) {
- // list options
- shell.print("Available commands:");
- for (const auto & cf : Mqtt::commands()) {
- if (cf.device_type_ == device_type_) {
- shell.printf(" %s", uuid::read_flash_string(cf.cmd_).c_str());
- }
- }
- shell.println();
- return;
- }
-
- const char * cmd = arguments[0].c_str();
- if (arguments.size() == 1) {
- // no value specified
- Mqtt::call_command(device_type_, cmd, nullptr, -1);
- } else if (arguments.size() == 2) {
- // has a value but no id
- Mqtt::call_command(device_type_, cmd, arguments.back().c_str(), -1);
- } else {
- // use value, which could be an id or hc
- Mqtt::call_command(device_type_, cmd, arguments[1].c_str(), atoi(arguments[2].c_str()));
- }
- },
- [&](Shell & shell __attribute__((unused)), const std::vector & arguments) -> std::vector {
- if (arguments.size() > 0) {
- return {};
- }
- std::vector commands;
- for (const auto & cf : Mqtt::commands()) {
- if (cf.device_type_ == device_type()) {
- commands.emplace_back(uuid::read_flash_string(cf.cmd_));
- }
- }
- return commands;
- });
-}
-
-
} // namespace emsesp
diff --git a/src/emsdevice.h b/src/emsdevice.h
index fdd697288..6293ed5ed 100644
--- a/src/emsdevice.h
+++ b/src/emsdevice.h
@@ -47,12 +47,13 @@ class EMSdevice {
virtual ~EMSdevice() = default; // destructor of base class must always be virtual because it's a polymorphic class
- inline uint8_t get_device_id() const {
+ inline uint8_t device_id() const {
return device_id_;
}
std::string device_type_name() const;
- static std::string device_type_topic_name(const uint8_t device_type);
+ static std::string device_type_2_device_name(const uint8_t device_type);
+ static uint8_t device_name_2_device_type(const char * topic);
inline uint8_t product_id() const {
return product_id_;
@@ -127,20 +128,17 @@ class EMSdevice {
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);
-
void read_command(const uint16_t type_id);
- void add_context_commands(unsigned int context);
-
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
- void register_mqtt_cmd(const __FlashStringHelper * cmd, mqtt_cmdfunction_p f);
+ void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f);
// virtual functions overrules by derived classes
virtual void show_values(uuid::console::Shell & shell) = 0;
virtual void publish_values() = 0;
virtual bool updated_values() = 0;
virtual void add_context_menu() = 0;
- virtual void device_info_web(JsonArray & root) = 0;
+ virtual void device_info_web(JsonArray & root) = 0;
std::string telegram_type_name(std::shared_ptr telegram);
@@ -230,7 +228,7 @@ class EMSdevice {
};
enum DeviceType : uint8_t {
- SERVICEKEY = 0, // this is us
+ SERVICEKEY = 0, // this is us (EMS-ESP)
BOILER,
THERMOSTAT,
MIXING,
@@ -239,8 +237,8 @@ class EMSdevice {
GATEWAY,
SWITCH,
CONTROLLER,
- CONNECT
-
+ CONNECT,
+ UNKNOWN
};
// device IDs
diff --git a/src/emsesp.cpp b/src/emsesp.cpp
index a422dbbaf..795da3842 100644
--- a/src/emsesp.cpp
+++ b/src/emsesp.cpp
@@ -36,6 +36,7 @@ EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&web
EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
+EMSESPAPIService EMSESP::emsespAPIService = EMSESPAPIService(&webServer);
using DeviceFlags = emsesp::EMSdevice;
using DeviceType = emsesp::EMSdevice::DeviceType;
@@ -615,7 +616,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
- if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->get_device_id() == actual_master_thermostat())) {
+ if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->device_id() == actual_master_thermostat())) {
shell.printf(F(" ** master device **"));
}
shell.println();
@@ -727,7 +728,7 @@ void EMSESP::send_write_request(const uint16_t type_id, const uint8_t dest, cons
// we check if its a complete telegram or just a single byte (which could be a poll or a return status)
// the CRC check is not done here, only when it's added to the Rx queue with add()
void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
-#ifdef EMSESP_DEBUG
+#ifdef EMSESP_UART_DEBUG
static uint32_t rx_time_ = 0;
#endif
// check first for echo
@@ -735,9 +736,9 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) {
// if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
-#ifdef EMSESP_DEBUG
+#ifdef EMSESP_UART_DEBUG
// get_uptime is only updated once per loop, does not give the right time
- LOG_TRACE(F("[DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
+ LOG_TRACE(F("[UART_DEBUG] Echo after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
#endif
return; // it's an echo
}
@@ -786,14 +787,14 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
if (length == 1) {
EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active
-#ifdef EMSESP_DEBUG
+#ifdef EMSESP_UART_DEBUG
char s[4];
if (first_value & 0x80) {
- LOG_TRACE(F("[DEBUG] next Poll %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
+ LOG_TRACE(F("[UART_DEBUG] next Poll %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
// time measurement starts here, use millis because get_uptime is only updated once per loop
rx_time_ = ::millis();
} else {
- LOG_TRACE(F("[DEBUG] Poll ack %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
+ LOG_TRACE(F("[UART_DEBUG] Poll ack %s after %d ms"), Helpers::hextoa(s, first_value), ::millis() - rx_time_);
}
#endif
// check for poll to us, if so send top message from Tx queue immediately and quit
@@ -805,8 +806,8 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
Roomctrl::send(first_value ^ 0x80 ^ rxservice_.ems_mask());
return;
} else {
-#ifdef EMSESP_DEBUG
- LOG_TRACE(F("[DEBUG] Reply after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
+#ifdef EMSESP_UART_DEBUG
+ LOG_TRACE(F("[UART_DEBUG] Reply after %d ms: %s"), ::millis() - rx_time_, Helpers::data_to_hex(data, length).c_str());
#endif
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); // check if there is a message for the roomcontroller
diff --git a/src/emsesp.h b/src/emsesp.h
index 479a4c35a..6bf913c5e 100644
--- a/src/emsesp.h
+++ b/src/emsesp.h
@@ -37,6 +37,7 @@
#include "EMSESPStatusService.h"
#include "EMSESPDevicesService.h"
#include "EMSESPSettingsService.h"
+#include "EMSESPAPIService.h"
#include "emsdevice.h"
#include "emsfactory.h"
@@ -47,6 +48,7 @@
#include "console.h"
#include "shower.h"
#include "roomcontrol.h"
+#include "command.h"
#define WATCH_ID_NONE 0 // no watch id set
@@ -162,6 +164,7 @@ class EMSESP {
static EMSESPSettingsService emsespSettingsService;
static EMSESPStatusService emsespStatusService;
static EMSESPDevicesService emsespDevicesService;
+ static EMSESPAPIService emsespAPIService;
static uuid::log::Logger logger() {
return logger_;
diff --git a/src/locale_EN.h b/src/locale_EN.h
index 548c21d76..169d18f6c 100644
--- a/src/locale_EN.h
+++ b/src/locale_EN.h
@@ -61,11 +61,20 @@ MAKE_PSTR_WORD(ssid)
MAKE_PSTR_WORD(heartbeat)
MAKE_PSTR_WORD(users)
MAKE_PSTR_WORD(master)
-MAKE_PSTR_WORD(test)
MAKE_PSTR_WORD(pin)
+#if defined(EMSESP_DEBUG)
+MAKE_PSTR_WORD(test)
+#endif
+
// for commands
MAKE_PSTR_WORD(call)
+MAKE_PSTR_WORD(cmd)
+MAKE_PSTR_WORD(id)
+MAKE_PSTR_WORD(device)
+MAKE_PSTR_WORD(data)
+MAKE_PSTR_WORD(command)
+MAKE_PSTR_WORD(commands)
// devices
MAKE_PSTR_WORD(boiler)
@@ -104,7 +113,6 @@ MAKE_PSTR(degrees, "°C")
MAKE_PSTR(asterisks, "********")
MAKE_PSTR(n_mandatory, "")
MAKE_PSTR(n_optional, "[n]")
-MAKE_PSTR(gpio_mandatory, "")
MAKE_PSTR(data_optional, "[data]")
MAKE_PSTR(typeid_mandatory, "")
MAKE_PSTR(deviceid_mandatory, "")
diff --git a/src/mqtt.cpp b/src/mqtt.cpp
index 2bfea6436..d3cc9e36c 100644
--- a/src/mqtt.cpp
+++ b/src/mqtt.cpp
@@ -37,7 +37,6 @@ uint32_t Mqtt::publish_time_other_;
uint32_t Mqtt::publish_time_sensor_;
std::vector Mqtt::mqtt_subfunctions_;
-std::vector Mqtt::mqtt_cmdfunctions_;
uint16_t Mqtt::mqtt_publish_fails_ = 0;
size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
@@ -67,10 +66,9 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
mqtt_subfunctions_.emplace_back(device_type, std::move(topic), std::move(message->topic), std::move(cb));
}
-// adds a command and callback function for a specific device
-void Mqtt::add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb) {
- // subscribe to the command topic if it doesn't exist yet
- std::string cmd_topic = EMSdevice::device_type_topic_name(device_type); // cmd topic for a device like "_cmd" e.g. "boiler_cmd"
+// subscribe to the command topic if it doesn't exist yet
+void Mqtt::register_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb) {
+ std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type);
bool exists = false;
if (!mqtt_subfunctions_.empty()) {
@@ -84,9 +82,7 @@ void Mqtt::add_command(const uint8_t device_type, const uint8_t device_id, const
Mqtt::subscribe(device_type, cmd_topic, nullptr); // use an empty function handler to signal this is a command function
}
- LOG_DEBUG(F("Registering MQTT cmd %s with topic %s"), uuid::read_flash_string(cmd).c_str(), EMSdevice::device_type_topic_name(device_type).c_str());
-
- mqtt_cmdfunctions_.emplace_back(device_type, device_id, cmd, cb);
+ 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());
}
// subscribe to an MQTT topic, and store the associated callback function. For generic functions not tied to a specific device
@@ -156,25 +152,13 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat = settings.system_heartbeat; });
shell.printfln(F_(system_heartbeat_fmt), system_heartbeat ? F_(enabled) : F_(disabled));
- shell.printfln(F("MQTT publish fails: %lu"), mqtt_publish_fails_);
+ shell.printfln(F("MQTT publish fails count: %lu"), mqtt_publish_fails_);
shell.println();
// show subscriptions
- shell.printfln(F("MQTT subscriptions:"));
+ shell.printfln(F("MQTT topic subscriptions:"));
for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
- // don't show commands if its homeassistant
- if ((strncmp(mqtt_subfunction.full_topic_.c_str(), "homeassistant/", 13) == 0)) {
- shell.printf(F(" topic: %s"), mqtt_subfunction.full_topic_.c_str());
- } else {
- // show the commands associated with this subscription
- shell.printf(F(" topic: %s, [cmd]:"), mqtt_subfunction.full_topic_.c_str());
- for (const auto & mqtt_cmdfunction : mqtt_cmdfunctions_) {
- if (EMSdevice::device_type_topic_name(mqtt_cmdfunction.device_type_) == mqtt_subfunction.topic_) {
- shell.printf(F(" %s"), uuid::read_flash_string(mqtt_cmdfunction.cmd_).c_str());
- }
- }
- }
- shell.println();
+ shell.printfln(F(" %s"), mqtt_subfunction.full_topic_.c_str());
}
shell.println();
@@ -224,31 +208,6 @@ void Mqtt::incoming(const char * topic, const char * payload) {
}
#endif
-// calls a command, context is the device_type
-// id may be used to represent a heating circuit for example
-bool Mqtt::call_command(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
-#ifdef EMSESP_DEBUG
- if (id == -1) {
- LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is default"), cmd, value);
- } else {
- LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is %d"), cmd, value, id);
- }
-#endif
-
- if (!mqtt_cmdfunctions_.empty()) {
- for (const auto & cf : mqtt_cmdfunctions_) {
- if (cf.device_type_ == device_type) {
- const char * cf_cmd = uuid::read_flash_string(cf.cmd_).c_str();
- if (strcmp(cf_cmd, cmd) == 0) {
- (cf.mqtt_cmdfunction_)(value, id); // call function, data needs to be a string and can be null
- return true;
- }
- }
- }
- }
- return false;
-}
-
// received an MQTT message that we subscribed too
void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
if (len == 0) {
@@ -295,13 +254,13 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
bool cmd_known = false;
JsonVariant data = doc["data"];
if (data.is()) {
- cmd_known = call_command(mf.device_type_, command, data.as(), n);
+ cmd_known = Command::call_command(mf.device_type_, command, data.as(), n);
} else if (data.is()) {
char data_str[10];
- cmd_known = call_command(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as()), n);
+ cmd_known = Command::call_command(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as()), n);
} else if (data.is()) {
char data_str[10];
- cmd_known = call_command(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as(), 2), n);
+ cmd_known = Command::call_command(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as(), 2), n);
}
if (!cmd_known) {
@@ -417,7 +376,6 @@ void Mqtt::start() {
});
// create space for command buffer, to avoid heap memory fragmentation
- mqtt_cmdfunctions_.reserve(40); // current count with boiler+thermostat is 37
mqtt_subfunctions_.reserve(10);
}
@@ -493,9 +451,9 @@ void Mqtt::on_connect() {
resubscribe(); // in case this is a reconnect, re-subscribe again to all MQTT topics
- // these commands respond to the topic "system_cmd" and take a payload like {cmd:"", data:"", id:""}
- add_command(EMSdevice::DeviceType::SERVICEKEY, bus_id_, F("pin"), System::mqtt_command_pin);
- add_command(EMSdevice::DeviceType::SERVICEKEY, bus_id_, F("send"), System::mqtt_command_send);
+ // these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
+ Command::add_command(EMSdevice::DeviceType::SERVICEKEY, bus_id_, F("pin"), System::command_pin);
+ Command::add_command(EMSdevice::DeviceType::SERVICEKEY, bus_id_, F("send"), System::command_send);
LOG_INFO(F("MQTT connected"));
}
diff --git a/src/mqtt.h b/src/mqtt.h
index 9a6b4f17c..1abafd70e 100644
--- a/src/mqtt.h
+++ b/src/mqtt.h
@@ -32,6 +32,7 @@
#include "helpers.h"
#include "system.h"
#include "console.h"
+#include "command.h"
#include
@@ -44,7 +45,7 @@ using uuid::console::Shell;
namespace emsesp {
using mqtt_subfunction_p = std::function;
-using mqtt_cmdfunction_p = std::function;
+using cmdfunction_p = std::function;
struct MqttMessage {
~MqttMessage() = default;
@@ -85,7 +86,7 @@ class Mqtt {
static void subscribe(const std::string & topic, mqtt_subfunction_p cb);
static void resubscribe();
- static void add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb);
+ static void register_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb);
static void publish(const std::string & topic, const std::string & payload);
static void publish(const std::string & topic, const JsonDocument & payload);
@@ -104,8 +105,6 @@ class Mqtt {
static void on_connect();
- static bool call_command(const uint8_t device_type, const char * cmd, const char * value, const int8_t id);
-
void disconnect() {
mqttClient_->disconnect();
}
@@ -126,24 +125,6 @@ class Mqtt {
mqtt_publish_fails_ = 0;
}
- struct MQTTCmdFunction {
- uint8_t device_type_;
- uint8_t device_id_;
- const __FlashStringHelper * cmd_;
- mqtt_cmdfunction_p mqtt_cmdfunction_;
-
- MQTTCmdFunction(uint8_t device_type, uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p mqtt_cmdfunction)
- : device_type_(device_type)
- , device_id_(device_id)
- , cmd_(cmd)
- , mqtt_cmdfunction_(mqtt_cmdfunction) {
- }
- };
-
- static std::vector commands() {
- return mqtt_cmdfunctions_;
- }
-
private:
static uuid::log::Logger logger_;
@@ -199,7 +180,6 @@ class Mqtt {
};
static std::vector mqtt_subfunctions_; // list of mqtt subscribe callbacks for all devices
- static std::vector mqtt_cmdfunctions_; // list of commands
uint32_t last_mqtt_poll_ = 0;
uint32_t last_publish_boiler_ = 0;
diff --git a/src/system.cpp b/src/system.cpp
index 4fe892103..eb8e70ede 100644
--- a/src/system.cpp
+++ b/src/system.cpp
@@ -39,18 +39,22 @@ uint16_t System::analog_ = 0;
// send on/off to a gpio pin
// value: true = HIGH, false = LOW
-void System::mqtt_command_pin(const char * value, const int8_t id) {
+bool System::command_pin(const char * value, const int8_t id) {
bool v = false;
if (Helpers::value2bool(value, v)) {
pinMode(id, OUTPUT);
digitalWrite(id, v);
LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
+ return true;
}
+
+ return false;
}
// send raw
-void System::mqtt_command_send(const char * value, const int8_t id) {
+bool System::command_send(const char * value, const int8_t id) {
EMSESP::send_raw_telegram(value); // ignore id
+ return true;
}
// restart EMS-ESP
@@ -590,32 +594,12 @@ void System::console_commands(Shell & shell, unsigned int context) {
shell.printfln(F_(wifi_password_fmt), wifiSettings.ssid.isEmpty() ? F_(unset) : F_(asterisks));
});
});
- /*
- EMSESPShell::commands->add_command(ShellContext::SYSTEM,
- CommandFlags::USER,
- flash_string_vector{F_(show), F_(mqtt)},
- [](Shell & shell, const std::vector & arguments __attribute__((unused))) { Mqtt::show_mqtt(shell); });
-*/
+
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(show), F_(users)},
[](Shell & shell, const std::vector & arguments __attribute__((unused))) { System::show_users(shell); });
- EMSESPShell::commands->add_command(ShellContext::SYSTEM,
- CommandFlags::ADMIN,
- flash_string_vector{F_(pin)},
- flash_string_vector{F_(gpio_mandatory), F_(data_optional)},
- [](Shell & shell, const std::vector & arguments) {
- if (arguments.size() == 1) {
- shell.printfln(F("use on/off, 1/0 or true/false"));
- return;
- }
- int pin = 0;
- if (Helpers::value2number(arguments[0].c_str(), pin)) {
- System::mqtt_command_pin(arguments[1].c_str(), pin);
- }
- });
-
// enter the context
Console::enter_custom_context(shell, context);
}
@@ -820,4 +804,81 @@ bool System::check_upgrade() {
#endif
}
+// export all settings to JSON text
+// http://ems-esp/api?device=system&cmd=info
+String System::export_settings() {
+ StaticJsonDocument doc;
+
+#ifndef EMSESP_STANDALONE
+ JsonObject root = doc.to();
+
+ EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & settings) {
+ JsonObject node = root.createNestedObject("WIFI");
+ node["ssid"] = settings.ssid;
+ // node["password"] = settings.password;
+ node["hostname"] = settings.hostname;
+ node["static_ip_config"] = settings.staticIPConfig;
+ JsonUtils::writeIP(node, "local_ip", settings.localIP);
+ JsonUtils::writeIP(node, "gateway_ip", settings.gatewayIP);
+ JsonUtils::writeIP(node, "subnet_mask", settings.subnetMask);
+ JsonUtils::writeIP(node, "dns_ip_1", settings.dnsIP1);
+ JsonUtils::writeIP(node, "dns_ip_2", settings.dnsIP2);
+ });
+
+ EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) {
+ JsonObject node = root.createNestedObject("AP");
+ node["provision_mode"] = settings.provisionMode;
+ node["ssid"] = settings.ssid;
+ // node["password"] = settings.password;
+ node["local_ip"] = settings.localIP.toString();
+ node["gateway_ip"] = settings.gatewayIP.toString();
+ node["subnet_mask"] = settings.subnetMask.toString();
+ });
+
+ EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
+ JsonObject node = root.createNestedObject("MQTT");
+ node["enabled"] = settings.enabled;
+ node["host"] = settings.host;
+ node["port"] = settings.port;
+ node["username"] = settings.username;
+ // node["password"] = settings.password;
+ node["client_id"] = settings.clientId;
+ node["keep_alive"] = settings.keepAlive;
+ node["clean_session"] = settings.cleanSession;
+ node["max_topic_length"] = settings.maxTopicLength;
+ node["system_heartbeat"] = settings.system_heartbeat;
+ node["publish_time_boiler"] = settings.publish_time_boiler;
+ node["publish_time_thermostat"] = settings.publish_time_thermostat;
+ node["publish_time_solar"] = settings.publish_time_solar;
+ node["publish_time_mixing"] = settings.publish_time_mixing;
+ node["publish_time_other"] = settings.publish_time_other;
+ node["publish_time_sensor"] = settings.publish_time_sensor;
+ node["mqtt_format"] = settings.mqtt_format;
+ node["mqtt_qos"] = settings.mqtt_qos;
+ node["mqtt_retain"] = settings.mqtt_retain;
+ });
+
+ EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) {
+ JsonObject node = root.createNestedObject("NTP");
+ node["enabled"] = settings.enabled;
+ node["server"] = settings.server;
+ node["tz_label"] = settings.tzLabel;
+ node["tz_format"] = settings.tzFormat;
+ });
+
+ EMSESP::esp8266React.getOTASettingsService()->read([&](OTASettings & settings) {
+ JsonObject node = root.createNestedObject("OTA");
+ node["enabled"] = settings.enabled;
+ node["port"] = settings.port;
+ // node["password"] = settings.password;
+ });
+
+#endif
+
+ String buffer;
+ serializeJsonPretty(doc, buffer);
+
+ return buffer;
+}
+
} // namespace emsesp
diff --git a/src/system.h b/src/system.h
index c63dd9f07..22c13a141 100644
--- a/src/system.h
+++ b/src/system.h
@@ -47,19 +47,20 @@ class System {
static void format(uuid::console::Shell & shell);
static void console_commands(Shell & shell, unsigned int context);
-
- static void mqtt_command_pin(const char * value, const int8_t id);
- static void mqtt_command_send(const char * value, const int8_t id);
+ static bool command_pin(const char * value, const int8_t id);
+ static bool command_send(const char * value, const int8_t id);
static uint8_t free_mem();
static void upload_status(bool in_progress);
static bool upload_status();
- void syslog_init();
- void set_heartbeat(bool system_heartbeat);
- void send_heartbeat();
static void show_mem(const char * note);
static void set_led();
- bool check_upgrade();
+ static String export_settings();
+
+ bool check_upgrade();
+ void syslog_init();
+ void set_heartbeat(bool system_heartbeat);
+ void send_heartbeat();
private:
static uuid::log::Logger logger_;
diff --git a/src/test/test.cpp b/src/test/test.cpp
index 0a6bee463..04507f840 100644
--- a/src/test/test.cpp
+++ b/src/test/test.cpp
@@ -601,12 +601,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
// test publish and adding to queue
EMSESP::txservice_.flush_tx_queue();
- EMSESP::EMSESP::mqtt_.publish("boiler_cmd", "test me");
+ EMSESP::EMSESP::mqtt_.publish("boiler", "test me");
Mqtt::show_mqtt(shell); // show queue
- strcpy(boiler_topic, "ems-esp/boiler_cmd");
- strcpy(thermostat_topic, "ems-esp/thermostat_cmd");
- strcpy(system_topic, "ems-esp/saystem_cmd");
+ strcpy(boiler_topic, "ems-esp/boiler");
+ strcpy(thermostat_topic, "ems-esp/thermostat");
+ strcpy(system_topic, "ems-esp/saystem");
EMSESP::mqtt_.incoming(boiler_topic, "12345"); // invalid format
EMSESP::mqtt_.incoming("bad_topic", "12345"); // no matching topic
diff --git a/src/version.h b/src/version.h
index 1122d499d..53a6dd302 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1 +1 @@
-#define EMSESP_APP_VERSION "2.0.2b2"
+#define EMSESP_APP_VERSION "2.0.2b3"