diff --git a/interface/src/project/EMSESPHelp.tsx b/interface/src/project/EMSESPHelp.tsx
index 8709c6f7c..bec31b637 100644
--- a/interface/src/project/EMSESPHelp.tsx
+++ b/interface/src/project/EMSESPHelp.tsx
@@ -17,7 +17,7 @@ class EMSESPHelp extends Component {
- Check for news and updates on the {'Wiki'}.
+ Check for news and updates on the {'Documentation site'}.
For live community chat go to {'Gitter'}.
diff --git a/interface/src/project/EMSESPSettingsController.tsx b/interface/src/project/EMSESPSettingsController.tsx
index 332447dfc..f8be4938a 100644
--- a/interface/src/project/EMSESPSettingsController.tsx
+++ b/interface/src/project/EMSESPSettingsController.tsx
@@ -48,7 +48,7 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
- Customize EMS-ESP by modifying the default settings here. Refer to the {'Wiki'} for descriptions of each setting.
+ Customize EMS-ESP by modifying the default settings here. Refer to the {'Documentation'} for descriptions of each setting.
@@ -181,6 +181,20 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
label="Shower Alert"
/>
+
+ API
+
+
+ }
+ label="Enable WEB API (for write commands)"
+ />
+
Syslog
diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts
index 2f59596b1..861ff81fa 100644
--- a/interface/src/project/EMSESPtypes.ts
+++ b/interface/src/project/EMSESPtypes.ts
@@ -13,6 +13,7 @@ export interface EMSESPSettings {
dallas_parasite: boolean;
led_gpio: number;
hide_led: boolean;
+ api_enabled: boolean;
}
export enum busConnectionStatus {
diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp
index e5a4928a1..53348bf8e 100644
--- a/lib/framework/MqttSettingsService.cpp
+++ b/lib/framework/MqttSettingsService.cpp
@@ -4,9 +4,9 @@
namespace emsesp {
class EMSESP {
public:
- static System system_;
- static Mqtt mqtt_;
- static Sensors sensors_;
+ static System system_;
+ static Mqtt mqtt_;
+ static Sensor sensor_;
};
} // namespace emsesp
@@ -169,7 +169,7 @@ void MqttSettingsService::configureMqtt() {
_mqttClient.connect();
}
- emsesp::EMSESP::sensors_.reload();
+ emsesp::EMSESP::sensor_.reload();
}
void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h
index f138989bb..83e8f9d5a 100644
--- a/lib/framework/MqttSettingsService.h
+++ b/lib/framework/MqttSettingsService.h
@@ -10,7 +10,7 @@
#include "../../src/system.h"
#include "../../src/mqtt.h"
-#include "../../src/sensors.h"
+#include "../../src/sensor.h"
#define MQTT_RECONNECTION_DELAY 1000
diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h
index 7183c4a36..36e2e6851 100644
--- a/lib_standalone/ESP8266React.h
+++ b/lib_standalone/ESP8266React.h
@@ -23,6 +23,7 @@ class DummySettings {
bool shower_timer = false;
bool shower_alert = false;
bool hide_led = false;
+ bool api_enabled = true;
uint16_t publish_time = 10; // seconds
uint8_t mqtt_format = 3; // 1=single, 2=nested, 3=ha, 4=custom
uint8_t mqtt_qos = 0;
diff --git a/src/EMSESPAPIService.cpp b/src/EMSESPAPIService.cpp
index c1df17702..ec8ebcbdd 100644
--- a/src/EMSESPAPIService.cpp
+++ b/src/EMSESPAPIService.cpp
@@ -30,9 +30,13 @@ EMSESPAPIService::EMSESPAPIService(AsyncWebServer * server) {
// http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
void EMSESPAPIService::emsespAPIService(AsyncWebServerRequest * request) {
+ // see if the API is enabled
+ bool api_enabled;
+ EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { api_enabled = settings.api_enabled; });
+
// must have device and cmd parameters
if ((!request->hasParam(F_(device))) || (!request->hasParam(F_(cmd)))) {
- request->send(400, "text/plain", F("invalid syntax"));
+ request->send(400, "text/plain", F("Invalid syntax"));
return;
}
@@ -40,26 +44,16 @@ void EMSESPAPIService::emsespAPIService(AsyncWebServerRequest * request) {
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"));
+ 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"));
+ request->send(400, "text/plain", F("Invalid cmd"));
return;
}
@@ -73,31 +67,54 @@ void EMSESPAPIService::emsespAPIService(AsyncWebServerRequest * request) {
id = request->getParam(F_(id))->value();
}
+ DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
+ JsonObject output = doc.to();
+ bool ok = false;
+
// 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
+ ok = Command::call(device_type, cmd.c_str(), nullptr, -1, output); // command only
} else {
- ok = Command::call_command(device_type, cmd.c_str(), data.c_str(), id.toInt()); // has cmd, data and id
+ if (api_enabled) {
+ // we only allow commands with parameters if the API is enabled
+ if (id.isEmpty()) {
+ ok = Command::call(device_type, cmd.c_str(), data.c_str(), -1, output); // only ID
+ } else {
+ ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), output); // has cmd, data and id
+ }
+ } else {
+ request->send(401, "text/plain", F("Unauthorized"));
+ return;
+ }
}
// debug
#if defined(EMSESP_DEBUG)
- std::string output(200, '\0');
- snprintf_P(&output[0],
- output.capacity() + 1,
+ std::string debug(200, '\0');
+ snprintf_P(&debug[0],
+ debug.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());
+ ok ? F("OK") : F("Invalid"));
+ EMSESP::logger().info(debug.c_str());
+ if (output.size()) {
+ char buffer2[EMSESP_MAX_JSON_SIZE_LARGE];
+ serializeJson(doc, buffer2);
+ EMSESP::logger().info("output (max 255 chars): %s", buffer2);
+ }
#endif
- request->send(200, "text/plain", ok ? F("OK") : F("Failed"));
+ // if we have returned data in JSON format, send this to the WEB
+ if (output.size()) {
+ char buffer[EMSESP_MAX_JSON_SIZE_LARGE];
+ serializeJson(doc, buffer);
+ request->send(200, "text/plain", buffer);
+ } else {
+ request->send(200, "text/plain", ok ? F("OK") : F("Invalid"));
+ }
}
} // namespace emsesp
\ No newline at end of file
diff --git a/src/EMSESPAPIService.h b/src/EMSESPAPIService.h
index 90d45c11d..1ecbc4490 100644
--- a/src/EMSESPAPIService.h
+++ b/src/EMSESPAPIService.h
@@ -33,7 +33,6 @@ class EMSESPAPIService {
private:
void emsespAPIService(AsyncWebServerRequest * request);
-
};
} // namespace emsesp
diff --git a/src/EMSESPSettingsService.cpp b/src/EMSESPSettingsService.cpp
index cf4cbf018..7541fdb32 100644
--- a/src/EMSESPSettingsService.cpp
+++ b/src/EMSESPSettingsService.cpp
@@ -42,6 +42,7 @@ void EMSESPSettings::read(EMSESPSettings & settings, JsonObject & root) {
root["dallas_parasite"] = settings.dallas_parasite;
root["led_gpio"] = settings.led_gpio;
root["hide_led"] = settings.hide_led;
+ root["api_enabled"] = settings.api_enabled;
}
StateUpdateResult EMSESPSettings::update(JsonObject & root, EMSESPSettings & settings) {
@@ -59,6 +60,7 @@ StateUpdateResult EMSESPSettings::update(JsonObject & root, EMSESPSettings & set
settings.dallas_parasite = root["dallas_parasite"] | EMSESP_DEFAULT_DALLAS_PARASITE;
settings.led_gpio = root["led_gpio"] | EMSESP_DEFAULT_LED_GPIO;
settings.hide_led = root["hide_led"] | EMSESP_DEFAULT_HIDE_LED;
+ settings.api_enabled = root["api_enabled"] | EMSESP_DEFAULT_API_ENABLED;
return StateUpdateResult::CHANGED;
}
@@ -70,8 +72,8 @@ void EMSESPSettingsService::onUpdate() {
// EMSESP::system_.syslog_init(); // changing SysLog will require a restart
EMSESP::init_tx();
System::set_led();
- Sensors sensors_; // Dallas sensors
- sensors_.start();
+ Sensor sensor_; // Dallas sensors
+ sensor_.start();
}
void EMSESPSettingsService::begin() {
diff --git a/src/EMSESPSettingsService.h b/src/EMSESPSettingsService.h
index a23d344ca..7be982e6c 100644
--- a/src/EMSESPSettingsService.h
+++ b/src/EMSESPSettingsService.h
@@ -35,6 +35,7 @@
#define EMSESP_DEFAULT_SHOWER_ALERT false
#define EMSESP_DEFAULT_HIDE_LED false
#define EMSESP_DEFAULT_DALLAS_PARASITE false
+#define EMSESP_DEFAULT_API_ENABLED true
// Default GPIO PIN definitions
#if defined(ESP8266)
@@ -75,6 +76,7 @@ class EMSESPSettings {
bool dallas_parasite;
uint8_t led_gpio;
bool hide_led;
+ bool api_enabled;
static void read(EMSESPSettings & settings, JsonObject & root);
static StateUpdateResult update(JsonObject & root, EMSESPSettings & settings);
diff --git a/src/command.cpp b/src/command.cpp
index 7694caf29..cbc3dad94 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -29,12 +29,15 @@ 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) {
+bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & output) {
#ifdef EMSESP_DEBUG
- if (id == -1) {
- LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is default"), cmd, value);
+ std::string dname = EMSdevice::device_type_2_device_name(device_type);
+ if (value == nullptr) {
+ LOG_DEBUG(F("[DEBUG] Calling command %s in %s"), cmd, dname.c_str());
+ } else if (id == -1) {
+ LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is default in %s"), cmd, value, dname.c_str());
} else {
- LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is %d"), cmd, value, id);
+ LOG_DEBUG(F("[DEBUG] Calling command %s, value %s, id is %d in %s"), cmd, value, id, dname.c_str());
}
#endif
@@ -42,23 +45,37 @@ bool Command::call_command(const uint8_t device_type, const char * cmd, const ch
for (const auto & cf : cmdfunctions_) {
if (cf.device_type_ == device_type) {
const char * cf_cmd = uuid::read_flash_string(cf.cmd_).c_str();
+ // find a matching command and call it
if (strcmp(cf_cmd, cmd) == 0) {
- return ((cf.cmdfunction_)(value, id)); // call function, data needs to be a string and can be null
+ if (cf.cmdfunction_json_) {
+ // check if json object is empty, if so quit
+ if (output.isNull()) {
+ LOG_WARNING(F("Ignore call for command %s in %s because no json"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str());
+ }
+ return ((cf.cmdfunction_json_)(value, id, output));
+ } else {
+ return ((cf.cmdfunction_)(value, id));
+ }
}
}
}
}
- return false;
+ return false; // command not found
}
-// 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);
+// add a command to the list, which does not return json
+void Command::add(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb) {
+ cmdfunctions_.emplace_back(device_type, cmd, cb, nullptr);
// see if we need to subscribe
Mqtt::register_command(device_type, device_id, cmd, cb);
}
+// add a command to the list, which does return json object as output
+void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb) {
+ cmdfunctions_.emplace_back(device_type, cmd, nullptr, 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_) {
@@ -70,7 +87,7 @@ bool Command::find(const uint8_t device_type, const char * cmd) {
}
// output list of all commands to console for a specific DeviceType
-void Command::show_commands(uuid::console::Shell & shell, uint8_t device_type) {
+void Command::show(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());
@@ -81,9 +98,9 @@ void Command::show_commands(uuid::console::Shell & shell, uint8_t device_type) {
// 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::MAIN) {
+ // return EMSdevice::DeviceType::SERVICEKEY;
+ // }
if (context == ShellContext::BOILER) {
return EMSdevice::DeviceType::BOILER;
}
@@ -99,34 +116,36 @@ uint8_t Command::context_2_device_type(unsigned int context) {
if (context == ShellContext::THERMOSTAT) {
return EMSdevice::DeviceType::THERMOSTAT;
}
+ if (context == ShellContext::SENSOR) {
+ return EMSdevice::DeviceType::SENSOR;
+ }
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()));
+void Command::show(uuid::console::Shell & shell) {
+ show(shell, context_2_device_type(shell.context()));
}
-
// output list of all commands to console
-void Command::show_all_commands(uuid::console::Shell & shell) {
+void Command::show_all(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);
+ show(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);
+ show(shell, device_class.first);
}
}
}
}
-// given a context, automatically add the commands to the console
+// Add the console 'call' command to the given context
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;
@@ -147,7 +166,7 @@ void Command::add_context_commands(unsigned int context) {
if (arguments.empty()) {
// list options
shell.print("Available commands: ");
- show_commands(shell);
+ show(shell);
shell.println();
return;
}
@@ -155,16 +174,35 @@ void Command::add_context_commands(unsigned int context) {
// determine the device_type from the shell context
uint8_t device_type = context_2_device_type(shell.context());
+ // validate the command
const char * cmd = arguments[0].c_str();
+ if (!find(device_type, cmd)) {
+ shell.print(F("Unknown command. Available commands are: "));
+ show(shell);
+ shell.println();
+ return;
+ }
+
+ DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
+ JsonObject output = doc.to();
+
+ bool ok = false;
if (arguments.size() == 1) {
- // no value specified
- (void)Command::call_command(device_type, cmd, nullptr, -1);
+ // no value specified, just the cmd
+ ok = Command::call(device_type, cmd, nullptr, -1, output);
} else if (arguments.size() == 2) {
// has a value but no id
- (void)Command::call_command(device_type, cmd, arguments.back().c_str(), -1);
+ ok = Command::call(device_type, cmd, arguments.back().c_str(), -1, output);
} 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()));
+ ok = Command::call(device_type, cmd, arguments[1].c_str(), atoi(arguments[2].c_str()), output);
+ }
+
+ if (ok) {
+ shell.print(F("output: "));
+ serializeJson(doc, shell);
+ shell.println();
+ shell.println();
}
},
[&](Shell & shell __attribute__((unused)), const std::vector & arguments) -> std::vector {
@@ -182,5 +220,4 @@ void Command::add_context_commands(unsigned int context) {
});
}
-
} // namespace emsesp
\ No newline at end of file
diff --git a/src/command.h b/src/command.h
index f020ff2d5..ff8580c59 100644
--- a/src/command.h
+++ b/src/command.h
@@ -34,21 +34,22 @@ using uuid::console::Shell;
namespace emsesp {
-using cmdfunction_p = std::function;
+using cmdfunction_p = std::function;
+using cmdfunction_json_p = std::function;
class Command {
public:
struct CmdFunction {
uint8_t device_type_; // DeviceType::
- uint8_t device_id_;
const __FlashStringHelper * cmd_;
cmdfunction_p cmdfunction_;
+ cmdfunction_json_p cmdfunction_json_;
- CmdFunction(uint8_t device_type, uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cmdfunction)
+ CmdFunction(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cmdfunction, cmdfunction_json_p cmdfunction_json)
: device_type_(device_type)
- , device_id_(device_id)
, cmd_(cmd)
- , cmdfunction_(cmdfunction) {
+ , cmdfunction_(cmdfunction)
+ , cmdfunction_json_(cmdfunction_json) {
}
};
@@ -56,10 +57,11 @@ class Command {
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 bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & output);
+ static void add(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb);
+ static void add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb);
+ static void show_all(uuid::console::Shell & shell);
+ static void show(uuid::console::Shell & shell);
static void add_context_commands(unsigned int context);
static bool find(const uint8_t device_type, const char * cmd);
@@ -68,7 +70,7 @@ class Command {
private:
static uuid::log::Logger logger_;
- static void show_commands(uuid::console::Shell & shell, uint8_t device_type);
+ static void show(uuid::console::Shell & shell, uint8_t device_type);
static uint8_t context_2_device_type(unsigned int context);
};
diff --git a/src/console.cpp b/src/console.cpp
index 288fa599c..3eeb9d981 100644
--- a/src/console.cpp
+++ b/src/console.cpp
@@ -153,7 +153,7 @@ void EMSESPShell::add_console_commands() {
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); });
+ [](Shell & shell, const std::vector & arguments __attribute__((unused))) { Command::show_all(shell); });
commands->add_command(
ShellContext::MAIN,
@@ -466,8 +466,10 @@ void Console::load_standard_commands(unsigned int context) {
// load the commands (console & mqtt topics) for this specific context
- Command::add_context_commands(context);
-
+ // unless it's main (the root)
+ if (context != ShellContext::MAIN) {
+ Command::add_context_commands(context);
+ }
}
// prompt, change per context
@@ -485,6 +487,9 @@ std::string EMSESPShell::context_text() {
case ShellContext::THERMOSTAT:
return std::string{"/thermostat"};
+ case ShellContext::SENSOR:
+ return std::string{"/sensor"};
+
default:
return std::string{};
}
@@ -577,7 +582,6 @@ void Console::start() {
shell->log_level(uuid::log::Level::DEBUG);
#endif
-
#if defined(EMSESP_STANDALONE)
// always start in su/admin mode when running tests
shell->add_flags(CommandFlags::ADMIN);
diff --git a/src/console.h b/src/console.h
index 1b4fd7304..9149e951d 100644
--- a/src/console.h
+++ b/src/console.h
@@ -79,7 +79,8 @@ enum ShellContext : uint8_t {
BOILER,
THERMOSTAT,
SOLAR,
- MIXING
+ MIXING,
+ SENSOR
};
diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp
index 1daa9e6b5..83c75e231 100644
--- a/src/devices/boiler.cpp
+++ b/src/devices/boiler.cpp
@@ -69,6 +69,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
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); });
+ // API call
+ // Command::add_with_json(this->device_type(), F("info"), Boiler::command_info);
+ Command::add_with_json(this->device_type(), F("info"), [&](const char * value, const int8_t id, JsonObject & object) {
+ return command_info(value, id, object);
+ });
+
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
mqtt_format_ = settings.mqtt_format; // single, nested or ha
@@ -140,208 +146,217 @@ void Boiler::device_info_web(JsonArray & root) {
render_value_json(root, "", F("Heat Pump modulation"), pumpMod2_, F_(percent));
}
+bool Boiler::command_info(const char * value, const int8_t id, JsonObject & output) {
+ return (export_values(output));
+}
+
+// creates JSON doc from values
+// returns false if empty
+bool Boiler::export_values(JsonObject & output) {
+ char s[10]; // for formatting strings
+
+ if (Helpers::hasValue(wWComfort_)) {
+ if (wWComfort_ == 0x00) {
+ output["wWComfort"] = "Hot";
+ } else if (wWComfort_ == 0xD8) {
+ output["wWComfort"] = "Eco";
+ } else if (wWComfort_ == 0xEC) {
+ output["wWComfort"] = "Intelligent";
+ }
+ }
+
+ if (Helpers::hasValue(wWSelTemp_)) {
+ output["wWSelTemp"] = wWSelTemp_;
+ }
+ if (Helpers::hasValue(wWSetTmp_)) {
+ output["wWSetTemp"] = wWSetTmp_;
+ }
+ if (Helpers::hasValue(wWDisinfectTemp_)) {
+ output["wWDisinfectionTemp"] = wWDisinfectTemp_;
+ }
+ if (Helpers::hasValue(selFlowTemp_)) {
+ output["selFlowTemp"] = selFlowTemp_;
+ }
+ if (Helpers::hasValue(selBurnPow_)) {
+ output["selBurnPow"] = selBurnPow_;
+ }
+ if (Helpers::hasValue(curBurnPow_)) {
+ output["curBurnPow"] = curBurnPow_;
+ }
+ if (Helpers::hasValue(pumpMod_)) {
+ output["pumpMod"] = pumpMod_;
+ }
+ if (Helpers::hasValue(pumpMod2_)) {
+ output["pumpMod2"] = pumpMod2_;
+ }
+ if (wWType_ == 0) { // no output if not set
+ output["wWType"] = F("off");
+ } else if (wWType_ == 1) {
+ output["wWType"] = F("flow");
+ } else if (wWType_ == 2) {
+ output["wWType"] = F("buffered flow");
+ } else if (wWType_ == 3) {
+ output["wWType"] = F("buffer");
+ } else if (wWType_ == 4) {
+ output["wWType"] = F("layered buffer");
+ }
+ if (Helpers::hasValue(wWChargeType_, EMS_VALUE_BOOL)) {
+ output["wWChargeType"] = wWChargeType_ ? "valve" : "pump";
+ }
+ if (Helpers::hasValue(wWCircPump_, EMS_VALUE_BOOL)) {
+ output["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWCircPumpMode_)) {
+ output["wWCiPuMode"] = wWCircPumpMode_;
+ }
+ if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) {
+ output["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(extTemp_)) {
+ output["outdoorTemp"] = (float)extTemp_ / 10;
+ }
+ if (Helpers::hasValue(wWCurTmp_)) {
+ output["wWCurTmp"] = (float)wWCurTmp_ / 10;
+ }
+ if (Helpers::hasValue(wWCurTmp2_)) {
+ output["wWCurTmp2"] = (float)wWCurTmp2_ / 10;
+ }
+ if (Helpers::hasValue(wWCurFlow_)) {
+ output["wWCurFlow"] = (float)wWCurFlow_ / 10;
+ }
+ if (Helpers::hasValue(curFlowTemp_)) {
+ output["curFlowTemp"] = (float)curFlowTemp_ / 10;
+ }
+ if (Helpers::hasValue(retTemp_)) {
+ output["retTemp"] = (float)retTemp_ / 10;
+ }
+ if (Helpers::hasValue(switchTemp_)) {
+ output["switchTemp"] = (float)switchTemp_ / 10;
+ }
+ if (Helpers::hasValue(sysPress_)) {
+ output["sysPress"] = (float)sysPress_ / 10;
+ }
+ if (Helpers::hasValue(boilTemp_)) {
+ output["boilTemp"] = (float)boilTemp_ / 10;
+ }
+ if (Helpers::hasValue(wwStorageTemp1_)) {
+ output["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10;
+ }
+ if (Helpers::hasValue(wwStorageTemp2_)) {
+ output["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10;
+ }
+ if (Helpers::hasValue(exhaustTemp_)) {
+ output["exhaustTemp"] = (float)exhaustTemp_ / 10;
+ }
+ if (Helpers::hasValue(wWActivated_, EMS_VALUE_BOOL)) {
+ output["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWOneTime_, EMS_VALUE_BOOL)) {
+ output["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWDisinfecting_, EMS_VALUE_BOOL)) {
+ output["wWDisinfecting"] = Helpers::render_value(s, wWDisinfecting_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWReadiness_, EMS_VALUE_BOOL)) {
+ output["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWRecharging_, EMS_VALUE_BOOL)) {
+ output["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWTemperatureOK_, EMS_VALUE_BOOL)) {
+ output["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) {
+ output["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(burnGas_, EMS_VALUE_BOOL)) {
+ output["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(flameCurr_)) {
+ output["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
+ }
+ if (Helpers::hasValue(heatPmp_, EMS_VALUE_BOOL)) {
+ output["heatPump"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(fanWork_, EMS_VALUE_BOOL)) {
+ output["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(ignWork_, EMS_VALUE_BOOL)) {
+ output["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(wWHeat_, EMS_VALUE_BOOL)) {
+ output["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(heating_activated_, EMS_VALUE_BOOL)) {
+ output["heatingActivated"] = Helpers::render_value(s, heating_activated_, EMS_VALUE_BOOL);
+ }
+ if (Helpers::hasValue(heating_temp_)) {
+ output["heatingTemp"] = heating_temp_;
+ }
+ if (Helpers::hasValue(pump_mod_max_)) {
+ output["pumpModMax"] = pump_mod_max_;
+ }
+ if (Helpers::hasValue(pump_mod_min_)) {
+ output["pumpModMin"] = pump_mod_min_;
+ }
+ if (Helpers::hasValue(pumpDelay_)) {
+ output["pumpDelay"] = pumpDelay_;
+ }
+ if (Helpers::hasValue(burnPeriod_)) {
+ output["burnMinPeriod"] = burnPeriod_;
+ }
+ if (Helpers::hasValue(burnPowermin_)) {
+ output["burnMinPower"] = burnPowermin_;
+ }
+ if (Helpers::hasValue(burnPowermax_)) {
+ output["burnMaxPower"] = burnPowermax_;
+ }
+ if (Helpers::hasValue(boilTemp_on_)) {
+ output["boilHystOn"] = boilTemp_on_;
+ }
+ if (Helpers::hasValue(boilTemp_off_)) {
+ output["boilHystOff"] = boilTemp_off_;
+ }
+ if (Helpers::hasValue(setFlowTemp_)) {
+ output["setFlowTemp"] = setFlowTemp_;
+ }
+ if (Helpers::hasValue(setWWPumpPow_)) {
+ output["wWSetPumpPower"] = setWWPumpPow_;
+ }
+ if (Helpers::hasValue(wWStarts_)) {
+ output["wWStarts"] = wWStarts_;
+ }
+ if (Helpers::hasValue(wWWorkM_)) {
+ output["wWWorkM"] = wWWorkM_;
+ }
+ if (Helpers::hasValue(UBAuptime_)) {
+ output["UBAuptime"] = UBAuptime_;
+ }
+ if (Helpers::hasValue(burnStarts_)) {
+ output["burnStarts"] = burnStarts_;
+ }
+ if (Helpers::hasValue(burnWorkMin_)) {
+ output["burnWorkMin"] = burnWorkMin_;
+ }
+ if (Helpers::hasValue(heatWorkMin_)) {
+ output["heatWorkMin"] = heatWorkMin_;
+ }
+ if (Helpers::hasValue(serviceCode_)) {
+ output["serviceCode"] = serviceCodeChar_;
+ output["serviceCodeNumber"] = serviceCode_;
+ }
+
+ return (output.size());
+}
+
// publish values via MQTT
void Boiler::publish_values() {
// const size_t capacity = JSON_OBJECT_SIZE(56); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
// DynamicJsonDocument doc(capacity);
StaticJsonDocument doc;
-
- char s[10]; // for formatting strings
-
- if (Helpers::hasValue(wWComfort_)) {
- if (wWComfort_ == 0x00) {
- doc["wWComfort"] = "Hot";
- } else if (wWComfort_ == 0xD8) {
- doc["wWComfort"] = "Eco";
- } else if (wWComfort_ == 0xEC) {
- doc["wWComfort"] = "Intelligent";
- }
- }
-
- if (Helpers::hasValue(wWSelTemp_)) {
- doc["wWSelTemp"] = wWSelTemp_;
- }
- if (Helpers::hasValue(wWSetTmp_)) {
- doc["wWSetTemp"] = wWSetTmp_;
- }
- if (Helpers::hasValue(wWDisinfectTemp_)) {
- doc["wWDisinfectionTemp"] = wWDisinfectTemp_;
- }
- if (Helpers::hasValue(selFlowTemp_)) {
- doc["selFlowTemp"] = selFlowTemp_;
- }
- if (Helpers::hasValue(selBurnPow_)) {
- doc["selBurnPow"] = selBurnPow_;
- }
- if (Helpers::hasValue(curBurnPow_)) {
- doc["curBurnPow"] = curBurnPow_;
- }
- if (Helpers::hasValue(pumpMod_)) {
- doc["pumpMod"] = pumpMod_;
- }
- if (Helpers::hasValue(pumpMod2_)) {
- doc["pumpMod2"] = pumpMod2_;
- }
- if (wWType_ == 0) { // no output if not set
- doc["wWType"] = F("off");
- } else if (wWType_ == 1) {
- doc["wWType"] = F("flow");
- } else if (wWType_ == 2) {
- doc["wWType"] = F("buffered flow");
- } else if (wWType_ == 3) {
- doc["wWType"] = F("buffer");
- } else if (wWType_ == 4) {
- doc["wWType"] = F("layered buffer");
- }
- if (Helpers::hasValue(wWChargeType_, EMS_VALUE_BOOL)) {
- doc["wWChargeType"] = wWChargeType_ ? "valve" : "pump";
- }
- if (Helpers::hasValue(wWCircPump_, EMS_VALUE_BOOL)) {
- doc["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWCircPumpMode_)) {
- doc["wWCiPuMode"] = wWCircPumpMode_;
- }
- if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) {
- doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(extTemp_)) {
- doc["outdoorTemp"] = (float)extTemp_ / 10;
- }
- if (Helpers::hasValue(wWCurTmp_)) {
- doc["wWCurTmp"] = (float)wWCurTmp_ / 10;
- }
- if (Helpers::hasValue(wWCurTmp2_)) {
- doc["wWCurTmp2"] = (float)wWCurTmp2_ / 10;
- }
- if (Helpers::hasValue(wWCurFlow_)) {
- doc["wWCurFlow"] = (float)wWCurFlow_ / 10;
- }
- if (Helpers::hasValue(curFlowTemp_)) {
- doc["curFlowTemp"] = (float)curFlowTemp_ / 10;
- }
- if (Helpers::hasValue(retTemp_)) {
- doc["retTemp"] = (float)retTemp_ / 10;
- }
- if (Helpers::hasValue(switchTemp_)) {
- doc["switchTemp"] = (float)switchTemp_ / 10;
- }
- if (Helpers::hasValue(sysPress_)) {
- doc["sysPress"] = (float)sysPress_ / 10;
- }
- if (Helpers::hasValue(boilTemp_)) {
- doc["boilTemp"] = (float)boilTemp_ / 10;
- }
- if (Helpers::hasValue(wwStorageTemp1_)) {
- doc["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10;
- }
- if (Helpers::hasValue(wwStorageTemp2_)) {
- doc["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10;
- }
- if (Helpers::hasValue(exhaustTemp_)) {
- doc["exhaustTemp"] = (float)exhaustTemp_ / 10;
- }
- if (Helpers::hasValue(wWActivated_, EMS_VALUE_BOOL)) {
- doc["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWOneTime_, EMS_VALUE_BOOL)) {
- doc["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWDisinfecting_, EMS_VALUE_BOOL)) {
- doc["wWDisinfecting"] = Helpers::render_value(s, wWDisinfecting_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWReadiness_, EMS_VALUE_BOOL)) {
- doc["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWRecharging_, EMS_VALUE_BOOL)) {
- doc["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWTemperatureOK_, EMS_VALUE_BOOL)) {
- doc["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) {
- doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(burnGas_, EMS_VALUE_BOOL)) {
- doc["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(flameCurr_)) {
- doc["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
- }
- if (Helpers::hasValue(heatPmp_, EMS_VALUE_BOOL)) {
- doc["heatPump"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(fanWork_, EMS_VALUE_BOOL)) {
- doc["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(ignWork_, EMS_VALUE_BOOL)) {
- doc["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(wWHeat_, EMS_VALUE_BOOL)) {
- doc["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(heating_activated_, EMS_VALUE_BOOL)) {
- doc["heatingActivated"] = Helpers::render_value(s, heating_activated_, EMS_VALUE_BOOL);
- }
- if (Helpers::hasValue(heating_temp_)) {
- doc["heatingTemp"] = heating_temp_;
- }
- if (Helpers::hasValue(pump_mod_max_)) {
- doc["pumpModMax"] = pump_mod_max_;
- }
- if (Helpers::hasValue(pump_mod_min_)) {
- doc["pumpModMin"] = pump_mod_min_;
- }
- if (Helpers::hasValue(pumpDelay_)) {
- doc["pumpDelay"] = pumpDelay_;
- }
- if (Helpers::hasValue(burnPeriod_)) {
- doc["burnMinPeriod"] = burnPeriod_;
- }
- if (Helpers::hasValue(burnPowermin_)) {
- doc["burnMinPower"] = burnPowermin_;
- }
- if (Helpers::hasValue(burnPowermax_)) {
- doc["burnMaxPower"] = burnPowermax_;
- }
- if (Helpers::hasValue(boilTemp_on_)) {
- doc["boilHystOn"] = boilTemp_on_;
- }
- if (Helpers::hasValue(boilTemp_off_)) {
- doc["boilHystOff"] = boilTemp_off_;
- }
- if (Helpers::hasValue(setFlowTemp_)) {
- doc["setFlowTemp"] = setFlowTemp_;
- }
- if (Helpers::hasValue(setWWPumpPow_)) {
- doc["wWSetPumpPower"] = setWWPumpPow_;
- }
- if (Helpers::hasValue(wWStarts_)) {
- doc["wWStarts"] = wWStarts_;
- }
- if (Helpers::hasValue(wWWorkM_)) {
- doc["wWWorkM"] = wWWorkM_;
- }
- if (Helpers::hasValue(UBAuptime_)) {
- doc["UBAuptime"] = UBAuptime_;
- }
- if (Helpers::hasValue(burnStarts_)) {
- doc["burnStarts"] = burnStarts_;
- }
- if (Helpers::hasValue(burnWorkMin_)) {
- doc["burnWorkMin"] = burnWorkMin_;
- }
- if (Helpers::hasValue(heatWorkMin_)) {
- doc["heatWorkMin"] = heatWorkMin_;
- }
- if (Helpers::hasValue(serviceCode_)) {
- doc["serviceCode"] = serviceCodeChar_;
- doc["serviceCodeNumber"] = serviceCode_;
- }
-
- // if we have data, publish it
- if (!doc.isNull()) {
- Mqtt::publish(F("boiler_data"), doc);
+ JsonObject output = doc.to();
+ if (export_values(output)) {
+ Mqtt::publish(F("boiler_data"), doc.as());
}
}
@@ -801,6 +816,7 @@ bool Boiler::set_heating_activated(const char * value, const int8_t id) {
if (!Helpers::value2bool(value, v)) {
return false;
}
+
LOG_INFO(F("Setting boiler heating "), v ? "on" : "off");
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
write_command(EMS_TYPE_UBAParametersPlus, 0, v ? 0x01 : 0, EMS_TYPE_UBAParametersPlus);
diff --git a/src/devices/boiler.h b/src/devices/boiler.h
index d1c63d244..c2bf4ee6c 100644
--- a/src/devices/boiler.h
+++ b/src/devices/boiler.h
@@ -50,6 +50,7 @@ class Boiler : public EMSdevice {
void console_commands(Shell & shell, unsigned int context);
void register_mqtt_ha_config();
void check_active();
+ bool export_values(JsonObject & doc);
uint8_t last_boilerState = 0xFF; // remember last state of heating and warm water on/off
uint8_t mqtt_format_; // single, nested or ha
@@ -76,7 +77,7 @@ class Boiler : public EMSdevice {
// MC10Status
uint16_t wwMixTemperature_ = EMS_VALUE_USHORT_NOTSET; // mengertemperatuur
- uint16_t wwBufferBoilerTemperature_ = EMS_VALUE_USHORT_NOTSET; // bufferboilertemperatuur
+ uint16_t wwBufferBoilerTemperature_ = EMS_VALUE_USHORT_NOTSET; // bufferboilertemperature
// UBAMonitorFast - 0x18 on EMS1
uint8_t selFlowTemp_ = EMS_VALUE_UINT_NOTSET; // Selected flow temperature
@@ -146,6 +147,8 @@ class Boiler : public EMSdevice {
uint8_t heating_active_ = EMS_VALUE_BOOL_NOTSET; // Central heating is on/off
uint8_t pumpMod2_ = EMS_VALUE_UINT_NOTSET; // heatpump modulation from 0xE3 (heatpumps)
+ bool command_info(const char * value, const int8_t id, JsonObject & output);
+
void process_UBAParameterWW(std::shared_ptr telegram);
void process_UBAMonitorFast(std::shared_ptr telegram);
void process_UBATotalUptime(std::shared_ptr telegram);
diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp
index 2026c158f..ca6a901e3 100644
--- a/src/devices/mixing.cpp
+++ b/src/devices/mixing.cpp
@@ -53,10 +53,24 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
register_telegram_type(0x010C, F("IPMSetMessage"), false, [&](std::shared_ptr t) { process_IPMStatusMessage(t); });
}
+
+ // API call
+ Command::add_with_json(this->device_type(), F("info"), [&](const char * value, const int8_t id, JsonObject & object) {
+ return command_info(value, id, object);
+ });
}
// add context submenu
void Mixing::add_context_menu() {
+ // TODO support for multiple mixing units from a single menu, similar to set master with thermostat
+ /*
+ EMSESPShell::commands->add_command(ShellContext::MAIN,
+ CommandFlags::USER,
+ flash_string_vector{F_(mixing)},
+ [&](Shell & shell, const std::vector & arguments __attribute__((unused))) {
+ Mixing::console_commands(shell, ShellContext::MIXING);
+ });
+ */
}
// output json to web UI
@@ -89,7 +103,24 @@ bool Mixing::updated_values() {
}
// add console commands
-void Mixing::console_commands() {
+void Mixing::console_commands(Shell & shell, unsigned int context) {
+ EMSESPShell::commands->add_command(ShellContext::MIXING,
+ CommandFlags::ADMIN,
+ flash_string_vector{F_(read)},
+ flash_string_vector{F_(typeid_mandatory)},
+ [=](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, device_id());
+ });
+
+ EMSESPShell::commands->add_command(ShellContext::MIXING,
+ CommandFlags::USER,
+ flash_string_vector{F_(show)},
+ [&](Shell & shell, const std::vector & arguments __attribute__((unused))) { show_values(shell); });
+
+ // enter the context
+ Console::enter_custom_context(shell, context);
}
// display all values into the shell console
@@ -117,49 +148,66 @@ void Mixing::show_values(uuid::console::Shell & shell) {
shell.println();
}
+bool Mixing::command_info(const char * value, const int8_t id, JsonObject & output) {
+ return (export_values(output));
+}
+
// publish values via MQTT
// ideally we should group up all the mixing units together into a nested JSON but for now we'll send them individually
void Mixing::publish_values() {
StaticJsonDocument doc;
- char s[5]; // for formatting strings
+ JsonObject output = doc.to();
+ if (export_values(output)) {
+ char topic[30];
+ char s[5];
+ strlcpy(topic, "mixing_data", 30);
+ strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic
+ Mqtt::publish(topic, doc.as());
+ }
+}
+
+// creates JSON doc from values
+// returns false if empty
+bool Mixing::export_values(JsonObject & output) {
+ char s[5]; // for formatting strings
switch (type_) {
case Type::HC:
- doc["type"] = "hc";
+ output["type"] = "hc";
if (Helpers::hasValue(flowTemp_)) {
- doc["flowTemp"] = (float)flowTemp_ / 10;
+ output["flowTemp"] = (float)flowTemp_ / 10;
}
if (Helpers::hasValue(flowSetTemp_)) {
- doc["flowSetTemp"] = flowSetTemp_;
+ output["flowSetTemp"] = flowSetTemp_;
}
if (Helpers::hasValue(pump_)) {
- doc["pumpStatus"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
+ output["pumpStatus"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(status_)) {
- doc["valveStatus"] = status_;
+ output["valveStatus"] = status_;
}
break;
+
case Type::WWC:
- doc["type"] = "wwc";
+ output["type"] = "wwc";
if (Helpers::hasValue(flowTemp_)) {
- doc["wwTemp"] = (float)flowTemp_ / 10;
+ output["wwTemp"] = (float)flowTemp_ / 10;
}
if (Helpers::hasValue(pump_)) {
- doc["pumpStatus"] = pump_;
+ output["pumpStatus"] = pump_;
}
if (Helpers::hasValue(status_)) {
- doc["tempStatus"] = status_;
+ output["tempStatus"] = status_;
}
break;
+
case Type::NONE:
default:
- return;
+ return false;
+ break;
}
- char topic[30];
- strlcpy(topic, "mixing_data", 30);
- strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic
- Mqtt::publish(topic, doc);
+ return output.size();
}
// heating circuits 0x02D7, 0x02D8 etc...
diff --git a/src/devices/mixing.h b/src/devices/mixing.h
index 2dac113d2..9795d7ff0 100644
--- a/src/devices/mixing.h
+++ b/src/devices/mixing.h
@@ -25,6 +25,7 @@
#include
#include "emsdevice.h"
+#include "emsesp.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
@@ -44,7 +45,9 @@ class Mixing : public EMSdevice {
private:
static uuid::log::Logger logger_;
- void console_commands();
+ void console_commands(Shell & shell, unsigned int context);
+ bool export_values(JsonObject & doc);
+ bool command_info(const char * value, const int8_t id, JsonObject & output);
void process_MMPLUSStatusMessage_HC(std::shared_ptr telegram);
void process_MMPLUSStatusMessage_WWC(std::shared_ptr telegram);
@@ -66,7 +69,8 @@ class Mixing : public EMSdevice {
int8_t status_ = EMS_VALUE_UINT_NOTSET;
uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET;
Type type_ = Type::NONE;
- bool changed_ = false;
+
+ bool changed_ = false;
};
} // namespace emsesp
diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp
index f563b02c3..4b6064b57 100644
--- a/src/devices/solar.cpp
+++ b/src/devices/solar.cpp
@@ -47,10 +47,28 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, [&](std::shared_ptr t) { process_ISM1StatusMessage(t); });
register_telegram_type(0x0101, F("ISM1Set"), false, [&](std::shared_ptr t) { process_ISM1Set(t); });
}
+
+ // API call
+ Command::add_with_json(this->device_type(), F("info"), [&](const char * value, const int8_t id, JsonObject & object) {
+ return command_info(value, id, object);
+ });
+}
+
+bool Solar::command_info(const char * value, const int8_t id, JsonObject & output) {
+ return (export_values(output));
}
// context submenu
void Solar::add_context_menu() {
+ // TODO support for multiple solar units from a single menu, similar to set master with thermostat
+ /*
+ EMSESPShell::commands->add_command(ShellContext::MAIN,
+ CommandFlags::USER,
+ flash_string_vector{F_(solar)},
+ [&](Shell & shell, const std::vector & arguments __attribute__((unused))) {
+ Solar::console_commands(shell, ShellContext::SOLAR);
+ });
+ */
}
// print to web
@@ -109,69 +127,74 @@ void Solar::show_values(uuid::console::Shell & shell) {
// publish values via MQTT
void Solar::publish_values() {
StaticJsonDocument doc;
+ JsonObject output = doc.to();
+ if (export_values(output)) {
+ Mqtt::publish(F("sm_data"), doc.as());
+ }
+}
+// creates JSON doc from values
+// returns false if empty
+bool Solar::export_values(JsonObject & output) {
char s[10]; // for formatting strings
if (Helpers::hasValue(collectorTemp_)) {
- doc["collectorTemp"] = (float)collectorTemp_ / 10;
+ output["collectorTemp"] = (float)collectorTemp_ / 10;
}
if (Helpers::hasValue(tankBottomTemp_)) {
- doc["tankBottomTemp"] = (float)tankBottomTemp_ / 10;
+ output["tankBottomTemp"] = (float)tankBottomTemp_ / 10;
}
if (Helpers::hasValue(tankBottomTemp2_)) {
- doc["tankBottomTemp2"] = (float)tankBottomTemp2_ / 10;
+ output["tankBottomTemp2"] = (float)tankBottomTemp2_ / 10;
}
if (Helpers::hasValue(heatExchangerTemp_)) {
- doc["heatExchangerTemp"] = (float)heatExchangerTemp_ / 10;
+ output["heatExchangerTemp"] = (float)heatExchangerTemp_ / 10;
}
if (Helpers::hasValue(solarPumpModulation_)) {
- doc["solarPumpModulation"] = solarPumpModulation_;
+ output["solarPumpModulation"] = solarPumpModulation_;
}
if (Helpers::hasValue(cylinderPumpModulation_)) {
- doc["cylinderPumpModulation"] = cylinderPumpModulation_;
+ output["cylinderPumpModulation"] = cylinderPumpModulation_;
}
if (Helpers::hasValue(solarPump_, EMS_VALUE_BOOL)) {
- doc["solarPump"] = Helpers::render_value(s, solarPump_, EMS_VALUE_BOOL);
+ output["solarPump"] = Helpers::render_value(s, solarPump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(valveStatus_, EMS_VALUE_BOOL)) {
- doc["valveStatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
+ output["valveStatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(pumpWorkMin_)) {
- doc["pumpWorkMin"] = pumpWorkMin_;
+ output["pumpWorkMin"] = pumpWorkMin_;
}
if (Helpers::hasValue(tankHeated_, EMS_VALUE_BOOL)) {
- doc["tankHeated"] = Helpers::render_value(s, tankHeated_, EMS_VALUE_BOOL);
+ output["tankHeated"] = Helpers::render_value(s, tankHeated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(collectorShutdown_, EMS_VALUE_BOOL)) {
- doc["collectorShutdown"] = Helpers::render_value(s, collectorShutdown_, EMS_VALUE_BOOL);
+ output["collectorShutdown"] = Helpers::render_value(s, collectorShutdown_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(energyLastHour_)) {
- doc["energyLastHour"] = (float)energyLastHour_ / 10;
+ output["energyLastHour"] = (float)energyLastHour_ / 10;
}
if (Helpers::hasValue(energyToday_)) {
- doc["energyToday"] = energyToday_;
+ output["energyToday"] = energyToday_;
}
if (Helpers::hasValue(energyTotal_)) {
- doc["energyTotal"] = (float)energyTotal_ / 10;
+ output["energyTotal"] = (float)energyTotal_ / 10;
}
- // if we have data, publish it
- if (!doc.isNull()) {
- Mqtt::publish(F("sm_data"), doc);
- }
+ return output.size();
}
// check to see if values have been updated
@@ -184,7 +207,24 @@ bool Solar::updated_values() {
}
// add console commands
-void Solar::console_commands() {
+void Solar::console_commands(Shell & shell, unsigned int context) {
+ EMSESPShell::commands->add_command(ShellContext::SOLAR,
+ CommandFlags::ADMIN,
+ flash_string_vector{F_(read)},
+ flash_string_vector{F_(typeid_mandatory)},
+ [=](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, device_id());
+ });
+
+ EMSESPShell::commands->add_command(ShellContext::SOLAR,
+ CommandFlags::USER,
+ flash_string_vector{F_(show)},
+ [&](Shell & shell, const std::vector & arguments __attribute__((unused))) { show_values(shell); });
+
+ // enter the context
+ Console::enter_custom_context(shell, context);
}
// SM10Monitor - type 0x97
diff --git a/src/devices/solar.h b/src/devices/solar.h
index 248cd71ec..e2f928232 100644
--- a/src/devices/solar.h
+++ b/src/devices/solar.h
@@ -25,6 +25,7 @@
#include
#include "emsdevice.h"
+#include "emsesp.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
@@ -44,7 +45,9 @@ class Solar : public EMSdevice {
private:
static uuid::log::Logger logger_;
- void console_commands();
+ void console_commands(Shell & shell, unsigned int context);
+ bool export_values(JsonObject & doc);
+ bool command_info(const char * value, const int8_t id, JsonObject & output);
int16_t collectorTemp_ = EMS_VALUE_SHORT_NOTSET; // TS1: Temperature sensor for collector array 1
int16_t tankBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // TS2: Temperature sensor 1 cylinder, bottom (solar thermal system)
@@ -65,7 +68,8 @@ class Solar : public EMSdevice {
uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t userFlag_ = EMS_VALUE_BOOL_NOTSET;
- bool changed_ = false;
+
+ bool changed_ = false;
void process_SM10Monitor(std::shared_ptr telegram);
void process_SM100Monitor(std::shared_ptr telegram);
diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp
index fbb205de9..9a04e7e8d 100644
--- a/src/devices/thermostat.cpp
+++ b/src/devices/thermostat.cpp
@@ -248,21 +248,34 @@ bool Thermostat::updated_values() {
return false;
}
+// info API command
+// returns the same MQTT publish payload in Nested format
+bool Thermostat::command_info(const char * value, const int8_t id, JsonObject & output) {
+ return (export_values(MQTT_format::NESTED, output));
+}
+
// publish values via MQTT
void Thermostat::publish_values() {
- // only publish on the master thermostat
if (EMSESP::actual_master_thermostat() != this->device_id()) {
return;
}
- uint8_t flags = this->model();
-
StaticJsonDocument doc;
- JsonObject rootThermostat = doc.to();
- JsonObject dataThermostat;
+ JsonObject output = doc.to();
+ export_values(mqtt_format_, output);
+
+ if (mqtt_format_ == MQTT_format::NESTED) {
+ Mqtt::publish(F("thermostat_data"), output);
+ }
+}
+
+// creates JSON doc from values
+// returns false if empty
+bool Thermostat::export_values(uint8_t mqtt_format, JsonObject & rootThermostat) {
+ uint8_t flags = this->model();
+ JsonObject dataThermostat;
// add external temp and other stuff specific to the RC30 and RC35
- // if ((flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) && (mqtt_format_ == MQTT_format::SINGLE || mqtt_format_ == MQTT_format::CUSTOM)) {
if (flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) {
if (datetime_.size()) {
rootThermostat["time"] = datetime_.c_str();
@@ -302,9 +315,9 @@ void Thermostat::publish_values() {
}
// send this specific data using the thermostat_data topic
- if (mqtt_format_ != MQTT_format::NESTED) {
- Mqtt::publish(F("thermostat_data"), doc);
- rootThermostat = doc.to(); // clear object
+ if (mqtt_format != MQTT_format::NESTED) {
+ Mqtt::publish(F("thermostat_data"), rootThermostat);
+ rootThermostat.clear(); // clear object
}
}
@@ -315,8 +328,7 @@ void Thermostat::publish_values() {
has_data = true;
// if the MQTT format is 'nested' or 'ha' then create the parent object hc
- // if (mqtt_format_ != MQTT_format::SINGLE) {
- if ((mqtt_format_ == MQTT_format::NESTED) || (mqtt_format_ == MQTT_format::HA)) {
+ if ((mqtt_format == MQTT_format::NESTED) || (mqtt_format == MQTT_format::HA)) {
char hc_name[10]; // hc{1-4}
strlcpy(hc_name, "hc", 10);
char s[3];
@@ -397,11 +409,11 @@ void Thermostat::publish_values() {
dataThermostat["summertemp"] = hc->summertemp;
}
- // when using HA always send the mode otherwise it'll may break the component/widget and report an error
- if ((Helpers::hasValue(hc->mode)) || (mqtt_format_ == MQTT_format::HA)) {
+ // mode - always force showing this when in HA so not to break HA's climate component
+ if ((Helpers::hasValue(hc->mode)) || (mqtt_format == MQTT_format::HA)) {
uint8_t hc_mode = hc->get_mode(flags);
// if we're sending to HA the only valid mode types are heat, auto and off
- if (mqtt_format_ == MQTT_format::HA) {
+ if (mqtt_format == MQTT_format::HA) {
if ((hc_mode == HeatingCircuit::Mode::MANUAL) || (hc_mode == HeatingCircuit::Mode::DAY)) {
hc_mode = HeatingCircuit::Mode::HEAT;
} else if ((hc_mode == HeatingCircuit::Mode::NIGHT) || (hc_mode == HeatingCircuit::Mode::OFF)) {
@@ -425,15 +437,13 @@ void Thermostat::publish_values() {
// if format is single, send immediately and clear object for next hc
// the topic will have the hc number appended
- // if (mqtt_format_ == MQTT_format::SINGLE) {
- if ((mqtt_format_ == MQTT_format::SINGLE) || (mqtt_format_ == MQTT_format::CUSTOM)) {
+ if ((mqtt_format == MQTT_format::SINGLE) || (mqtt_format == MQTT_format::CUSTOM)) {
char topic[30];
char s[3];
strlcpy(topic, "thermostat_data", 30);
strlcat(topic, Helpers::itoa(s, hc->hc_num()), 30); // append hc to topic
- Mqtt::publish(topic, doc);
- rootThermostat = doc.to(); // clear object
- } else if (mqtt_format_ == MQTT_format::HA) {
+ rootThermostat.clear(); // clear object
+ } else if (mqtt_format == MQTT_format::HA) {
// see if we have already registered this with HA MQTT Discovery, if not send the config
if (!hc->ha_registered()) {
register_mqtt_ha_config(hc->hc_num());
@@ -442,19 +452,12 @@ void Thermostat::publish_values() {
// send the thermostat topic and payload data
std::string topic(100, '\0');
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/hc%d/state"), hc->hc_num());
- Mqtt::publish(topic, doc);
+ Mqtt::publish(topic, rootThermostat);
}
}
}
- if (!has_data) {
- return; // nothing to send, quit
- }
-
- // if we're using nested json, send all in one go under one topic called thermostat_data
- if (mqtt_format_ == MQTT_format::NESTED) {
- Mqtt::publish(F("thermostat_data"), doc);
- }
+ return (has_data);
}
// returns the heating circuit object based on the hc number
@@ -599,7 +602,7 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
std::string topic(100, '\0'); // e.g homeassistant/climate/hc1/thermostat/config
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/hc%d/config"), hc_num);
// Mqtt::publish(topic); // empty payload, this remove any previous config sent to HA
- Mqtt::publish_retain(topic, doc, true); // publish the config payload with retain flag
+ Mqtt::publish_retain(topic, doc.as(), true); // publish the config payload with retain flag
// subscribe to the temp and mode commands
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/hc%d/cmd_temp"), hc_num);
@@ -1922,8 +1925,13 @@ 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
+// API commands for MQTT and Console
void Thermostat::add_commands() {
+ // API call
+ Command::add_with_json(this->device_type(), F("info"), [&](const char * value, const int8_t id, JsonObject & object) {
+ return command_info(value, id, object);
+ });
+
// if this thermostat doesn't support write, don't add the commands
if ((this->flags() & EMSdevice::EMS_DEVICE_FLAG_NO_WRITE) == EMSdevice::EMS_DEVICE_FLAG_NO_WRITE) {
return;
diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h
index 39fa79b60..78dc51a60 100644
--- a/src/devices/thermostat.h
+++ b/src/devices/thermostat.h
@@ -108,6 +108,7 @@ class Thermostat : public EMSdevice {
void console_commands(Shell & shell, unsigned int context);
void add_commands();
+ bool export_values(uint8_t mqtt_format, JsonObject & doc);
// specific thermostat characteristics, stripping the option bits at pos 6 and 7
inline uint8_t model() const {
@@ -221,6 +222,7 @@ class Thermostat : public EMSdevice {
std::shared_ptr heating_circuit(const uint8_t hc_num);
void register_mqtt_ha_config(uint8_t hc_num);
+ bool command_info(const char * value, const int8_t id, JsonObject & output);
void process_RCOutdoorTemp(std::shared_ptr telegram);
void process_IBASettings(std::shared_ptr telegram);
diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp
index af6fb9180..72039285b 100644
--- a/src/emsdevice.cpp
+++ b/src/emsdevice.cpp
@@ -111,6 +111,10 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
return DeviceType::MIXING;
}
+ if (strcmp(topic, "sensor") == 0) {
+ return DeviceType::SENSOR;
+ }
+
return DeviceType::UNKNOWN;
}
@@ -286,7 +290,7 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_
// 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);
+ Command::add(this->device_type_, this->device_id_, cmd, f);
}
// register a call back function for a specific telegram type
diff --git a/src/emsdevice.h b/src/emsdevice.h
index 6293ed5ed..d26e05a11 100644
--- a/src/emsdevice.h
+++ b/src/emsdevice.h
@@ -229,6 +229,7 @@ class EMSdevice {
enum DeviceType : uint8_t {
SERVICEKEY = 0, // this is us (EMS-ESP)
+ SENSOR, // for internal dallas sensors
BOILER,
THERMOSTAT,
MIXING,
diff --git a/src/emsesp.cpp b/src/emsesp.cpp
index 795da3842..16521d95b 100644
--- a/src/emsesp.cpp
+++ b/src/emsesp.cpp
@@ -51,7 +51,7 @@ TxService EMSESP::txservice_; // outgoing Telegram Tx handler
Mqtt EMSESP::mqtt_; // mqtt handler
System EMSESP::system_; // core system services
Console EMSESP::console_; // telnet and serial console
-Sensors EMSESP::sensors_; // Dallas sensors
+Sensor EMSESP::sensor_; // Dallas sensors
Shower EMSESP::shower_; // Shower logic
// static/common variables
@@ -311,8 +311,8 @@ void EMSESP::publish_other_values() {
void EMSESP::publish_sensor_values(const bool force) {
if (Mqtt::connected()) {
- if (sensors_.updated_values() || force) {
- sensors_.publish_values();
+ if (sensor_.updated_values() || force) {
+ sensor_.publish_values();
}
}
}
@@ -337,7 +337,7 @@ void EMSESP::publish_response(std::shared_ptr telegram) {
doc["value"] = value;
}
- Mqtt::publish(F("response"), doc);
+ Mqtt::publish(F("response"), doc.as());
}
// search for recognized device_ids : Me, All, otherwise print hex value
@@ -845,7 +845,7 @@ void EMSESP::start() {
mqtt_.start(); // mqtt init
system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings.
shower_.start(); // initialize shower timer and shower alert
- sensors_.start(); // dallas external sensors
+ sensor_.start(); // dallas external sensors
webServer.begin(); // start web server
emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem
@@ -868,7 +868,7 @@ void EMSESP::loop() {
system_.loop(); // does LED and checks system health, and syslog service
shower_.loop(); // check for shower on/off
- sensors_.loop(); // this will also send out via MQTT
+ sensor_.loop(); // this will also send out via MQTT
mqtt_.loop(); // sends out anything in the queue via MQTT
console_.loop(); // telnet/serial console
rxservice_.loop(); // process any incoming Rx telegrams
diff --git a/src/emsesp.h b/src/emsesp.h
index 6bf913c5e..181c84ce7 100644
--- a/src/emsesp.h
+++ b/src/emsesp.h
@@ -44,7 +44,7 @@
#include "telegram.h"
#include "mqtt.h"
#include "system.h"
-#include "sensors.h"
+#include "sensor.h"
#include "console.h"
#include "shower.h"
#include "roomcontrol.h"
@@ -108,8 +108,8 @@ class EMSESP {
static void incoming_telegram(uint8_t * data, const uint8_t length);
- static const std::vector sensor_devices() {
- return sensors_.devices();
+ static const std::vector sensor_devices() {
+ return sensor_.devices();
}
enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW };
@@ -153,7 +153,7 @@ class EMSESP {
// services
static Mqtt mqtt_;
static System system_;
- static Sensors sensors_;
+ static Sensor sensor_;
static Console console_;
static Shower shower_;
static RxService rxservice_;
diff --git a/src/locale_EN.h b/src/locale_EN.h
index 169d18f6c..783ffa1f9 100644
--- a/src/locale_EN.h
+++ b/src/locale_EN.h
@@ -88,7 +88,7 @@ MAKE_PSTR_WORD(connect)
MAKE_PSTR_WORD(heatpump)
// dallas sensors
-MAKE_PSTR_WORD(sensors)
+MAKE_PSTR_WORD(sensor)
MAKE_PSTR(kwh, "kWh")
MAKE_PSTR(wh, "Wh")
@@ -99,7 +99,8 @@ MAKE_PSTR(hostname_fmt, "WiFi Hostname = %s")
MAKE_PSTR(mark_interval_fmt, "Mark interval = %lus")
MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID = %s")
MAKE_PSTR(wifi_password_fmt, "WiFi Password = %S")
-MAKE_PSTR(system_heartbeat_fmt, "MQTT Heartbeat is %s")
+MAKE_PSTR(mqtt_heartbeat_fmt, "MQTT Heartbeat is %s")
+MAKE_PSTR(mqtt_format_fmt, "MQTT Format is %d")
MAKE_PSTR(cmd_optional, "[cmd]")
MAKE_PSTR(deep_optional, "[deep]")
MAKE_PSTR(tx_mode_fmt, "Tx mode = %d")
diff --git a/src/mqtt.cpp b/src/mqtt.cpp
index d3cc9e36c..2466baf19 100644
--- a/src/mqtt.cpp
+++ b/src/mqtt.cpp
@@ -28,7 +28,6 @@ AsyncMqttClient * Mqtt::mqttClient_;
std::string Mqtt::hostname_;
uint8_t Mqtt::mqtt_qos_;
bool Mqtt::mqtt_retain_;
-uint8_t Mqtt::bus_id_;
uint32_t Mqtt::publish_time_boiler_;
uint32_t Mqtt::publish_time_thermostat_;
uint32_t Mqtt::publish_time_solar_;
@@ -148,9 +147,10 @@ void Mqtt::loop() {
void Mqtt::show_mqtt(uuid::console::Shell & shell) {
shell.printfln(F("MQTT is %s"), connected() ? uuid::read_flash_string(F_(connected)).c_str() : uuid::read_flash_string(F_(disconnected)).c_str());
- bool system_heartbeat;
- EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { system_heartbeat = settings.system_heartbeat; });
- shell.printfln(F_(system_heartbeat_fmt), system_heartbeat ? F_(enabled) : F_(disabled));
+ EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
+ shell.printfln(F_(mqtt_heartbeat_fmt), settings.system_heartbeat ? F_(enabled) : F_(disabled));
+ shell.printfln(F_(mqtt_format_fmt), settings.mqtt_format);
+ });
shell.printfln(F("MQTT publish fails count: %lu"), mqtt_publish_fails_);
shell.println();
@@ -253,14 +253,19 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
bool cmd_known = false;
JsonVariant data = doc["data"];
+
+ JsonObject output; // empty object
+
if (data.is()) {
- cmd_known = Command::call_command(mf.device_type_, command, data.as(), n);
+ cmd_known = Command::call(mf.device_type_, command, data.as(), n, output);
} else if (data.is()) {
char data_str[10];
- cmd_known = Command::call_command(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as()), n);
+ cmd_known = Command::call(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as()), n, output);
} else if (data.is()) {
char data_str[10];
- cmd_known = Command::call_command(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as(), 2), n);
+ cmd_known = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as(), 2), n, output);
+ } else if (data.isNull()) {
+ cmd_known = Command::call(mf.device_type_, command, "", n, output);
}
if (!cmd_known) {
@@ -336,8 +341,6 @@ void Mqtt::start() {
mqtt_retain_ = mqttSettings.mqtt_retain;
});
- EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { bus_id_ = settings.ems_bus_id; });
-
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
mqttClient_->onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
@@ -443,7 +446,7 @@ void Mqtt::on_connect() {
#ifndef EMSESP_STANDALONE
doc["ip"] = WiFi.localIP().toString();
#endif
- publish(F("info"), doc);
+ publish(F("info"), doc.as());
publish_retain(F("status"), "online", true); // say we're alive to the Last Will topic, with retain on
@@ -451,10 +454,6 @@ 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" 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"));
}
@@ -470,12 +469,14 @@ std::shared_ptr Mqtt::queue_message(const uint8_t operation,
std::shared_ptr message;
if ((strncmp(topic.c_str(), "homeassistant/", 13) == 0)) {
// leave topic as it is
- message = std::make_shared(operation, topic, std::move(payload), retain);
+ // message = std::make_shared(operation, topic, std::move(payload), retain);
+ message = std::make_shared(operation, topic, payload, retain);
+
} else {
// prefix the hostname
std::string full_topic(50, '\0');
snprintf_P(&full_topic[0], full_topic.capacity() + 1, PSTR("%s/%s"), Mqtt::hostname_.c_str(), topic.c_str());
- message = std::make_shared(operation, full_topic, std::move(payload), retain);
+ message = std::make_shared(operation, full_topic, payload, retain);
}
// if the queue is full, make room but removing the last one
@@ -507,7 +508,7 @@ void Mqtt::publish(const __FlashStringHelper * topic, const std::string & payloa
queue_publish_message(uuid::read_flash_string(topic), payload, mqtt_retain_);
}
-void Mqtt::publish(const __FlashStringHelper * topic, const JsonDocument & payload) {
+void Mqtt::publish(const __FlashStringHelper * topic, const JsonObject & payload) {
publish(uuid::read_flash_string(topic), payload);
}
@@ -516,17 +517,17 @@ void Mqtt::publish_retain(const __FlashStringHelper * topic, const std::string &
queue_publish_message(uuid::read_flash_string(topic), payload, retain);
}
-void Mqtt::publish_retain(const std::string & topic, const JsonDocument & payload, bool retain) {
+void Mqtt::publish_retain(const std::string & topic, const JsonObject & payload, bool retain) {
std::string payload_text;
serializeJson(payload, payload_text); // convert json to string
queue_publish_message(topic, payload_text, retain);
}
-void Mqtt::publish_retain(const __FlashStringHelper * topic, const JsonDocument & payload, bool retain) {
+void Mqtt::publish_retain(const __FlashStringHelper * topic, const JsonObject & payload, bool retain) {
publish_retain(uuid::read_flash_string(topic), payload, retain);
}
-void Mqtt::publish(const std::string & topic, const JsonDocument & payload) {
+void Mqtt::publish(const std::string & topic, const JsonObject & payload) {
std::string payload_text;
serializeJson(payload, payload_text); // convert json to string
queue_publish_message(topic, payload_text, mqtt_retain_);
diff --git a/src/mqtt.h b/src/mqtt.h
index 1abafd70e..a1bf1725d 100644
--- a/src/mqtt.h
+++ b/src/mqtt.h
@@ -45,7 +45,7 @@ using uuid::console::Shell;
namespace emsesp {
using mqtt_subfunction_p = std::function;
-using cmdfunction_p = std::function;
+using cmdfunction_p = std::function;
struct MqttMessage {
~MqttMessage() = default;
@@ -55,7 +55,8 @@ struct MqttMessage {
const std::string payload;
const bool retain;
- MqttMessage(const uint8_t operation, const std::string & topic, const std::string && payload, bool retain)
+ // MqttMessage(const uint8_t operation, const std::string & topic, const std::string && payload, bool retain)
+ MqttMessage(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain)
: operation(operation)
, topic(topic)
, payload(payload)
@@ -89,16 +90,16 @@ class Mqtt {
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);
- static void publish(const __FlashStringHelper * topic, const JsonDocument & payload);
+ static void publish(const std::string & topic, const JsonObject & payload);
+ static void publish(const __FlashStringHelper * topic, const JsonObject & payload);
static void publish(const __FlashStringHelper * topic, const std::string & payload);
static void publish(const std::string & topic, const bool value);
static void publish(const __FlashStringHelper * topi, const bool value);
static void publish(const std::string & topic);
- static void publish_retain(const std::string & topic, const JsonDocument & payload, bool retain);
+ static void publish_retain(const std::string & topic, const JsonObject & payload, bool retain);
static void publish_retain(const __FlashStringHelper * topic, const std::string & payload, bool retain);
- static void publish_retain(const __FlashStringHelper * topic, const JsonDocument & payload, bool retain);
+ static void publish_retain(const __FlashStringHelper * topic, const JsonObject & payload, bool retain);
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
static void show_mqtt(uuid::console::Shell & shell);
@@ -194,7 +195,6 @@ class Mqtt {
static uint8_t mqtt_qos_;
static bool mqtt_retain_;
static uint32_t publish_time_;
- static uint8_t bus_id_;
static uint32_t publish_time_boiler_;
static uint32_t publish_time_thermostat_;
static uint32_t publish_time_solar_;
diff --git a/src/sensors.cpp b/src/sensor.cpp
similarity index 86%
rename from src/sensors.cpp
rename to src/sensor.cpp
index d98152bfb..42db2b18c 100644
--- a/src/sensors.cpp
+++ b/src/sensor.cpp
@@ -18,7 +18,7 @@
// code originally written by nomis - https://github.com/nomis
-#include "sensors.h"
+#include "sensor.h"
#include "emsesp.h"
#ifdef ESP32
@@ -29,10 +29,10 @@
namespace emsesp {
-uuid::log::Logger Sensors::logger_{F_(sensors), uuid::log::Facility::DAEMON};
+uuid::log::Logger Sensor::logger_{F_(sensor), uuid::log::Facility::DAEMON};
// start the 1-wire
-void Sensors::start() {
+void Sensor::start() {
reload();
#ifndef EMSESP_STANDALONE
@@ -40,10 +40,15 @@ void Sensors::start() {
bus_.begin(dallas_gpio_);
}
#endif
+
+ // API call
+ Command::add_with_json(EMSdevice::DeviceType::SENSOR, F("info"), [&](const char * value, const int8_t id, JsonObject & object) {
+ return command_info(value, id, object);
+ });
}
// load the MQTT settings
-void Sensors::reload() {
+void Sensor::reload() {
// copy over values from MQTT so we don't keep on quering the filesystem
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
mqtt_format_ = settings.mqtt_format; // single, nested or ha
@@ -60,7 +65,7 @@ void Sensors::reload() {
}
}
-void Sensors::loop() {
+void Sensor::loop() {
#ifndef EMSESP_STANDALONE
uint32_t time_now = uuid::get_uptime();
@@ -157,7 +162,7 @@ void Sensors::loop() {
#endif
}
-bool Sensors::temperature_convert_complete() {
+bool Sensor::temperature_convert_complete() {
#ifndef EMSESP_STANDALONE
if (parasite_) {
return true; // don't care, use the minimum time in loop
@@ -171,7 +176,7 @@ bool Sensors::temperature_convert_complete() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
-float Sensors::get_temperature_c(const uint8_t addr[]) {
+float Sensor::get_temperature_c(const uint8_t addr[]) {
#ifndef EMSESP_STANDALONE
if (!bus_.reset()) {
LOG_ERROR(F("Bus reset failed before reading scratchpad from %s"), Device(addr).to_string().c_str());
@@ -233,20 +238,20 @@ float Sensors::get_temperature_c(const uint8_t addr[]) {
#pragma GCC diagnostic pop
-const std::vector Sensors::devices() const {
+const std::vector Sensor::devices() const {
return devices_;
}
-Sensors::Device::Device(const uint8_t addr[])
+Sensor::Device::Device(const uint8_t addr[])
: id_(((uint64_t)addr[0] << 56) | ((uint64_t)addr[1] << 48) | ((uint64_t)addr[2] << 40) | ((uint64_t)addr[3] << 32) | ((uint64_t)addr[4] << 24)
| ((uint64_t)addr[5] << 16) | ((uint64_t)addr[6] << 8) | (uint64_t)addr[7]) {
}
-uint64_t Sensors::Device::id() const {
+uint64_t Sensor::Device::id() const {
return id_;
}
-std::string Sensors::Device::to_string() const {
+std::string Sensor::Device::to_string() const {
std::string str(20, '\0');
snprintf_P(&str[0],
str.capacity() + 1,
@@ -260,7 +265,7 @@ std::string Sensors::Device::to_string() const {
}
// check to see if values have been updated
-bool Sensors::updated_values() {
+bool Sensor::updated_values() {
if (changed_) {
changed_ = false;
return true;
@@ -268,9 +273,35 @@ bool Sensors::updated_values() {
return false;
}
+bool Sensor::command_info(const char * value, const int8_t id, JsonObject & output) {
+ return (export_values(output));
+}
+
+// creates JSON doc from values
+// returns false if empty
+// e.g. sensors = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"23.30"},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":"24.0"}}
+bool Sensor::export_values(JsonObject & output) {
+ if (devices_.size() == 0) {
+ return false;
+ }
+ uint8_t i = 1; // sensor count
+ for (const auto & device : devices_) {
+ char s[7];
+ char sensorID[10]; // sensor{1-n}
+ strlcpy(sensorID, "sensor", 10);
+ strlcat(sensorID, Helpers::itoa(s, i), 10);
+ JsonObject dataSensor = output.createNestedObject(sensorID);
+ dataSensor["id"] = device.to_string();
+ dataSensor["temp"] = Helpers::render_value(s, device.temperature_c, 1);
+ i++;
+ }
+
+ return true;
+}
+
// send all dallas sensor values as a JSON package to MQTT
// assumes there are devices
-void Sensors::publish_values() {
+void Sensor::publish_values() {
uint8_t num_devices = devices_.size();
if (num_devices == 0) {
@@ -288,7 +319,7 @@ void Sensors::publish_values() {
char topic[60]; // sensors{1-n}
strlcpy(topic, "sensor_", 50); // create topic, e.g. home/ems-esp/sensor_28-EA41-9497-0E03-5F
strlcat(topic, device.to_string().c_str(), 60);
- Mqtt::publish(topic, doc);
+ Mqtt::publish(topic, doc.as());
doc.clear(); // clear json doc so we can reuse the buffer again
}
return;
@@ -342,7 +373,7 @@ void Sensors::publish_values() {
config["uniq_id"] = str;
snprintf_P(&topic[0], 50, PSTR("homeassistant/sensor/ems-esp/sensor%d/config"), i);
- Mqtt::publish_retain(topic, config, false); // publish the config payload with no retain flag
+ Mqtt::publish_retain(topic, config.as(), false); // publish the config payload with no retain flag
registered_ha_[i] = true;
}
@@ -351,9 +382,10 @@ void Sensors::publish_values() {
}
if ((mqtt_format_ == MQTT_format::NESTED) || (mqtt_format_ == MQTT_format::CUSTOM)) {
- Mqtt::publish(F("sensors"), doc);
+ Mqtt::publish(F("sensors"), doc.as());
} else if (mqtt_format_ == MQTT_format::HA) {
- Mqtt::publish(F("homeassistant/sensor/ems-esp/state"), doc);
+ Mqtt::publish(F("homeassistant/sensor/ems-esp/state"), doc.as());
}
}
+
} // namespace emsesp
\ No newline at end of file
diff --git a/src/sensors.h b/src/sensor.h
similarity index 93%
rename from src/sensors.h
rename to src/sensor.h
index 8da3a18a4..e9e0b09a3 100644
--- a/src/sensors.h
+++ b/src/sensor.h
@@ -18,8 +18,8 @@
// code originally written by nomis - https://github.com/nomis
-#ifndef EMSESP_SENSORS_H
-#define EMSESP_SENSORS_H
+#ifndef EMSESP_SENSOR_H
+#define EMSESP_SENSOR_H
#include
#include
@@ -36,7 +36,7 @@
namespace emsesp {
-class Sensors {
+class Sensor {
public:
class Device {
public:
@@ -53,8 +53,8 @@ class Sensors {
bool registered_ = false;
};
- Sensors() = default;
- ~Sensors() = default;
+ Sensor() = default;
+ ~Sensor() = default;
void start();
void loop();
@@ -62,6 +62,9 @@ class Sensors {
void reload();
bool updated_values();
+ bool command_info(const char * value, const int8_t id, JsonObject & output);
+ bool export_values(JsonObject & doc);
+
const std::vector devices() const;
private:
diff --git a/src/shower.cpp b/src/shower.cpp
index 4451e0a71..f6fc1be51 100644
--- a/src/shower.cpp
+++ b/src/shower.cpp
@@ -129,7 +129,7 @@ void Shower::publish_values() {
doc["duration"] = s;
}
- Mqtt::publish(F("shower_data"), doc);
+ Mqtt::publish(F("shower_data"), doc.as());
}
} // namespace emsesp
diff --git a/src/system.cpp b/src/system.cpp
index eb8e70ede..7e2d33988 100644
--- a/src/system.cpp
+++ b/src/system.cpp
@@ -152,8 +152,16 @@ void System::start() {
EMSESP::esp8266React.getWiFiSettingsService()->read(
[&](WiFiSettings & wifiSettings) { LOG_INFO(F("System %s booted (EMS-ESP version %s)"), wifiSettings.hostname.c_str(), EMSESP_APP_VERSION); });
- syslog_init(); // init SysLog
- set_led(); // init LED
+ syslog_init(); // init SysLog
+ set_led(); // init LED
+
+ // these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""}
+ EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
+ Command::add(EMSdevice::DeviceType::SERVICEKEY, settings.ems_bus_id, F("pin"), System::command_pin);
+ Command::add(EMSdevice::DeviceType::SERVICEKEY, settings.ems_bus_id, F("send"), System::command_send);
+ Command::add_with_json(EMSdevice::DeviceType::SERVICEKEY, F("info"), System::command_info);
+ });
+
EMSESP::init_tx(); // start UART
}
@@ -241,7 +249,7 @@ void System::send_heartbeat() {
doc["rxfails"] = EMSESP::rxservice_.telegram_error_count();
doc["adc"] = analog_; //analogRead(A0);
- Mqtt::publish_retain(F("heartbeat"), doc, false); // send to MQTT with retain off. This will add to MQTT queue.
+ Mqtt::publish_retain(F("heartbeat"), doc.as(), false); // send to MQTT with retain off. This will add to MQTT queue.
}
// measure and moving average adc
@@ -806,14 +814,14 @@ bool System::check_upgrade() {
// 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();
+// value and id are ignored
+bool System::command_info(const char * value, const int8_t id, JsonObject & output) {
+#ifdef EMSESP_STANDALONE
+ output["test"] = "testing";
+#else
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & settings) {
- JsonObject node = root.createNestedObject("WIFI");
+ JsonObject node = output.createNestedObject("WIFI");
node["ssid"] = settings.ssid;
// node["password"] = settings.password;
node["hostname"] = settings.hostname;
@@ -826,7 +834,7 @@ String System::export_settings() {
});
EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) {
- JsonObject node = root.createNestedObject("AP");
+ JsonObject node = output.createNestedObject("AP");
node["provision_mode"] = settings.provisionMode;
node["ssid"] = settings.ssid;
// node["password"] = settings.password;
@@ -836,7 +844,7 @@ String System::export_settings() {
});
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
- JsonObject node = root.createNestedObject("MQTT");
+ JsonObject node = output.createNestedObject("MQTT");
node["enabled"] = settings.enabled;
node["host"] = settings.host;
node["port"] = settings.port;
@@ -859,7 +867,7 @@ String System::export_settings() {
});
EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) {
- JsonObject node = root.createNestedObject("NTP");
+ JsonObject node = output.createNestedObject("NTP");
node["enabled"] = settings.enabled;
node["server"] = settings.server;
node["tz_label"] = settings.tzLabel;
@@ -867,18 +875,33 @@ String System::export_settings() {
});
EMSESP::esp8266React.getOTASettingsService()->read([&](OTASettings & settings) {
- JsonObject node = root.createNestedObject("OTA");
+ JsonObject node = output.createNestedObject("OTA");
node["enabled"] = settings.enabled;
node["port"] = settings.port;
// node["password"] = settings.password;
});
+ EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
+ JsonObject node = output.createNestedObject("Settings");
+ node["tx_mode"] = settings.tx_mode;
+ node["ems_bus_id"] = settings.ems_bus_id;
+ node["syslog_level"] = settings.syslog_level;
+ node["syslog_mark_interval"] = settings.syslog_mark_interval;
+ node["syslog_host"] = settings.syslog_host;
+ node["master_thermostat"] = settings.master_thermostat;
+ node["shower_timer"] = settings.shower_timer;
+ node["shower_alert"] = settings.shower_alert;
+ node["rx_gpio"] = settings.rx_gpio;
+ node["tx_gpio"] = settings.tx_gpio;
+ node["dallas_gpio"] = settings.dallas_gpio;
+ node["dallas_parasite"] = settings.dallas_parasite;
+ node["led_gpio"] = settings.led_gpio;
+ node["hide_led"] = settings.hide_led;
+ });
+
#endif
-
- String buffer;
- serializeJsonPretty(doc, buffer);
-
- return buffer;
+ return true;
}
+
} // namespace emsesp
diff --git a/src/system.h b/src/system.h
index 22c13a141..e23931512 100644
--- a/src/system.h
+++ b/src/system.h
@@ -50,12 +50,13 @@ class System {
static bool command_pin(const char * value, const int8_t id);
static bool command_send(const char * value, const int8_t id);
+ static bool command_info(const char * value, const int8_t id, JsonObject & output);
+
static uint8_t free_mem();
static void upload_status(bool in_progress);
static bool upload_status();
static void show_mem(const char * note);
static void set_led();
- static String export_settings();
bool check_upgrade();
void syslog_init();
diff --git a/src/test/test.cpp b/src/test/test.cpp
index 04507f840..2be27f10b 100644
--- a/src/test/test.cpp
+++ b/src/test/test.cpp
@@ -28,7 +28,7 @@ namespace emsesp {
// used with the 'test' command, under su/admin
void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "default") {
- run_test(shell, "mqtt"); // add the default test case here
+ run_test(shell, "cmd"); // add the default test case here
}
if (command.empty()) {
@@ -565,6 +565,45 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
EMSESP::txservice_.flush_tx_queue();
}
+ if (command == "cmd") {
+ shell.printfln(F("Testing Commands..."));
+
+ // change MQTT format
+ EMSESP::esp8266React.getMqttSettingsService()->updateWithoutPropagation([&](MqttSettings & mqttSettings) {
+ mqttSettings.mqtt_format = MQTT_format::SINGLE;
+ // mqttSettings.mqtt_format = MQTT_format::NESTED;
+ // mqttSettings.mqtt_format = MQTT_format::HA;
+ return StateUpdateResult::CHANGED;
+ });
+
+ EMSESP::add_context_menus(); // need to add this as it happens later in the code
+ shell.invoke_command("su");
+ shell.invoke_command("system");
+ shell.invoke_command("call");
+ shell.invoke_command("call info");
+ shell.invoke_command("exit");
+
+ char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
+ strcpy(system_topic, "ems-esp/system");
+ EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"info\"}"); // this should fail
+
+ // add a thermostat with 3 HCs
+ std::string version("1.2.3");
+ EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
+ uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
+ 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
+ uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
+ 0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC2
+ uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // HC3
+ EMSESP::add_context_menus(); // need to add this as it happens later in the code
+ shell.invoke_command("thermostat");
+ shell.invoke_command("show");
+ shell.invoke_command("call");
+ shell.invoke_command("call info");
+ shell.invoke_command("exit");
+ shell.invoke_command("show mqtt");
+ }
+
if (command == "pin") {
shell.printfln(F("Testing pin..."));
@@ -579,6 +618,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
if (command == "mqtt") {
shell.printfln(F("Testing MQTT..."));
+ // EMSESP::esp8266React.getMqttSettingsService()->updateWithoutPropagation([&](MqttSettings & mqttSettings) {
+ // mqttSettings.mqtt_format = MQTT_format::SINGLE;
+ // return StateUpdateResult::CHANGED;
+ // });
+
// add a boiler
// question: do we need to set the mask?
std::string version("1.2.3");
@@ -700,6 +744,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
// check for error "[emsesp] No telegram type handler found for ID 0x255 (src 0x20, dest 0x00)"
rx_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A});
+
+ EMSESP::add_context_menus(); // need to add this as it happens later in the code
}
// finally dump to console