Merge remote-tracking branch 'origin/dev' into main

This commit is contained in:
proddy
2021-05-04 12:21:51 +02:00
90 changed files with 13231 additions and 6082 deletions

View File

@@ -28,10 +28,6 @@ WebAPIService::WebAPIService(AsyncWebServer * server) {
// e.g. http://ems-esp/api?device=boiler&cmd=wwtemp&data=20&id=1
void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
// see if the API is enabled
bool api_enabled;
EMSESP::webSettingsService.read([&](WebSettings & 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"));
@@ -49,12 +45,6 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
// get cmd, we know we have one
String cmd = request->getParam(F_(cmd))->value();
// look up command in our list
if (Command::find_command(device_type, cmd.c_str()) == nullptr) {
request->send(400, "text/plain", F("Invalid cmd"));
return;
}
String data;
if (request->hasParam(F_(data))) {
data = request->getParam(F_(data))->value();
@@ -77,8 +67,10 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
if (data.isEmpty()) {
ok = Command::call(device_type, cmd.c_str(), nullptr, id.toInt(), json); // command only
} else {
// we only allow commands with parameters if the API is enabled
bool api_enabled;
EMSESP::webSettingsService.read([&](WebSettings & settings) { api_enabled = settings.api_enabled; });
if (api_enabled) {
// we only allow commands with parameters if the API is enabled
ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), json); // has cmd, data and id
} else {
request->send(401, "text/plain", F("Unauthorized"));
@@ -90,7 +82,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
doc.shrinkToFit();
std::string buffer;
serializeJsonPretty(doc, buffer);
request->send(200, "text/plain", buffer.c_str());
request->send(200, "text/plain;charset=utf-8", buffer.c_str());
return;
}
request->send(200, "text/plain", ok ? F("OK") : F("Invalid"));

View File

@@ -23,14 +23,18 @@ namespace emsesp {
using namespace std::placeholders; // for `_1` etc
WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager)
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) {
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
, _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) {
server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
server->on(SCAN_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
_device_dataHandler.setMethod(HTTP_POST);
_device_dataHandler.setMaxContentLength(256);
server->addHandler(&_device_dataHandler);
_writevalue_dataHandler.setMethod(HTTP_POST);
_writevalue_dataHandler.setMaxContentLength(256);
server->addHandler(&_writevalue_dataHandler);
}
void WebDevicesService::scan_devices(AsyncWebServerRequest * request) {
@@ -96,4 +100,49 @@ void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant
request->send(response);
}
// takes a command and its data value from a specific Device, from the Web
void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
// only issue commands if the API is enabled
EMSESP::webSettingsService.read([&](WebSettings & settings) {
if (!settings.api_enabled) {
request->send(403); // forbidden error
return;
}
});
if (json.is<JsonObject>()) {
JsonObject dv = json["devicevalue"];
// using the unique ID from the web find the real device type
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice) {
if (emsdevice->unique_id() == dv["id"].as<int>()) {
const char * cmd = dv["cmd"];
uint8_t device_type = emsdevice->device_type();
bool ok = false;
char s[10];
// the data could be in any format, but we need string
JsonVariant data = dv["data"];
if (data.is<char *>()) {
ok = Command::call(device_type, cmd, data.as<const char *>());
} else if (data.is<int>()) {
ok = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0));
} else if (data.is<float>()) {
ok = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as<float>(), 1));
} else if (data.is<bool>()) {
ok = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false");
}
if (ok) {
request->send(200);
}
return; // found device, quit
}
}
}
request->send(204); // no content error
}
}
} // namespace emsesp

View File

@@ -27,6 +27,8 @@
#define EMSESP_DEVICES_SERVICE_PATH "/rest/allDevices"
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
#define WRITE_VALUE_SERVICE_PATH "/rest/writeValue"
namespace emsesp {
@@ -35,11 +37,15 @@ class WebDevicesService {
WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager);
private:
// GET
void all_devices(AsyncWebServerRequest * request);
void scan_devices(AsyncWebServerRequest * request);
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
AsyncCallbackJsonWebHandler _device_dataHandler;
// POST
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
void write_value(AsyncWebServerRequest * request, JsonVariant & json);
AsyncCallbackJsonWebHandler _device_dataHandler, _writevalue_dataHandler;
};
} // namespace emsesp

View File

@@ -22,39 +22,12 @@
#include <HttpEndpoint.h>
#include <FSPersistence.h>
#include "default_settings.h"
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
#define EMSESP_DEFAULT_SYSLOG_HOST ""
#define EMSESP_DEFAULT_SYSLOG_PORT 514
#define EMSESP_DEFAULT_TRACELOG_RAW false
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
#define EMSESP_DEFAULT_SHOWER_TIMER false
#define EMSESP_DEFAULT_SHOWER_ALERT false
#define EMSESP_DEFAULT_HIDE_LED false
#define EMSESP_DEFAULT_DALLAS_PARASITE false
#define EMSESP_DEFAULT_API_ENABLED false // turn off, because its insecure
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
#define EMSESP_DEFAULT_ANALOG_ENABLED false
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
#endif
// Default GPIO PIN definitions - based on Wemos/Nodemcu
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
#define EMSESP_DEFAULT_TX_GPIO 5 // D8
#define EMSESP_DEFAULT_DALLAS_GPIO 18
#define EMSESP_DEFAULT_LED_GPIO 2
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
namespace emsesp {
class WebSettings {

View File

@@ -30,14 +30,18 @@ std::vector<Command::CmdFunction> Command::cmdfunctions_;
// id may be used to represent a heating circuit for example, it's optional
// returns false if error or not found
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
auto cf = find_command(device_type, cmd);
std::string dname = EMSdevice::device_type_2_device_name(device_type);
int8_t id_new = id;
char cmd_new[20];
check_command(cmd_new, cmd, id_new);
auto cf = find_command(device_type, cmd_new);
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
LOG_WARNING(F("Command %s not found"), cmd);
LOG_WARNING(F("Command %s on %s not found"), cmd, dname.c_str());
return false; // command not found, or requires a json
}
#ifdef EMSESP_DEBUG
std::string dname = EMSdevice::device_type_2_device_name(device_type);
if (value == nullptr) {
LOG_DEBUG(F("[DEBUG] Calling %s command %s"), dname.c_str(), cmd);
} else if (id == -1) {
@@ -47,18 +51,18 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
}
#endif
return ((cf->cmdfunction_)(value, id));
return ((cf->cmdfunction_)(value, id_new));
}
// calls a command. Takes a json object for output.
// id may be used to represent a heating circuit for example
// returns false if error or not found
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) {
auto cf = find_command(device_type, cmd);
if (cf == nullptr) {
LOG_WARNING(F("Command %s not found"), cmd);
return false; // command not found or not json
}
int8_t id_new = id;
char cmd_new[20];
check_command(cmd_new, cmd, id_new);
auto cf = find_command(device_type, cmd_new);
#ifdef EMSESP_DEBUG
std::string dname = EMSdevice::device_type_2_device_name(device_type);
@@ -77,13 +81,49 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
return false;
}
if (!cf->cmdfunction_json_) {
return ((cf->cmdfunction_)(value, id));
if (cf == nullptr) {
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type);
}
if (cf->cmdfunction_json_) {
return ((cf->cmdfunction_json_)(value, id_new, json));
} else {
return ((cf->cmdfunction_json_)(value, id, json));
if (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0) {
return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type);
}
return ((cf->cmdfunction_)(value, id_new));
}
}
char * Command::check_command(char * out, const char * cmd, int8_t & id) {
// convert cmd to lowercase
strlcpy(out, cmd, 20);
for (char * p = out; *p; p++) {
*p = tolower(*p);
}
//scan for prefix hc.
for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) {
if ((strncmp(out, EMSdevice::tag_to_string(i).c_str(), 3) == 0) && (strlen(out) > 4)) {
strcpy(out, &out[4]);
id = 1 + i - DeviceValueTAG::TAG_HC1;
break;
}
}
//scan for prefix wwc.
for (uint8_t i = DeviceValueTAG::TAG_WWC1; i <= DeviceValueTAG::TAG_WWC4; i++) {
if ((strncmp(out, EMSdevice::tag_to_string(i).c_str(), 4) == 0) && (strlen(out) > 5)) {
strcpy(out, &out[5]);
id = 8 + i - DeviceValueTAG::TAG_WWC1;
break;
}
}
return out;
}
// add a command to the list, which does not return json
void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) {
// if the command already exists for that device type don't add it
@@ -110,6 +150,7 @@ void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper
}
// see if a command exists for that device type
// is not case sensitive
Command::CmdFunction * Command::find_command(const uint8_t device_type, const char * cmd) {
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
return nullptr;
@@ -123,7 +164,7 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch
}
for (auto & cf : cmdfunctions_) {
if (!strcmp_P(lowerCmd, reinterpret_cast<PGM_P>(cf.cmd_)) && (cf.device_type_ == device_type)) {
if (!strcmp(lowerCmd, Helpers::toLower(uuid::read_flash_string(cf.cmd_)).c_str()) && (cf.device_type_ == device_type)) {
return &cf;
}
}

View File

@@ -41,7 +41,7 @@ class Command {
public:
struct CmdFunction {
uint8_t device_type_; // DeviceType::
uint8_t flag_;
uint8_t flag_; // mqtt flags for command subscriptions
const __FlashStringHelper * cmd_;
cmdfunction_p cmdfunction_;
cmdfunction_json_p cmdfunction_json_;
@@ -65,6 +65,7 @@ class Command {
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 Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd);
static char * check_command(char * out, const char * cmd, int8_t & id);
static void show(uuid::console::Shell & shell, uint8_t device_type);
static void show_devices(uuid::console::Shell & shell);

View File

@@ -20,7 +20,7 @@
#include "emsesp.h"
#include "version.h"
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
#include "test/test.h"
#endif
@@ -54,9 +54,8 @@ void EMSESPShell::stopped() {
logger().log(LogLevel::DEBUG, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
// remove all custom contexts
commands->remove_all_commands();
console_commands_loaded_ = false; // make sure they get reloaded next time a console is opened
// commands->remove_all_commands();
// console_commands_loaded_ = false; // make sure they get reloaded next time a console is opened
}
// show welcome banner
@@ -99,9 +98,8 @@ void EMSESPShell::add_console_commands() {
if (console_commands_loaded_) {
return;
}
console_commands_loaded_ = true;
// just in case, remove everything
// commands->remove_context_commands(ShellContext::MAIN);
commands->remove_all_commands();
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(show)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
@@ -212,6 +210,7 @@ void EMSESPShell::add_console_commands() {
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
char buffer[4];
shell.printfln(F_(master_thermostat_fmt), settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, settings.master_thermostat));
shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str());
});
});
@@ -222,12 +221,16 @@ void EMSESPShell::add_console_commands() {
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
uint16_t type_id = Helpers::hextoint(arguments[1].c_str());
EMSESP::set_read_id(type_id);
if (arguments.size() == 3) {
if (arguments.size() == 4) {
uint16_t offset = Helpers::hextoint(arguments[2].c_str());
uint8_t length = Helpers::hextoint(arguments.back().c_str());
EMSESP::send_read_request(type_id, device_id, offset, length);
} else if (arguments.size() == 3) {
uint16_t offset = Helpers::hextoint(arguments.back().c_str());
EMSESP::send_read_request(type_id, device_id, offset);
EMSESP::send_read_request(type_id, device_id, offset, EMS_MAX_TELEGRAM_LENGTH);
} else {
EMSESP::send_read_request(type_id, device_id);
// send with length to send immediatly and trigger publish read_id
EMSESP::send_read_request(type_id, device_id, 0, EMS_MAX_TELEGRAM_LENGTH);
}
});
@@ -346,11 +349,6 @@ void EMSESPShell::add_console_commands() {
}
const char * cmd = arguments[1].c_str();
if (Command::find_command(device_type, cmd) == nullptr) {
shell.print(F("Unknown command. Available commands are: "));
Command::show(shell, device_type);
return;
}
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
JsonObject json = doc.to<JsonObject>();
@@ -376,6 +374,11 @@ void EMSESPShell::add_console_commands() {
doc.shrinkToFit();
serializeJsonPretty(doc, shell);
shell.println();
return;
} else if (!ok) {
shell.println(F("Unknown command, value, or id."));
shell.print(F("Available commands are: "));
Command::show(shell, device_type);
}
},
[&](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) -> std::vector<std::string> {
@@ -405,45 +408,42 @@ void EMSESPShell::add_console_commands() {
return {};
});
// System context menu
commands->add_command(ShellContext::MAIN, CommandFlags::USER, flash_string_vector{F_(system)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
System::console_commands(shell, ShellContext::SYSTEM);
});
Console::load_standard_commands(ShellContext::MAIN);
console_commands_loaded_ = true;
Console::load_system_commands(ShellContext::MAIN);
}
std::string EMSESPShell::hostname_text() {
return console_hostname_;
}
/*
// remove commands from the current context to save memory before exiting
bool EMSESPShell::exit_context() {
unsigned int current_context = context();
commands->remove_context_commands(current_context);
if (current_context == ShellContext::MAIN) {
Shell::stop();
return true;
}
return Shell::exit_context();
// commands->remove_context_commands(current_context);
// return Shell::exit_context();
return false;
}
// enter a custom context (sub-menu)
void Console::enter_custom_context(Shell & shell, unsigned int context) {
load_standard_commands(context);
// load_standard_commands(context);
// don't go into the new context if it's already the root (to prevent double loading)
if (context != ShellContext::MAIN) {
shell.enter_context(context);
}
}
*/
// each custom context has the common commands like log, help, exit, su etc
void Console::load_standard_commands(unsigned int context) {
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("test")}, flash_string_vector{F_(name_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) {
Test::run_test(shell, "default");
@@ -500,9 +500,8 @@ void Console::load_standard_commands(unsigned int context) {
});
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(exit)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
// delete MAIN console stuff first to save memory
EMSESPShell::commands->remove_context_commands(context);
shell.exit_context();
shell.stop();
// shell.exit_context();
});
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(su)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
@@ -534,6 +533,139 @@ void Console::load_standard_commands(unsigned int context) {
});
}
// console commands to add
void Console::load_system_commands(unsigned int context) {
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(restart)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.restart(); });
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(wifi), F_(reconnect)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.wifi_reconnect(); });
EMSESPShell::commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, flash_string_vector{F_(format)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
if (completed) {
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
if (securitySettings.jwtSecret.equals(password.c_str())) {
EMSESP::system_.format(shell);
} else {
shell.println(F("incorrect password"));
}
});
}
});
});
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(passwd)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
if (completed) {
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
if (completed) {
if (password1 == password2) {
EMSESP::esp8266React.getSecuritySettingsService()->update(
[&](SecuritySettings & securitySettings) {
securitySettings.jwtSecret = password2.c_str();
return StateUpdateResult::CHANGED;
},
"local");
shell.println(F("su password updated"));
} else {
shell.println(F("Passwords do not match"));
}
}
});
}
});
});
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F_(show), F_(system)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.show_system(shell);
shell.println();
});
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(hostname)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
shell.println("The network connection will be reset...");
Shell::loop_all();
delay(1000); // wait a second
EMSESP::esp8266React.getNetworkSettingsService()->update(
[&](NetworkSettings & networkSettings) {
networkSettings.hostname = arguments.front().c_str();
return StateUpdateResult::CHANGED;
},
"local");
});
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
networkSettings.ssid = arguments.front().c_str();
return StateUpdateResult::CHANGED;
});
shell.println("Use `wifi reconnect` to save and apply the new settings");
});
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(set), F_(wifi), F_(password)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
if (completed) {
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
if (completed) {
if (password1 == password2) {
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
networkSettings.password = password2.c_str();
return StateUpdateResult::CHANGED;
});
shell.println("Use `wifi reconnect` to save and apply the new settings");
} else {
shell.println(F("Passwords do not match"));
}
}
});
}
});
});
EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(board_profile)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
std::vector<uint8_t> data; // led, dallas, rx, tx, button
std::string board_profile = Helpers::toUpper(arguments.front());
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, TLK110, LAN8720, CUSTOM)"));
return;
}
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
settings.board_profile = board_profile.c_str();
settings.led_gpio = data[0];
settings.dallas_gpio = data[1];
settings.rx_gpio = data[2];
settings.tx_gpio = data[3];
settings.pbutton_gpio = data[4];
return StateUpdateResult::CHANGED;
},
"local");
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
EMSESP::system_.network_init(true);
});
EMSESPShell::commands->add_command(context, CommandFlags::ADMIN, flash_string_vector{F_(show), F_(users)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.show_users(shell);
});
}
/*
// prompt, change per context
std::string EMSESPShell::context_text() {
switch (static_cast<ShellContext>(context())) {
@@ -547,6 +679,7 @@ std::string EMSESPShell::context_text() {
return std::string{};
}
}
*/
// when in su (admin) show # as the prompt suffix
std::string EMSESPShell::prompt_suffix() {
@@ -566,7 +699,7 @@ EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, bool local)
, uuid::console::StreamConsole(stream)
, EMSESPShell()
, name_(uuid::read_flash_string(F("Serial")))
, pty_(std::numeric_limits<size_t>::max())
, pty_(SIZE_MAX)
, addr_()
, port_(0) {
}
@@ -602,8 +735,6 @@ EMSESPStreamConsole::~EMSESPStreamConsole() {
#endif
ptys_[pty_] = false;
ptys_.shrink_to_fit();
EMSESPShell::commands->remove_all_commands();
}
}

View File

@@ -44,6 +44,8 @@ using uuid::log::Level;
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
#define MQTT_TOPIC(list_name) (__pstr__##list_name[0])
// clang-format off
// strings stored 32 bit aligned on ESP8266/ESP32
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal;
@@ -99,10 +101,10 @@ class EMSESPShell : virtual public uuid::console::Shell {
void stopped() override;
void display_banner() override;
std::string hostname_text() override;
std::string context_text() override;
// std::string context_text() override;
std::string prompt_suffix() override;
void end_of_transmission() override;
bool exit_context() override;
// bool exit_context() override;
private:
void add_console_commands();
@@ -134,8 +136,9 @@ class Console {
uuid::log::Level log_level();
static void enter_custom_context(Shell & shell, unsigned int context);
// static void enter_custom_context(Shell & shell, unsigned int context);
static void load_standard_commands(unsigned int context);
static void load_system_commands(unsigned int context);
};
} // namespace emsesp

View File

@@ -308,10 +308,18 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
for (const auto & sensor : sensors_) {
char sensorID[10]; // sensor{1-n}
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
JsonObject dataSensor = json.createNestedObject(sensorID);
dataSensor["id"] = sensor.to_string();
if (Helpers::hasValue(sensor.temperature_c)) {
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
if (id == 0) {
if (Mqtt::dallas_format() == Mqtt::Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) {
json[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
} else if (Helpers::hasValue(sensor.temperature_c)) {
json[sensorID] = (float)(sensor.temperature_c) / 10;
}
} else {
JsonObject dataSensor = json.createNestedObject(sensorID);
dataSensor["id"] = sensor.to_string();
if (Helpers::hasValue(sensor.temperature_c)) {
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
}
}
}
@@ -394,9 +402,9 @@ void DallasSensor::publish_values(const bool force) {
// use '_' as HA doesn't like '-' in the topic name
std::string topicname = sensor.to_string();
std::replace(topicname.begin(), topicname.end(), '-', '_');
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallas_sensor%s/config"), Mqtt::base().c_str(), topicname.c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%s/config"), Mqtt::base().c_str(), topicname.c_str());
} else {
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no);
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no);
}
Mqtt::publish_ha(topic, config.as<JsonObject>());

View File

@@ -68,6 +68,10 @@ class DallasSensor {
return sensorfails_;
}
bool dallas_enabled() {
return (dallas_gpio_ != 0);
}
private:
static constexpr uint8_t MAX_SENSORS = 20;

155
src/default_settings.h Normal file
View File

@@ -0,0 +1,155 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020 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 <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_DEFAULT_SETTINGS_H
#define EMSESP_DEFAULT_SETTINGS_H
// GENERAL SETTINGS
#ifndef EMSESP_DEFAULT_TX_MODE
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
#endif
#ifndef EMSESP_DEFAULT_TX_DELAY
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
#endif
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
#endif
#ifndef EMSESP_DEFAULT_SYSLOG_ENABLED
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
#endif
#ifndef EMSESP_DEFAULT_SYSLOG_LEVEL
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
#endif
#ifndef EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
#endif
#ifndef EMSESP_DEFAULT_SYSLOG_HOST
#define EMSESP_DEFAULT_SYSLOG_HOST ""
#endif
#ifndef EMSESP_DEFAULT_SYSLOG_PORT
#define EMSESP_DEFAULT_SYSLOG_PORT 514
#endif
#ifndef EMSESP_DEFAULT_TRACELOG_RAW
#define EMSESP_DEFAULT_TRACELOG_RAW false
#endif
#ifndef EMSESP_DEFAULT_MASTER_THERMOSTAT
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
#endif
#ifndef EMSESP_DEFAULT_SHOWER_TIMER
#define EMSESP_DEFAULT_SHOWER_TIMER false
#endif
#ifndef EMSESP_DEFAULT_SHOWER_ALERT
#define EMSESP_DEFAULT_SHOWER_ALERT false
#endif
#ifndef EMSESP_DEFAULT_HIDE_LED
#define EMSESP_DEFAULT_HIDE_LED false
#endif
#ifndef EMSESP_DEFAULT_DALLAS_PARASITE
#define EMSESP_DEFAULT_DALLAS_PARASITE false
#endif
#ifndef EMSESP_DEFAULT_API_ENABLED
#define EMSESP_DEFAULT_API_ENABLED false // turn off, because its insecure
#endif
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
#endif
#ifndef EMSESP_DEFAULT_ANALOG_ENABLED
#define EMSESP_DEFAULT_ANALOG_ENABLED false
#endif
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
#endif
// Default GPIO PIN definitions - based on Wemos/Nodemcu
#ifndef EMSESP_DEFAULT_RX_GPIO
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
#endif
#ifndef EMSESP_DEFAULT_TX_GPIO
#define EMSESP_DEFAULT_TX_GPIO 5 // D8
#endif
#ifndef EMSESP_DEFAULT_DALLAS_GPIO
#define EMSESP_DEFAULT_DALLAS_GPIO 18
#endif
#ifndef EMSESP_DEFAULT_LED_GPIO
#define EMSESP_DEFAULT_LED_GPIO 2
#endif
#ifndef EMSESP_DEFAULT_PBUTTON_GPIO
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
#endif
// MQTT
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
#endif
#ifndef EMSESP_DEFAULT_DALLAS_FORMAT
#define EMSESP_DEFAULT_DALLAS_FORMAT 1 // sensorid
#endif
#ifndef EMSESP_DEFAULT_HA_CLIMATE_FORMAT
#define EMSESP_DEFAULT_HA_CLIMATE_FORMAT 1 // current temp
#endif
#ifndef EMSESP_DEFAULT_MQTT_QOS
#define EMSESP_DEFAULT_MQTT_QOS 0
#endif
#ifndef EMSESP_DEFAULT_MQTT_RETAIN
#define EMSESP_DEFAULT_MQTT_RETAIN false
#endif
#ifndef EMSESP_DEFAULT_HA_ENABLED
#define EMSESP_DEFAULT_HA_ENABLED false
#endif
#ifndef EMSESP_DEFAULT_PUBLISH_TIME
#define EMSESP_DEFAULT_PUBLISH_TIME 10
#endif
#ifndef EMSESP_DEFAULT_NESTED_FORMAT
#define EMSESP_DEFAULT_NESTED_FORMAT 1
#endif
#ifndef EMSESP_DEFAULT_SUBSCRIBE_FORMAT
#define EMSESP_DEFAULT_SUBSCRIBE_FORMAT 0
#endif
#endif

View File

@@ -25,9 +25,9 @@
// Boilers - 0x08
{ 64, DeviceType::BOILER, F("BK13/BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_EMS},
{ 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_HT3},
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
@@ -35,7 +35,7 @@
{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
{195, DeviceType::BOILER, F("Condens 5000i/Greenstar 8000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
@@ -68,23 +68,24 @@
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19 / 0x38
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_1},// 0x10 - based on RC35
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
{ 79, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
{ 80, DeviceType::THERMOSTAT, F("Moduline 200"), DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
{ 86, DeviceType::THERMOSTAT, F("RC35"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
{216, DeviceType::THERMOSTAT, F("CRF200S"), DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
// Thermostat - Sieger - 0x10 / 0x17
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17 or remote
{ 66, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
// Thermostat - Junkers - 0x10
{105, DeviceType::THERMOSTAT, F("FW100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},

View File

@@ -33,172 +33,175 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_BOILER_1; // heating source id, count from 0
// Runtime of each heatingsource in 0x06DC, ff
register_telegram_type(0x6DC + hs, F("CascadeMessage"), false, MAKE_PF_CB(process_CascadeMessage));
register_device_value(TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, nullptr, F("burnWorkMin"), F("total burner operating time"), DeviceValueUOM::MINUTES);
register_device_value(TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, nullptr, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
// selBurnpower in D2 and E4
// register_telegram_type(0xD2, F("CascadePowerMessage"), false, MAKE_PF_CB(process_CascadePowerMessage));
// individual Flowtemps and powervalues for each heatingsource in E4
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_HS1 + hs, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT);
register_device_value(TAG_HS1 + hs, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), FL_(curFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
return;
}
// register values for master boiler/cascade module
reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
// the telegram handlers...
// common for all boilers
register_telegram_type(0x10, F("UBAErrorMessage1"), false, MAKE_PF_CB(process_UBAErrorMessage));
register_telegram_type(0x11, F("UBAErrorMessage2"), false, MAKE_PF_CB(process_UBAErrorMessage));
register_telegram_type(0x14, F("UBATotalUptime"), true, MAKE_PF_CB(process_UBATotalUptime));
register_telegram_type(0x15, F("UBAMaintenanceData"), false, MAKE_PF_CB(process_UBAMaintenanceData));
register_telegram_type(0x16, F("UBAParameters"), true, MAKE_PF_CB(process_UBAParameters));
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, MAKE_PF_CB(process_UBAMaintenanceStatus));
// EMS1.0 and maybe EMS+?
register_telegram_type(0x18, F("UBAMonitorFast"), false, MAKE_PF_CB(process_UBAMonitorFast));
register_telegram_type(0x19, F("UBAMonitorSlow"), true, MAKE_PF_CB(process_UBAMonitorSlow));
register_telegram_type(0x1A, F("UBASetPoints"), false, MAKE_PF_CB(process_UBASetPoints));
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, MAKE_PF_CB(process_UBAMaintenanceStatus));
register_telegram_type(0x26, F("UBASettingsWW"), true, MAKE_PF_CB(process_UBASettingsWW));
register_telegram_type(0x2A, F("MC10Status"), false, MAKE_PF_CB(process_MC10Status));
register_telegram_type(0x35, F("UBAFlags"), false, MAKE_PF_CB(process_UBAFlags));
// only EMS 1.0
register_telegram_type(0x16, F("UBAParameters"), true, MAKE_PF_CB(process_UBAParameters));
register_telegram_type(0x33, F("UBAParameterWW"), true, MAKE_PF_CB(process_UBAParameterWW));
register_telegram_type(0x34, F("UBAMonitorWW"), false, MAKE_PF_CB(process_UBAMonitorWW));
register_telegram_type(0x35, F("UBAFlags"), false, MAKE_PF_CB(process_UBAFlags));
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, MAKE_PF_CB(process_UBAOutdoorTemp));
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
register_telegram_type(0xE6, F("UBAParametersPlus"), true, MAKE_PF_CB(process_UBAParametersPlus));
register_telegram_type(0xE9, F("UBADHWStatus"), false, MAKE_PF_CB(process_UBADHWStatus));
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, MAKE_PF_CB(process_UBAParameterWWPlus));
register_telegram_type(0x494, F("UBAEnergySupplied"), false, MAKE_PF_CB(process_UBAEnergySupplied));
register_telegram_type(0x495, F("UBAInformation"), false, MAKE_PF_CB(process_UBAInformation));
// not ems1.0, but HT3
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS) {
register_telegram_type(0x26, F("UBASettingsWW"), true, MAKE_PF_CB(process_UBASettingsWW));
register_telegram_type(0x2A, F("MC110Status"), false, MAKE_PF_CB(process_MC110Status));
}
// only EMS+
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS && model() != EMSdevice::EMS_DEVICE_FLAG_HT3) {
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, MAKE_PF_CB(process_UBAOutdoorTemp));
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus2));
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, MAKE_PF_CB(process_UBAMonitorSlowPlus));
register_telegram_type(0xE6, F("UBAParametersPlus"), true, MAKE_PF_CB(process_UBAParametersPlus));
register_telegram_type(0xE9, F("UBAMonitorWWPlus"), false, MAKE_PF_CB(process_UBAMonitorWWPlus));
register_telegram_type(0xEA, F("UBAParameterWWPlus"), true, MAKE_PF_CB(process_UBAParameterWWPlus));
}
if (model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) {
register_telegram_type(0x494, F("UBAEnergySupplied"), false, MAKE_PF_CB(process_UBAEnergySupplied));
register_telegram_type(0x495, F("UBAInformation"), false, MAKE_PF_CB(process_UBAInformation));
register_telegram_type(0x48D, F("HpPower"), false, MAKE_PF_CB(process_HpPower));
register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor));
}
// MQTT commands for boiler topic
register_mqtt_cmd(F("comfort"), MAKE_CF_CB(set_warmwater_mode));
register_mqtt_cmd(F("wwactivated"), MAKE_CF_CB(set_warmwater_activated));
register_mqtt_cmd(F("wwtapactivated"), MAKE_CF_CB(set_tapwarmwater_activated));
register_mqtt_cmd(F("wwflowtempoffset"), MAKE_CF_CB(set_wWFlowTempOffset));
register_mqtt_cmd(F("wwmaxpower"), MAKE_CF_CB(set_warmwater_maxpower));
register_mqtt_cmd(F("wwonetime"), MAKE_CF_CB(set_warmwater_onetime));
register_mqtt_cmd(F("wwcircpump"), MAKE_CF_CB(set_warmwater_circulation_pump));
register_mqtt_cmd(F("wwcirculation"), MAKE_CF_CB(set_warmwater_circulation));
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_warmwater_circulation_mode));
register_mqtt_cmd(F("flowtemp"), MAKE_CF_CB(set_flow_temp));
register_mqtt_cmd(F("wwsettemp"), MAKE_CF_CB(set_warmwater_temp));
register_mqtt_cmd(F("heatingactivated"), MAKE_CF_CB(set_heating_activated));
register_mqtt_cmd(F("heatingtemp"), MAKE_CF_CB(set_heating_temp));
register_mqtt_cmd(F("burnmaxpower"), MAKE_CF_CB(set_max_power));
register_mqtt_cmd(F("burnminpower"), MAKE_CF_CB(set_min_power));
register_mqtt_cmd(F("boilhyston"), MAKE_CF_CB(set_hyst_on));
register_mqtt_cmd(F("boilhystoff"), MAKE_CF_CB(set_hyst_off));
register_mqtt_cmd(F("burnperiod"), MAKE_CF_CB(set_burn_period));
register_mqtt_cmd(F("pumpdelay"), MAKE_CF_CB(set_pump_delay));
register_mqtt_cmd(F("maintenance"), MAKE_CF_CB(set_maintenance));
register_mqtt_cmd(F("pumpmodmax"), MAKE_CF_CB(set_max_pump));
register_mqtt_cmd(F("pumpmodmin"), MAKE_CF_CB(set_min_pump));
register_mqtt_cmd(F("reset"), MAKE_CF_CB(set_reset));
register_device_value(TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_tapwarmwater_activated));
register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::ENUM, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
// add values
reserve_device_values(50);
// reserve_device_values(90);
// main - boiler_data topic
register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
id_ = product_id;
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, F("heatingActive"), F("heating active"));
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, F("tapwaterActive"), F("warm water active"));
register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, F("selFlowTemp"), F("selected flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, F("selBurnPow"), F("burner selected max power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, F("heatingPumpMod"), F("heating pump modulation"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &heatingPump2Mod_, DeviceValueType::UINT, nullptr, F("heatingPump2Mod"), F("heating pump 2 modulation"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &outdoorTemp_, DeviceValueType::SHORT, FL_(div10), F("outdoorTemp"), F("outside temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), F("curFlowTemp"), F("current flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &retTemp_, DeviceValueType::USHORT, FL_(div10), F("retTemp"), F("return temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &switchTemp_, DeviceValueType::USHORT, FL_(div10), F("switchTemp"), F("mixing switch temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), F("sysPress"), F("system pressure"), DeviceValueUOM::BAR);
register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), F("boilTemp"), F("max boiler temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), F("exhaustTemp"), F("exhaust temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, F("burnGas"), F("gas"));
register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), F("flameCurr"), F("flame current"), DeviceValueUOM::UA);
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, F("heatingPump"), F("heating pump"), DeviceValueUOM::PUMP);
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, F("fanWork"), F("fan"));
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, F("ignWork"), F("ignition"));
register_device_value(TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, F("heatingActivated"), F("heating activated"));
register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, F("heatingTemp"), F("heating temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, F("pumpModMax"), F("burner pump max power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, F("pumpModMin"), F("burner pump min power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &pumpDelay_, DeviceValueType::UINT, nullptr, F("pumpDelay"), F("pump delay"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &burnMinPeriod_, DeviceValueType::UINT, nullptr, F("burnMinPeriod"), F("burner min period"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, nullptr, F("burnMinPower"), F("burner min power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, nullptr, F("burnMaxPower"), F("burner max power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, nullptr, F("boilHystOn"), F("hysteresis on temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &boilHystOff_, DeviceValueType::INT, nullptr, F("boilHystOff"), F("hysteresis off temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, F("setFlowTemp"), F("set flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, F("setBurnPow"), F("burner set power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("burner current power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, F("burnStarts"), F("burner # starts"));
register_device_value(TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, nullptr, F("burnWorkMin"), F("total burner operating time"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, nullptr, F("heatWorkMin"), F("total heat operating time"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, F("UBAuptime"), F("total UBA operating time"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, F("lastCode"), F("last error code"));
register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, F("serviceCode"), F("service code"));
register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, F("serviceCodeNumber"), F("service code number"));
// info
register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), F("upTimeControl"), F("operating time total heat"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &upTimeCompHeating_, DeviceValueType::TIME, FL_(div60), F("upTimeCompHeating"), F("operating time compressor heating"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &upTimeCompCooling_, DeviceValueType::TIME, FL_(div60), F("upTimeCompCooling"), F("operating time compressor cooling"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &upTimeCompWw_, DeviceValueType::TIME, FL_(div60), F("upTimeCompWw"), F("operating time compressor warm water"), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, F("heatingStarts"), F("# heating control starts"));
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, F("coolingStarts"), F("# cooling control starts"));
register_device_value(TAG_BOILER_DATA, &nrgConsTotal_, DeviceValueType::ULONG, nullptr, F("nrgConsTotal"), F("total energy consumption"));
register_device_value(TAG_BOILER_DATA, &nrgConsCompTotal_, DeviceValueType::ULONG, nullptr, F("nrgConsCompTotal"), F("energy consumption compressor total"));
register_device_value(TAG_BOILER_DATA, &nrgConsCompHeating_, DeviceValueType::ULONG, nullptr, F("nrgConsCompHeating"), F("energy consumption compressor heating"));
register_device_value(TAG_BOILER_DATA, &nrgConsCompWw_, DeviceValueType::ULONG, nullptr, F("nrgConsCompWw"), F("energy consumption compressor warm water"));
register_device_value(TAG_BOILER_DATA, &nrgConsCompCooling_, DeviceValueType::ULONG, nullptr, F("nrgConsCompCooling"), F("energy consumption compressor cooling"));
register_device_value(TAG_BOILER_DATA, &nrgSuppTotal_, DeviceValueType::ULONG, nullptr, F("nrgSuppTotal"), F("total energy supplied"));
register_device_value(TAG_BOILER_DATA, &nrgSuppHeating_, DeviceValueType::ULONG, nullptr, F("nrgSuppHeating"), F("total energy supplied heating"));
register_device_value(TAG_BOILER_DATA, &nrgSuppWw_, DeviceValueType::ULONG, nullptr, F("nrgSuppWw"), F("total energy warm supplied warm water"));
register_device_value(TAG_BOILER_DATA, &nrgSuppCooling_, DeviceValueType::ULONG, nullptr, F("nrgSuppCooling"), F("total energy supplied cooling"));
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsTotal_, DeviceValueType::ULONG, nullptr, F("auxElecHeatNrgConsTotal"), F("auxiliary electrical heater energy consumption total"));
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsHeating_, DeviceValueType::ULONG, nullptr, F("auxElecHeatNrgConsHeating"), F("auxiliary electrical heater energy consumption heating"));
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsDHW_, DeviceValueType::ULONG, nullptr, F("auxElecHeatNrgConsDHW"), F("auxiliary electrical heater energy consumption DHW"));
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, F("maintenanceMessage"), F("maintenance message"));
register_device_value(TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, F("maintenanceDate"), F("maintenance set date"));
register_device_value(TAG_BOILER_DATA, &maintenanceType_, DeviceValueType::ENUM, FL_(enum_off_time_date), F("maintenanceType"), F("maintenance scheduled"));
register_device_value(TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, F("maintenanceTime"), F("maintenance set time"), DeviceValueUOM::HOURS);
register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
register_device_value(TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, nullptr, FL_(selBurnPow), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_burn_power));
register_device_value(TAG_BOILER_DATA, &heatingPumpMod_, DeviceValueType::UINT, nullptr, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &heatingPump2Mod_, DeviceValueType::UINT, nullptr, FL_(heatingPump2Mod), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &outdoorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(outdoorTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &curFlowTemp_, DeviceValueType::USHORT, FL_(div10), FL_(curFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &retTemp_, DeviceValueType::USHORT, FL_(div10), FL_(retTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &switchTemp_, DeviceValueType::USHORT, FL_(div10), FL_(switchTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), FL_(sysPress), DeviceValueUOM::BAR);
register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), FL_(boilTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), FL_(exhaustTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, FL_(burnGas), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), FL_(flameCurr), DeviceValueUOM::UA);
register_device_value(TAG_BOILER_DATA, &heatingPump_, DeviceValueType::BOOL, nullptr, FL_(heatingPump), DeviceValueUOM::PUMP);
register_device_value(TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, FL_(fanWork), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, FL_(ignWork), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, nullptr, FL_(heatingActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_heating_activated));
register_device_value(TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, FL_(heatingTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heating_temp));
register_device_value(TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, FL_(pumpModMax), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_pump));
register_device_value(TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, FL_(pumpModMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_pump));
register_device_value(TAG_BOILER_DATA, &pumpDelay_, DeviceValueType::UINT, nullptr, FL_(pumpDelay), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_pump_delay));
register_device_value(TAG_BOILER_DATA, &burnMinPeriod_, DeviceValueType::UINT, nullptr, FL_(burnMinPeriod), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_burn_period));
register_device_value(TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, nullptr, FL_(burnMinPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_min_power));
register_device_value(TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, nullptr, FL_(burnMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_max_power));
register_device_value(TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, nullptr, FL_(boilHystOn), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hyst_on));
register_device_value(TAG_BOILER_DATA, &boilHystOff_, DeviceValueType::INT, nullptr, FL_(boilHystOff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hyst_off));
register_device_value(TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, FL_(setBurnPow), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, FL_(curBurnPow), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, FL_(burnStarts), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, nullptr, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, nullptr, FL_(heatWorkMin), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, FL_(UBAuptime), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, FL_(lastCode), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, FL_(serviceCode), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, FL_(serviceCodeNumber), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, FL_(maintenanceMessage), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &maintenanceType_, DeviceValueType::ENUM, FL_(enum_off_time_date), FL_(maintenanceType), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenance));
register_device_value(TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, FL_(maintenanceTime), DeviceValueUOM::HOURS);
// heatpump info
if (model() == EMS_DEVICE_FLAG_HEATPUMP) {
register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), FL_(upTimeControl), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &upTimeCompHeating_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompHeating), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &upTimeCompCooling_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompCooling), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &upTimeCompWw_, DeviceValueType::TIME, FL_(div60), FL_(upTimeCompWw), DeviceValueUOM::MINUTES);
register_device_value(TAG_BOILER_DATA, &heatingStarts_, DeviceValueType::ULONG, nullptr, FL_(heatingStarts), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &coolingStarts_, DeviceValueType::ULONG, nullptr, FL_(coolingStarts), DeviceValueUOM::NONE);
register_device_value(TAG_BOILER_DATA, &nrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsTotal), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgConsCompTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompTotal), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgConsCompHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompHeating), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgConsCompWw_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompWw), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgConsCompCooling_, DeviceValueType::ULONG, nullptr, FL_(nrgConsCompCooling), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsTotal_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsTotal), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsHeating_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsHeating), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &auxElecHeatNrgConsWW_, DeviceValueType::ULONG, nullptr, FL_(auxElecHeatNrgConsWW), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgSuppTotal_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppTotal), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgSuppHeating_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppHeating), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgSuppWw_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppWw), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &nrgSuppCooling_, DeviceValueType::ULONG, nullptr, FL_(nrgSuppCooling), DeviceValueUOM::KWH);
register_device_value(TAG_BOILER_DATA, &hpPower_, DeviceValueType::UINT, FL_(div10), FL_(hpPower), DeviceValueUOM::KW);
register_device_value(TAG_BOILER_DATA, &hpTc0_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc0), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTc1_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc1), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTc3_, DeviceValueType::SHORT, FL_(div10), FL_(hpTc3), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTr3_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr3), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTr4_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr4), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTr5_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr5), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTr6_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr6), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTr7_, DeviceValueType::SHORT, FL_(div10), FL_(hpTr7), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpTl2_, DeviceValueType::SHORT, FL_(div10), FL_(hpTl2), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpPl1_, DeviceValueType::SHORT, FL_(div10), FL_(hpPl1), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA, &hpPh1_, DeviceValueType::SHORT, FL_(div10), FL_(hpPh1), DeviceValueUOM::DEGREES);
}
// warm water - boiler_data_ww topic
register_device_value(TAG_BOILER_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, nullptr, F("wWSelTemp"), F("selected temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, F("wWSetTemp"), F("set temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), F("wWType"), F("type"));
register_device_value(TAG_BOILER_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), F("wWComfort"), F("comfort"));
register_device_value(TAG_BOILER_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, F("wWFlowTempOffset"), F("flow temperature offset"));
register_device_value(TAG_BOILER_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, F("wWMaxPower"), F("max power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, F("wWCircPump"), F("circulation pump available"));
register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), F("wWChargeType"), F("charging type"));
register_device_value(TAG_BOILER_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, F("wWDisinfectionTemp"), F("disinfection temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), F("wWCircMode"), F("circulation pump freq"));
register_device_value(TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, F("wWCirc"), F("circulation active"));
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp"), F("current intern temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), F("wWCurTemp2"), F("current extern temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), F("wWCurFlow"), F("current tap water flow"), DeviceValueUOM::LMIN);
register_device_value(TAG_BOILER_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), F("wWStorageTemp1"), F("storage intern temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), F("wWStorageTemp2"), F("storage extern temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, F("wWActivated"), F("activated"));
register_device_value(TAG_BOILER_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, F("wWOneTime"), F("one time charging"));
register_device_value(TAG_BOILER_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, F("wWDisinfecting"), F("disinfecting"));
register_device_value(TAG_BOILER_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, F("wWCharging"), F("charging"));
register_device_value(TAG_BOILER_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, F("wWRecharging"), F("recharging"));
register_device_value(TAG_BOILER_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, F("wWTempOK"), F("temperature ok"));
register_device_value(TAG_BOILER_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, F("wWActive"), F("active"));
register_device_value(TAG_BOILER_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, F("wWHeat"), F("heating"));
register_device_value(TAG_BOILER_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, F("wWSetPumpPower"), F("pump set power"), DeviceValueUOM::PERCENT);
register_device_value(TAG_BOILER_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), F("mixerTemp"), F("mixer temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), F("tankMiddleTemp"), F("tank middle temperature (TS3)"), DeviceValueUOM::DEGREES);
register_device_value(TAG_BOILER_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, F("wWStarts"), F("# starts"));
register_device_value(TAG_BOILER_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, nullptr, F("wWStarts2"), F("# control starts"));
register_device_value(TAG_BOILER_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, F("wWWorkM"), F("active time"), DeviceValueUOM::MINUTES);
register_device_value(TAG_DEVICE_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, nullptr, FL_(wWSelTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, nullptr, FL_(wWSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_warmwater_temp));
register_device_value(TAG_DEVICE_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), FL_(wWType), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWComfort_, DeviceValueType::ENUM, FL_(enum_comfort), FL_(wWComfort), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_mode));
register_device_value(TAG_DEVICE_DATA_WW, &wWFlowTempOffset_, DeviceValueType::UINT, nullptr, FL_(wWFlowTempOffset), DeviceValueUOM::NONE, MAKE_CF_CB(set_wWFlowTempOffset));
register_device_value(TAG_DEVICE_DATA_WW, &wWMaxPower_, DeviceValueType::UINT, nullptr, FL_(wWMaxPower), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_warmwater_maxpower));
register_device_value(TAG_DEVICE_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_pump));
register_device_value(TAG_DEVICE_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, nullptr, FL_(wWDisinfectionTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_disinfect_temp));
register_device_value(TAG_DEVICE_DATA_WW, &wWCircMode_, DeviceValueType::ENUM, FL_(enum_freq), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation_mode));
register_device_value(TAG_DEVICE_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, FL_(wWCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_circulation));
register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWCurTemp2), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, FL_(div10), FL_(wWCurFlow), DeviceValueUOM::LMIN);
register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp1_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp1), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wWStorageTemp2_, DeviceValueType::USHORT, FL_(div10), FL_(wWStorageTemp2), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, FL_(wWActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_activated));
register_device_value(TAG_DEVICE_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, nullptr, FL_(wWOneTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_warmwater_onetime));
register_device_value(TAG_DEVICE_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, nullptr, FL_(wWDisinfecting), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, FL_(wWCharging), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, nullptr, FL_(wWRecharging), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, FL_(wWTempOK), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, FL_(wWActive), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, FL_(wWHeat), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, nullptr, FL_(wWSetPumpPower), DeviceValueUOM::PERCENT);
register_device_value(TAG_DEVICE_DATA_WW, &mixerTemp_, DeviceValueType::USHORT, FL_(div10), FL_(mixerTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &tankMiddleTemp_, DeviceValueType::USHORT, FL_(div10), FL_(tankMiddleTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, FL_(wWStarts), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, nullptr, FL_(wWStarts2), DeviceValueUOM::NONE);
register_device_value(TAG_DEVICE_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, FL_(wWWorkM), DeviceValueUOM::MINUTES);
// fetch some initial data
EMSESP::send_read_request(0x10, device_id); // read last errorcode on start (only published on errors)
@@ -227,7 +230,7 @@ bool Boiler::publish_ha_config() {
ids.add("ems-esp-boiler");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/boiler/config"), Mqtt::base().c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/boiler/config"), Mqtt::base().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
return true;
@@ -249,7 +252,7 @@ void Boiler::check_active(const bool force) {
if (heatingActive_ != val || force) {
heatingActive_ = val;
char s[7];
Mqtt::publish(F("heating_active"), Helpers::render_boolean(s, b));
Mqtt::publish(F_(heating_active), Helpers::render_boolean(s, b));
}
// check if tap water is active, bits 1 and 4 must be set
@@ -268,20 +271,24 @@ void Boiler::check_active(const bool force) {
if (tapwaterActive_ != val || force) {
tapwaterActive_ = val;
char s[7];
Mqtt::publish(F("tapwater_active"), Helpers::render_boolean(s, b));
Mqtt::publish(F_(tapwater_active), Helpers::render_boolean(s, b));
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
}
}
// 0x33
// Boiler(0x08) -> Me(0x0B), UBAParameterWW(0x33), data: 08 FF 30 FB FF 28 FF 07 46 00 00
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on
has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on
has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve
// has_update(telegram->read_bitvalue(wwEquipt_,0,3)); // 8=boiler has ww
has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on
has_update(telegram->read_value(wWSelTemp_, 2));
// has_update(telegram->read_value(wW?_, 3)); // Hyst on (default -5)
// has_update(telegram->read_value(wW?_, 4)); // (0xFF) Maybe: Hyst off -1?
has_update(telegram->read_value(wWFlowTempOffset_, 5)); // default 40
has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on
has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous
has_update(telegram->read_value(wWDisinfectionTemp_, 8));
has_update(telegram->read_value(wWFlowTempOffset_, 5));
has_update(telegram->read_value(wWChargeType_, 10)); // 0 = charge pump, 0xff = 3-way valve
telegram->read_value(wWComfort_, 9);
if (wWComfort_ == 0x00) {
@@ -355,6 +362,7 @@ void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(boilHystOff_, 4));
has_update(telegram->read_value(boilHystOn_, 5));
has_update(telegram->read_value(burnMinPeriod_, 6));
// has_update(telegram->read_value(pumpType_, 7)); // 0=off, 02=?
has_update(telegram->read_value(pumpDelay_, 8));
has_update(telegram->read_value(pumpModMax_, 9));
has_update(telegram->read_value(pumpModMin_, 10));
@@ -371,14 +379,15 @@ void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
/*
* UBAMonitorWW - type 0x34 - warm water monitor. 19 bytes long
* received every 10 seconds
*/
* Boiler(0x08) -> Me(0x0B), UBAMonitorWW(0x34), data: 30 01 BA 7D 00 21 00 00 03 00 01 22 2B 00 19 5B
*/
void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(wWSetTemp_, 0));
has_update(telegram->read_value(wWCurTemp_, 1));
has_update(telegram->read_value(wWCurTemp2_, 3));
has_update(telegram->read_value(wWCurFlow_, 9));
has_update(telegram->read_value(wWType_, 8));
has_update(telegram->read_value(wWType_, 8));
has_update(telegram->read_value(wWCurFlow_, 9));
has_update(telegram->read_value(wWWorkM_, 10, 3)); // force to 3 bytes
has_update(telegram->read_value(wWStarts_, 13, 3)); // force to 3 bytes
@@ -504,16 +513,16 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr<const Telegram> telegram)
// 0xEA
void Boiler::process_UBAParameterWWPlus(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on
has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes
has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
has_update(telegram->read_value(wWActivated_, 5)); // 0x01 means on
has_update(telegram->read_value(wWCircPump_, 10)); // 0x01 means yes
has_update(telegram->read_value(wWCircMode_, 11)); // 1=1x3min... 6=6x3min, 7=continuous
// has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9
// has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9
}
// 0xE9 - DHW Status
// 0xE9 - WW monitor ems+
// e.g. 08 00 E9 00 37 01 F6 01 ED 00 00 00 00 41 3C 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 (CRC=77) #data=27
void Boiler::process_UBADHWStatus(std::shared_ptr<const Telegram> telegram) {
void Boiler::process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(wWSetTemp_, 0));
has_update(telegram->read_value(wWCurTemp_, 1));
has_update(telegram->read_value(wWCurTemp2_, 3));
@@ -556,7 +565,7 @@ void Boiler::process_UBAInformation(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(auxElecHeatNrgConsTotal_, 40));
has_update(telegram->read_value(auxElecHeatNrgConsHeating_, 48));
has_update(telegram->read_value(auxElecHeatNrgConsDHW_, 44));
has_update(telegram->read_value(auxElecHeatNrgConsWW_, 44));
has_update(telegram->read_value(nrgConsCompTotal_, 56));
has_update(telegram->read_value(nrgConsCompHeating_, 68));
@@ -578,10 +587,31 @@ void Boiler::process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram)
has_update(telegram->read_value(nrgSuppCooling_, 16));
}
// 0x2A - MC10Status
// Heatpump power - type 0x48D
void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram){
has_update(telegram->read_value(hpPower_, 11));
}
// Heatpump outdoor unit - type 0x48F
void Boiler::process_HpOutdoor(std::shared_ptr<const Telegram> telegram){
has_update(telegram->read_value(hpTc0_, 6));
has_update(telegram->read_value(hpTc1_, 4));
has_update(telegram->read_value(hpTc3_, 2));
has_update(telegram->read_value(hpTr3_, 16));
has_update(telegram->read_value(hpTr4_, 18));
has_update(telegram->read_value(hpTr5_, 20));
has_update(telegram->read_value(hpTr6_, 0));
has_update(telegram->read_value(hpTr7_, 30));
has_update(telegram->read_value(hpTl2_, 12));
has_update(telegram->read_value(hpPl1_, 26));
has_update(telegram->read_value(hpPh1_, 28));
}
// 0x2A - MC110Status
// e.g. 88 00 2A 00 00 00 00 00 00 00 00 00 D2 00 00 80 00 00 01 08 80 00 02 47 00
// see https://github.com/emsesp/EMS-ESP/issues/397
void Boiler::process_MC10Status(std::shared_ptr<const Telegram> telegram) {
void Boiler::process_MC110Status(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(mixerTemp_, 14));
has_update(telegram->read_value(tankMiddleTemp_, 18));
}
@@ -595,8 +625,7 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
// UBASetPoint 0x1A
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(setFlowTemp_,
0)); // boiler set temp from thermostat
has_update(telegram->read_value(setFlowTemp_, 0)); // boiler set temp from thermostat
has_update(telegram->read_value(setBurnPow_, 1)); // max json power in %
has_update(telegram->read_value(wWSetPumpPower_, 2)); // ww pump speed/power?
}
@@ -615,6 +644,7 @@ void Boiler::process_CascadeMessage(std::shared_ptr<const Telegram> telegram) {
// 0x35 - not yet implemented
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
}
#pragma GCC diagnostic pop
// 0x1C
@@ -696,6 +726,7 @@ bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
write_command(EMS_TYPE_UBAParameterWWPlus, 6, v, EMS_TYPE_UBAParameterWWPlus);
} else {
// some boiler have it in 0x33, some in 0x35
write_command(EMS_TYPE_UBAFlags, 3, v, 0x34); // for i9000, see #397
write_command(EMS_TYPE_UBAParameterWW, 2, v, EMS_TYPE_UBAParameterWW); // read seltemp back
}
@@ -703,6 +734,23 @@ bool Boiler::set_warmwater_temp(const char * value, const int8_t id) {
return true;
}
// Set the warm water disinfection temperature
bool Boiler::set_disinfect_temp(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
LOG_WARNING(F("Set boiler warm water disinfect temperature: Invalid value"));
return false;
}
LOG_INFO(F("Setting boiler warm water disinfect temperature to %d C"), v);
if (get_toggle_fetch(EMS_TYPE_UBAParametersPlus)) {
write_command(EMS_TYPE_UBAParameterWWPlus, 12, v, EMS_TYPE_UBAParameterWWPlus);
} else {
write_command(EMS_TYPE_UBAParameterWW, 8, v, EMS_TYPE_UBAParameterWW);
}
return true;
}
// flow temp
bool Boiler::set_flow_temp(const char * value, const int8_t id) {
int v = 0;
@@ -712,9 +760,21 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
}
LOG_INFO(F("Setting boiler flow temperature to %d C"), v);
// some boiler have it in 0x1A, some in 0x35, but both telegrams are sometimes writeonly
write_command(EMS_TYPE_UBASetPoints, 0, v, EMS_TYPE_UBASetPoints);
// write_command(0x35, 3, v, 0x35);
return true;
}
// set selected burner power
bool Boiler::set_burn_power(const char * value, const int8_t id) {
int v = 0;
if (!Helpers::value2number(value, v)) {
LOG_WARNING(F("Set burner max. power: Invalid value"));
return false;
}
LOG_INFO(F("Setting burner max. power to %d %"), v);
write_command(EMS_TYPE_UBASetPoints, 1, v, EMS_TYPE_UBASetPoints);
return true;
}
@@ -930,7 +990,7 @@ bool Boiler::set_pump_delay(const char * value, const int8_t id) {
// 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
bool Boiler::set_warmwater_mode(const char * value, const int8_t id) {
uint8_t set;
if (!Helpers::value2enum(value, set, {F("hot"), F("eco"), F("intelligent")})) {
if (!Helpers::value2enum(value, set, FL_(enum_comfort))) {
LOG_WARNING(F("Set boiler warm water mode: Invalid value"));
return false;
}
@@ -1102,20 +1162,20 @@ bool Boiler::set_warmwater_circulation_mode(const char * value, const int8_t id)
}
// Reset command
// 0 & 1 Reset-Mode (Manuel, others)
// 0 & 1 Reset-Mode (Manual, others)
// 8 reset maintenance message Hxx
// 12 & 13 Reset that Error-memory
bool Boiler::set_reset(const char * value, const int8_t id) {
std::string s(12, '\0');
if (!Helpers::value2string(value, s)) {
uint8_t num;
if (!Helpers::value2enum(value, num, FL_(enum_reset))) {
return false;
}
if (s == "maintenance") {
if (num == 0) {
LOG_INFO(F("Reset boiler maintenance message"));
write_command(0x05, 0x08, 0xFF, 0x1C);
return true;
} else if (s == "error") {
} else if (num == 1) {
LOG_INFO(F("Reset boiler error message"));
write_command(0x05, 0x00, 0x5A); // error reset
return true;
@@ -1127,7 +1187,7 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
bool Boiler::set_maintenance(const char * value, const int8_t id) {
std::string s(12, '\0');
if (Helpers::value2string(value, s)) {
if (s == "reset") {
if (s == Helpers::toLower(uuid::read_flash_string(F_(reset)))) {
LOG_INFO(F("Reset boiler maintenance message"));
write_command(0x05, 0x08, 0xFF, 0x1C);
return true;
@@ -1160,7 +1220,7 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) {
}
uint8_t num;
if (Helpers::value2enum(value, num, {F("off"), F("time"), F("date")})) {
if (Helpers::value2enum(value, num, FL_(enum_off_time_date))) {
LOG_INFO(F("Setting maintenance type to %s"), value);
write_command(0x15, 0, num, 0x15);
return true;

View File

@@ -32,6 +32,11 @@ class Boiler : public EMSdevice {
private:
static uuid::log::Logger logger_;
// specific boiler characteristics, stripping the top 4 bits
inline uint8_t model() const {
return (flags() & 0x0F);
}
void check_active(const bool force = false);
uint8_t boilerState_ = EMS_VALUE_UINT_NOTSET; // Boiler state flag - FOR INTERNAL USE
@@ -84,6 +89,8 @@ class Boiler : public EMSdevice {
// main
uint8_t id_; // product id
uint8_t dummy8u_; // for commands with no output
uint8_t dummybool_; // for commands with no output
uint8_t heatingActive_; // Central heating is on/off
uint8_t tapwaterActive_; // Hot tap water is on/off
uint8_t selFlowTemp_; // Selected flow temperature
@@ -141,12 +148,26 @@ class Boiler : public EMSdevice {
uint32_t nrgSuppCooling_; // Energy supplied cooling
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
uint32_t auxElecHeatNrgConsHeating_; // Auxiliary electrical heater energy consumption heating
uint32_t auxElecHeatNrgConsDHW_; // Auxiliary electrical heater energy consumption DHW
uint32_t auxElecHeatNrgConsWW_; // Auxiliary electrical heater energy consumption DHW
char maintenanceMessage_[4];
char maintenanceDate_[12];
uint8_t maintenanceType_;
uint16_t maintenanceTime_;
// heatpump
uint8_t hpPower_;
int16_t hpTc0_;
int16_t hpTc1_;
int16_t hpTc3_;
int16_t hpTr3_;
int16_t hpTr4_;
int16_t hpTr5_;
int16_t hpTr6_;
int16_t hpTr7_;
int16_t hpTl2_;
int16_t hpPl1_;
int16_t hpPh1_;
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
void process_UBATotalUptime(std::shared_ptr<const Telegram> telegram);
@@ -161,15 +182,17 @@ class Boiler : public EMSdevice {
void process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram);
void process_UBASetPoints(std::shared_ptr<const Telegram> telegram);
void process_UBAFlags(std::shared_ptr<const Telegram> telegram);
void process_MC10Status(std::shared_ptr<const Telegram> telegram);
void process_MC110Status(std::shared_ptr<const Telegram> telegram);
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
void process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram);
void process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram);
void process_UBADHWStatus(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorWWPlus(std::shared_ptr<const Telegram> telegram);
void process_UBAInformation(std::shared_ptr<const Telegram> telegram);
void process_UBAEnergySupplied(std::shared_ptr<const Telegram> telegram);
void process_CascadeMessage(std::shared_ptr<const Telegram> telegram);
void process_UBASettingsWW(std::shared_ptr<const Telegram> telegram);
void process_HpPower(std::shared_ptr<const Telegram> telegram);
void process_HpOutdoor(std::shared_ptr<const Telegram> telegram);
// commands - none of these use the additional id parameter
bool set_warmwater_mode(const char * value, const int8_t id);
@@ -180,9 +203,11 @@ class Boiler : public EMSdevice {
bool set_warmwater_circulation_pump(const char * value, const int8_t id);
bool set_warmwater_circulation_mode(const char * value, const int8_t id);
bool set_warmwater_temp(const char * value, const int8_t id);
bool set_disinfect_temp(const char * value, const int8_t id);
bool set_warmwater_maxpower(const char * value, const int8_t id);
bool set_wWFlowTempOffset(const char * value, const int8_t id);
bool set_flow_temp(const char * value, const int8_t id);
bool set_burn_power(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);

View File

@@ -33,9 +33,9 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
register_telegram_type(0x047B, F("HP2"), true, MAKE_PF_CB(process_HPMonitor2));
// device values
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), F("airHumidity"), F("relative air humidity"));
register_device_value(TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, F("dewTemperature"), F("dew point temperature"));
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
register_device_value(TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), FL_(airHumidity), DeviceValueUOM::PERCENT);
register_device_value(TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, FL_(dewTemperature), DeviceValueUOM::DEGREES);
id_ = product_id;
}
@@ -61,7 +61,7 @@ bool Heatpump::publish_ha_config() {
ids.add("ems-esp-heatpump");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/heatpump/config"), Mqtt::base().c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/heatpump/config"), Mqtt::base().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
return true;

View File

@@ -57,20 +57,20 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
type_ = Type::HC;
hc_ = device_id - 0x20 + 1;
uint8_t tag = TAG_HC1 + hc_ - 1;
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, F("flowSetTemp"), F("setpoint flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), F("flowTempHc"), F("flow temperature in assigned hc (TC1)"), DeviceValueUOM::DEGREES);
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, F("pumpStatus"), F("pump status in assigned hc (PC1)"), DeviceValueUOM::PUMP);
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, F("valveStatus"), F("mixing valve actuator in assigned hc (VC1)"), DeviceValueUOM::PERCENT);
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), F("flowTempVf"), F("flow temperature in header (T0/Vf)"), DeviceValueUOM::DEGREES);
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::PUMP);
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES);
} else {
type_ = Type::WWC;
hc_ = device_id - 0x28 + 1;
uint8_t tag = TAG_WWC1 + hc_ - 1;
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), F("wwTemp"), F("current warm water temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, F("pumpStatus"), F("pump status in assigned hc (PC1)"), DeviceValueUOM::PUMP);
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, F("tempStatus"), F("temperature switch in assigned hc (MC1)"));
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::PUMP);
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(wwTempStatus), DeviceValueUOM::NONE);
}
id_ = product_id;
@@ -109,9 +109,9 @@ bool Mixer::publish_ha_config() {
// determine the topic, if its HC and WWC. This is determined by the incoming telegram types.
std::string topic(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
if (type_ == Type::HC) {
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/%s/mixer_hc%d/config"), Mqtt::base().c_str(), hc_);
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("sensor/%s/mixer_hc%d/config"), Mqtt::base().c_str(), hc_);
} else {
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/%s/mixer_wwc%d/config"), Mqtt::base().c_str(), hc_); // WWC
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("sensor/%s/mixer_wwc%d/config"), Mqtt::base().c_str(), hc_); // WWC
}
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
@@ -162,7 +162,6 @@ void Mixer::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
// Mixer IPM - 0x001E Temperature Message in unmixed circuits
// in unmixed circuits FlowTemp in 10C is zero, this is the measured flowtemp in header
void Mixer::process_IPMTempMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(flowTempVf_, 0)); // TC1, is * 10
}

View File

@@ -50,8 +50,6 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, MAKE_PF_CB(process_SM100CollectorConfig));
register_telegram_type(0x038E, F("SM100Energy"), true, MAKE_PF_CB(process_SM100Energy));
register_telegram_type(0x0391, F("SM100Time"), true, MAKE_PF_CB(process_SM100Time));
register_mqtt_cmd(F("SM100TankBottomMaxTemp"), MAKE_CF_CB(set_SM100TankBottomMaxTemp));
}
}
@@ -64,32 +62,39 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
// special case for a device_id with 0x2A where it's not actual a solar module
if (device_id == 0x2A) {
register_device_value(TAG_NONE, &type_, DeviceValueType::TEXT, nullptr, F("type"), F("type"));
strncpy(type_, "warm water circuit", sizeof(type_));
register_device_value(TAG_NONE, &type_, DeviceValueType::TEXT, nullptr, FL_(type), DeviceValueUOM::NONE);
strlcpy(type_, "warm water circuit", sizeof(type_));
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_1_, DeviceValueType::UINT, nullptr, FL_(wwTemp1), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_3_, DeviceValueType::UINT, nullptr, FL_(wwTemp3), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_4_, DeviceValueType::UINT, nullptr, FL_(wwTemp4), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_5_, DeviceValueType::UINT, nullptr, FL_(wwTemp5), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wwTemp_7_, DeviceValueType::UINT, nullptr, FL_(wwTemp7), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wwPump_, DeviceValueType::UINT, nullptr, FL_(wwPump), DeviceValueUOM::DEGREES);
return;
}
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
id_ = product_id;
register_device_value(TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), F("collectorTemp"), F("collector temperature (TS1)"), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, FL_(div10), F("tankBottomTemp"), F("tank bottom temperature (TS2)"), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), F("tank2BottomTemp"), F("second tank bottom temperature (TS5)"), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), F("heatExchangerTemp"), F("heat exchanger temperature (TS6)"), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, FL_(div10), FL_(collectorTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, FL_(div10), FL_(tankBottomTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, FL_(div10), FL_(tank2BottomTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, FL_(div10), FL_(heatExchangerTemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &tankBottomMaxTemp_, DeviceValueType::UINT, nullptr, F("tank1MaxTempCurrent"), F("maximum tank temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, F("solarPumpModulation"), F("pump modulation (PS1)"), DeviceValueUOM::PERCENT);
register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, F("cylinderPumpModulation"), F("cylinder pump modulation (PS5)"), DeviceValueUOM::PERCENT);
register_device_value(TAG_NONE, &tankBottomMaxTemp_, DeviceValueType::UINT, nullptr, FL_(tankMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_SM100TankBottomMaxTemp));
register_device_value(TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, nullptr, FL_(solarPumpModulation), DeviceValueUOM::PERCENT);
register_device_value(TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, nullptr, FL_(cylinderPumpModulation), DeviceValueUOM::PERCENT);
register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, F("solarPump"), F("pump (PS1)"), DeviceValueUOM::PUMP);
register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, F("valveStatus"), F("valve status"));
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, F("tankHeated"), F("tank heated"));
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, F("collectorShutdown"), F("collector shutdown"));
register_device_value(TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, FL_(solarPump), DeviceValueUOM::PUMP);
register_device_value(TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, FL_(valveStatus), DeviceValueUOM::NONE);
register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::NONE);
register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::NONE);
register_device_value(TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, F("pumpWorkTime"), F("pump working time"), DeviceValueUOM::MINUTES);
register_device_value(TAG_NONE, &pumpWorkTime_, DeviceValueType::TIME, nullptr, FL_(pumpWorkTime), DeviceValueUOM::MINUTES);
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), F("energyLastHour"), F("energy last hour"), DeviceValueUOM::WH);
register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), F("energyTotal"), F("energy total"), DeviceValueUOM::KWH);
register_device_value(TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, F("energyToday"), F("energy today"), DeviceValueUOM::WH);
register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH);
register_device_value(TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), FL_(energyTotal), DeviceValueUOM::KWH);
register_device_value(TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, FL_(energyToday), DeviceValueUOM::WH);
}
// publish HA config
@@ -112,7 +117,7 @@ bool Solar::publish_ha_config() {
ids.add("ems-esp-solar");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/solar/config"), Mqtt::base().c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/solar/config"), Mqtt::base().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
return true;
@@ -203,6 +208,22 @@ void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(heatExchangerTemp_, 20)); // is *10 - TS6: Heat exchanger temperature sensor
}
// SM100wwTemperatur - 0x07D6
// Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90
void Solar::process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(wwTemp_1_, 0));
has_update(telegram->read_value(wwTemp_3_, 4));
has_update(telegram->read_value(wwTemp_4_, 6));
has_update(telegram->read_value(wwTemp_5_, 8));
has_update(telegram->read_value(wwTemp_7_, 12));
}
// SM100wwStatus - 0x07AA
// Solar Module(0x2A) -> (0x00), (0x7AA), data: 64 00 04 00 03 00 28 01 0F
void Solar::process_SM100wwStatus(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(wwPump_, 0));
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
@@ -212,22 +233,6 @@ void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
// not implemented yet
}
// SM100wwTemperatur - 0x07D6
// Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90
void Solar::process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram) {
// has_update(telegram->read_value(wwTemp_1_, 0));
// has_update(telegram->read_value(wwTemp_3_, 4));
// has_update(telegram->read_value(wwTemp_4_, 6));
// has_update(telegram->read_value(wwTemp_5_, 8));
// has_update(telegram->read_value(wwTemp_7_, 12));
}
// SM100wwStatus - 0x07AA
// Solar Module(0x2A) -> (0x00), (0x7AA), data: 64 00 04 00 03 00 28 01 0F
void Solar::process_SM100wwStatus(std::shared_ptr<const Telegram> telegram) {
// has_update(telegram->read_value(wwPump_, 0));
}
// SM100wwCommand - 0x07AB
// Thermostat(0x10) -> Solar Module(0x2A), (0x7AB), data: 01 00 01
void Solar::process_SM100wwCommand(std::shared_ptr<const Telegram> telegram) {

View File

@@ -76,6 +76,16 @@ class Solar : public EMSdevice {
uint16_t collector1Area_; // Area of collector field 1
uint8_t collector1Type_; // Type of collector field 1, 01=flat, 02=vacuum
// SM100wwTemperature - 0x07D6
uint8_t wwTemp_1_;
uint8_t wwTemp_3_;
uint8_t wwTemp_4_;
uint8_t wwTemp_5_;
uint8_t wwTemp_7_;
// SM100wwStatus - 0x07AA
uint8_t wwPump_;
char type_[20]; // Solar of WWC
uint8_t id_;

View File

@@ -34,11 +34,10 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
register_telegram_type(0x9D, F("WM10SetMessage"), false, MAKE_PF_CB(process_WM10SetMessage));
register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage));
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, F("activated"), F("activated"));
register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), F("flowTempHc"), F("flow temperature in assigned hc (TC1)"), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, F("status"), F("status"));
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE);
register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, FL_(status), DeviceValueUOM::NONE);
id_ = product_id;
}
@@ -67,7 +66,7 @@ bool Switch::publish_ha_config() {
ids.add("ems-esp-switch");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/switch/config"), Mqtt::base().c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/switch/config"), Mqtt::base().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
return true;

View File

@@ -59,7 +59,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
}
// RC35
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC35) || (model == EMSdevice::EMS_DEVICE_FLAG_RC30_1)) {
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC35) || (model == EMSdevice::EMS_DEVICE_FLAG_RC30_N)) {
monitor_typeids = {0x3E, 0x48, 0x52, 0x5C};
set_typeids = {0x3D, 0x47, 0x51, 0x5B};
timer_typeids = {0x3F, 0x49, 0x53, 0x5D};
@@ -84,7 +84,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
register_telegram_type(0xAF, F("RC20Remote"), false, MAKE_PF_CB(process_RC20Remote));
}
// RC20 newer
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20_2) {
} else if (model == EMSdevice::EMS_DEVICE_FLAG_RC20_N) {
monitor_typeids = {0xAE};
set_typeids = {0xAD};
if (actual_master_thermostat == device_id) {
@@ -110,6 +110,13 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
set_typeids = {};
register_telegram_type(monitor_typeids[0], F("EasyMonitor"), true, MAKE_PF_CB(process_EasyMonitor));
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
set_typeids = {};
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
register_telegram_type(monitor_typeids[i], F("CRFMonitor"), true, MAKE_PF_CB(process_CRFMonitor));
}
// RC300/RC100
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC300) || (model == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
@@ -167,8 +174,6 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
// register device values for common values (not heating circuit)
register_device_values();
add_commands();
// only for for the master-thermostat, go a query all the heating circuits. This is only done once.
// The automatic fetch will from now on only update the active heating circuits
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
@@ -182,7 +187,9 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
EMSESP::send_read_request(set_typeids[i], device_id);
}
EMSESP::send_read_request(EMS_TYPE_RCTime, device_id);
EMSESP::send_read_request(0x12, device_id); // read last error (only published on errors)
EMSESP::send_read_request(0xA2, device_id); // read errorCode (only published on errors)
}
// publish HA config
@@ -205,7 +212,7 @@ bool Thermostat::publish_ha_config() {
ids.add("ems-esp-thermostat");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/thermostat/config"), Mqtt::base().c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/thermostat/config"), Mqtt::base().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
return true;
@@ -291,7 +298,8 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
// not found, search device-id types for remote thermostats
if (telegram->src >= 0x18 && telegram->src <= 0x1B) {
hc_num = telegram->src - 0x17;
hc_num = telegram->src - 0x17;
toggle_ = true;
}
// still didn't recognize it, ignore it
@@ -306,12 +314,10 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
return heating_circuit;
}
}
// register new heatingcircuits only on active monitor telegrams
if (!toggle_) {
return nullptr;
}
/*
* at this point we have discovered a new heating circuit
*/
@@ -378,7 +384,7 @@ void Thermostat::register_mqtt_ha_config_hc(uint8_t hc_num) {
doc["~"] = Mqtt::base(); // ems-esp
char topic_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
if (Mqtt::nested_format()) {
if (Mqtt::nested_format() == 1) {
snprintf_P(topic_t, sizeof(topic_t), PSTR("~/%s"), Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str());
char mode_str_tpl[40];
@@ -424,18 +430,13 @@ void Thermostat::register_mqtt_ha_config_hc(uint8_t hc_num) {
ids.add("ems-esp-thermostat");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/climate/%s/thermostat_hc%d/config"), Mqtt::base().c_str(), hc_num);
snprintf_P(topic, sizeof(topic), PSTR("climate/%s/thermostat_hc%d/config"), Mqtt::base().c_str(), hc_num);
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
// enable the a special "thermostat_hc<n>" topic to take both mode strings and floats for each of the heating circuits
std::string topic2(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
snprintf_P(&topic2[0], topic2.capacity() + 1, PSTR("thermostat_hc%d"), hc_num);
register_mqtt_topic(topic2, [=](const char * m) { return thermostat_ha_cmd(m, hc_num); });
char hc_name[10]; // hc{1-4}
strlcpy(hc_name, "hc", 10);
char s[3];
strlcat(hc_name, Helpers::itoa(s, hc_num), 10);
}
// for HA specifically when receiving over MQTT in the thermostat topic
@@ -475,6 +476,12 @@ uint8_t Thermostat::HeatingCircuit::get_mode() const {
} else if (mode == 2) {
return HeatingCircuit::Mode::AUTO;
}
} else if (model == EMSdevice::EMS_DEVICE_FLAG_CRF) {
if (mode == 0) {
return HeatingCircuit::Mode::AUTO;
} else if (mode == 1) {
return HeatingCircuit::Mode::OFF;
}
} else if ((model == EMSdevice::EMS_DEVICE_FLAG_RC300) || (model == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
if (mode == 0) {
return HeatingCircuit::Mode::MANUAL;
@@ -515,12 +522,18 @@ uint8_t Thermostat::HeatingCircuit::get_mode_type() const {
} else if (modetype == 1) {
return HeatingCircuit::Mode::NOFROST;
}
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_1)) {
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_N)) {
if (modetype == 0) {
return HeatingCircuit::Mode::NIGHT;
} else if (modetype == 1) {
return HeatingCircuit::Mode::DAY;
}
} else if (model == EMS_DEVICE_FLAG_CRF) {
if (modetype == 0) {
return HeatingCircuit::Mode::OFF;
} else if (modetype == 1) {
return HeatingCircuit::Mode::ON;
}
} else if (model == EMS_DEVICE_FLAG_RC300) {
if (modetype == 0) {
return HeatingCircuit::Mode::ECO;
@@ -539,59 +552,65 @@ uint8_t Thermostat::HeatingCircuit::get_mode_type() const {
std::string Thermostat::mode_tostring(uint8_t mode) {
switch (mode) {
case HeatingCircuit::Mode::OFF:
return read_flash_string(F("off"));
return read_flash_string(F_(off));
break;
case HeatingCircuit::Mode::MANUAL:
return read_flash_string(F("manual"));
return read_flash_string(F_(manual));
break;
case HeatingCircuit::Mode::DAY:
return read_flash_string(F("day"));
return read_flash_string(F_(day));
break;
case HeatingCircuit::Mode::NIGHT:
return read_flash_string(F("night"));
return read_flash_string(F_(night));
break;
case HeatingCircuit::Mode::ECO:
return read_flash_string(F("eco"));
return read_flash_string(F_(eco));
break;
case HeatingCircuit::Mode::COMFORT:
return read_flash_string(F("comfort"));
return read_flash_string(F_(comfort));
break;
case HeatingCircuit::Mode::HEAT:
return read_flash_string(F("heat"));
return read_flash_string(F_(heat));
break;
case HeatingCircuit::Mode::HOLIDAY:
return read_flash_string(F("holiday"));
return read_flash_string(F_(holiday));
break;
case HeatingCircuit::Mode::NOFROST:
return read_flash_string(F("nofrost"));
return read_flash_string(F_(nofrost));
break;
case HeatingCircuit::Mode::AUTO:
return read_flash_string(F("auto"));
return read_flash_string(F_(auto));
break;
case HeatingCircuit::Mode::SUMMER:
return read_flash_string(F("summer"));
return read_flash_string(F_(summer));
break;
case HeatingCircuit::Mode::OFFSET:
return read_flash_string(F("offset"));
return read_flash_string(F_(offset));
break;
case HeatingCircuit::Mode::DESIGN:
return read_flash_string(F("design"));
return read_flash_string(F_(design));
break;
case HeatingCircuit::Mode::MINFLOW:
return read_flash_string(F("minflow"));
return read_flash_string(F_(minflow));
break;
case HeatingCircuit::Mode::MAXFLOW:
return read_flash_string(F("maxflow"));
return read_flash_string(F_(maxflow));
break;
case HeatingCircuit::Mode::ROOMINFLUENCE:
return read_flash_string(F("roominfluence"));
return read_flash_string(F_(roominfluence[0]));
break;
case HeatingCircuit::Mode::FLOWOFFSET:
return read_flash_string(F("flowtempoffset"));
return read_flash_string(F_(flowtempoffset[0]));
break;
case HeatingCircuit::Mode::TEMPAUTO:
return read_flash_string(F_(tempauto));
break;
case HeatingCircuit::Mode::NOREDUCE:
return read_flash_string(F_(noreduce));
break;
default:
case HeatingCircuit::Mode::UNKNOWN:
return read_flash_string(F("unknown"));
return read_flash_string(F_(unknown));
break;
}
}
@@ -751,6 +770,19 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr<const Telegram> telegram
has_update(telegram->read_value(hc->mode, 1)); // 1 = manual, 2 = auto
}
// type 0x02A5 - data from Worchester CRF200
void Thermostat::process_CRFMonitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
has_update(telegram->read_value(hc->curr_roomTemp, 0)); // is * 10
has_update(telegram->read_bitvalue(hc->modetype, 2, 0));
has_update(telegram->read_bitvalue(hc->mode, 2, 4)); // bit 4, mode (auto=0 or off=1)
has_update(telegram->read_value(hc->setpoint_roomTemp, 6, 1)); // is * 2, force as single byte
has_update(telegram->read_value(hc->targetflowtemp, 4));
}
// type 0x02A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10
void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
@@ -792,8 +824,9 @@ void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
// check why mode is both in the Monitor and Set for the RC300. It'll be read twice!
// has_update(telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF
has_update(telegram->read_value(hc->daytemp, 2)); // is * 2
has_update(telegram->read_value(hc->nighttemp, 4)); // is * 2
has_update(telegram->read_value(hc->daytemp, 2)); // is * 2
has_update(telegram->read_value(hc->nighttemp, 4)); // is * 2
has_update(telegram->read_value(hc->tempautotemp, 8));
has_update(telegram->read_value(hc->manualtemp, 10)); // is * 2
has_update(telegram->read_value(hc->program, 11)); // timer program 1 or 2
}
@@ -957,8 +990,10 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(hc->nofrosttemp, 23)); // is * 1
has_update(telegram->read_value(hc->flowtempoffset, 24)); // is * 1, only in mixed circuits
has_update(telegram->read_value(hc->reducemode, 25)); // 0-nofrost, 1-reduce, 2-roomhold, 3-outdoorhold
has_update(telegram->read_value(hc->control, 26)); // 0-off, 1-RC20 (remote), 2-RC35
has_update(telegram->read_value(hc->controlmode, 33)); // 0-outdoortemp, 1-roomtemp
// has_update(telegram->read_value(hc->noreducetemp, 38)); // outdoor temperature for no reduce
has_update(telegram->read_value(hc->tempautotemp, 37));
has_update(telegram->read_value(hc->noreducetemp, 38)); // outdoor temperature for no reduce
has_update(telegram->read_value(hc->minflowtemp, 16));
if (hc->heatingtype == 3) {
has_update(telegram->read_value(hc->designtemp, 36)); // is * 1
@@ -977,6 +1012,8 @@ void Thermostat::process_RC35Timer(std::shared_ptr<const Telegram> telegram) {
}
has_update(telegram->read_value(hc->program, 84)); // 0 .. 10, 0-userprogram 1, 10-userprogram 2
has_update(telegram->read_value(hc->pause, 85)); // time in hours
has_update(telegram->read_value(hc->party, 86)); // time in hours
}
// process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
@@ -1135,10 +1172,11 @@ bool Thermostat::set_remotetemp(const char * value, const int8_t id) {
}
if (f > 100 || f < 0) {
Roomctrl::set_remotetemp(hc->hc_num() - 1, EMS_VALUE_SHORT_NOTSET);
hc->remotetemp = EMS_VALUE_SHORT_NOTSET;
} else {
Roomctrl::set_remotetemp(hc->hc_num() - 1, (int16_t)(f * 10));
hc->remotetemp = (int16_t)(f * 10);
}
Roomctrl::set_remotetemp(hc->hc_num() - 1, hc->remotetemp);
return true;
}
@@ -1146,26 +1184,27 @@ bool Thermostat::set_remotetemp(const char * value, const int8_t id) {
// 0xA5 - Set the building settings
bool Thermostat::set_building(const char * value, const int8_t id) {
uint8_t bd = 0;
if (!Helpers::value2enum(value, bd, {F("light"), F("medium"), F("heavy")})) {
LOG_WARNING(F("Set building: Invalid value"));
return false;
}
LOG_INFO(F("Setting building to %s"), value);
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
write_command(0x240, 9, bd + 1, 0x240);
if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) {
LOG_INFO(F("Setting building to %s"), value);
write_command(0x240, 9, bd, 0x240);
return true;
}
} else {
write_command(EMS_TYPE_IBASettings, 6, bd, EMS_TYPE_IBASettings);
if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType2))) {
LOG_INFO(F("Setting building to %s"), value);
write_command(EMS_TYPE_IBASettings, 6, bd, EMS_TYPE_IBASettings);
return true;
}
}
return true;
LOG_WARNING(F("Set building: Invalid value"));
return false;
}
// 0xA5 Set the language settings
bool Thermostat::set_language(const char * value, const int8_t id) {
uint8_t lg = 0;
if (!Helpers::value2enum(value, lg, {F("german"), F("dutch"), F("french"), F("italian")})) {
if (!Helpers::value2enum(value, lg, FL_(enum_ibaLanguage))) {
LOG_WARNING(F("Set language: Invalid value"));
return false;
}
@@ -1179,7 +1218,7 @@ bool Thermostat::set_language(const char * value, const int8_t id) {
// Set the control-mode for hc 0-off, 1-RC20, 2-RC3x
bool Thermostat::set_control(const char * value, const int8_t id) {
uint8_t ctrl = 0;
if (!Helpers::value2enum(value, ctrl, {F("off"), F("rc20"), F("rc3x")})) {
if (!Helpers::value2enum(value, ctrl, FL_(enum_control))) {
LOG_WARNING(F("Set control: Invalid value"));
return false;
}
@@ -1201,14 +1240,14 @@ bool Thermostat::set_wwmode(const char * value, const int8_t id) {
uint8_t set = 0xFF;
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
if (!Helpers::value2enum(value, set, {F("off"), F("low"), F("high"), F("auto"), F("own")})) {
if (!Helpers::value2enum(value, set, FL_(enum_wwMode))) {
LOG_WARNING(F("Set warm water mode: Invalid mode"));
return false;
}
LOG_INFO(F("Setting warm water mode to %s"), value);
write_command(0x02F5, 2, set, 0x02F5);
} else {
if (!Helpers::value2enum(value, set, {F("off"), F("on"), F("auto")})) {
if (!Helpers::value2enum(value, set, FL_(enum_wwMode2))) {
LOG_WARNING(F("Set warm water mode: Invalid mode"));
return false;
}
@@ -1260,7 +1299,7 @@ bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
uint8_t set = 0xFF;
if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) {
if (!Helpers::value2enum(value, set, {F("off"), F("on"), F("auto"), F("own")})) {
if (!Helpers::value2enum(value, set, FL_(enum_wwCircMode))) {
LOG_WARNING(F("Set warm water circulation mode: Invalid mode"));
return false;
}
@@ -1268,7 +1307,7 @@ bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
write_command(0x02F5, 3, set, 0x02F5);
return true;
}
if (!Helpers::value2enum(value, set, {F("off"), F("on"), F("auto")})) {
if (!Helpers::value2enum(value, set, FL_(enum_wwCircMode2))) {
LOG_WARNING(F("Set warm water circulation mode: Invalid mode"));
return false;
}
@@ -1279,34 +1318,39 @@ bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
// set the holiday as string dd.mm.yyyy-dd.mm.yyyy
bool Thermostat::set_holiday(const char * value, const int8_t id) {
std::string hd(30, '\0');
if (!Helpers::value2string(value, hd)) {
LOG_WARNING(F("Set holiday: Invalid value"));
return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> 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, device_id());
return false;
}
if (value == nullptr || value[0] == '-') {
read_command(timer_typeids[hc->hc_num() - 1], 87, 6);
return true;
} else if (strlen(value) == 1 && value[0] == '+') {
read_command(timer_typeids[hc->hc_num() - 1], 93, 6);
return true;
} else if (strlen(value) != 21) {
LOG_WARNING(F("Set holiday: Invalid value"));
return false;
}
uint8_t data[6];
data[0] = (hd[0] - '0') * 10 + (hd[1] - '0');
data[1] = (hd[3] - '0') * 10 + (hd[4] - '0');
data[2] = (hd[7] - '0') * 100 + (hd[8] - '0') * 10 + (hd[9] - '0');
data[3] = (hd[11] - '0') * 10 + (hd[12] - '0');
data[4] = (hd[14] - '0') * 10 + (hd[15] - '0');
data[5] = (hd[18] - '0') * 100 + (hd[19] - '0') * 10 + (hd[20] - '0');
data[0] = (value[0] - '0') * 10 + (value[1] - '0');
data[1] = (value[3] - '0') * 10 + (value[4] - '0');
data[2] = (value[7] - '0') * 100 + (value[8] - '0') * 10 + (value[9] - '0');
data[3] = (value[11] - '0') * 10 + (value[12] - '0');
data[4] = (value[14] - '0') * 10 + (value[15] - '0');
data[5] = (value[18] - '0') * 100 + (value[19] - '0') * 10 + (value[20] - '0');
if (hd[10] == '-') {
if (value[10] == '-') {
LOG_INFO(F("Setting holiday away from home for hc %d"), hc->hc_num());
write_command(timer_typeids[hc->hc_num() - 1], 87, data, 6, 0);
} else if (hd[10] == '+') {
} else if (value[10] == '+') {
LOG_INFO(F("Setting holiday at home for hc %d"), hc->hc_num());
write_command(timer_typeids[hc->hc_num() - 1], 93, data, 6, 0);
} else {
LOG_WARNING(F("Set holiday: Invalid"));
LOG_WARNING(F("Set holiday: Invalid value"));
return false;
}
@@ -1315,18 +1359,21 @@ bool Thermostat::set_holiday(const char * value, const int8_t id) {
// set pause in hours
bool Thermostat::set_pause(const char * value, const int8_t id) {
int hrs = 0;
if (!Helpers::value2number(value, hrs)) {
LOG_WARNING(F("Set pause: Invalid value"));
return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> 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, device_id());
return false;
}
if (value == nullptr) {
read_command(timer_typeids[hc->hc_num() - 1], 85, 1);
return true;
}
int hrs = 0;
if (!Helpers::value2number(value, hrs)) {
LOG_WARNING(F("Set pause: Invalid value"));
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);
@@ -1336,18 +1383,21 @@ bool Thermostat::set_pause(const char * value, const int8_t id) {
// set partymode in hours
bool Thermostat::set_party(const char * value, const int8_t id) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> 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, device_id());
return false;
}
if (value == nullptr) {
read_command(timer_typeids[hc->hc_num() - 1], 85, 1);
return true;
}
int hrs = 0;
if (!Helpers::value2number(value, hrs)) {
LOG_WARNING(F("Set party: Invalid value"));
return false;
}
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> 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, 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);
@@ -1492,7 +1542,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
offset = EMS_OFFSET_RC20Set_mode;
validate_typeid = set_typeids[hc_p];
break;
case EMSdevice::EMS_DEVICE_FLAG_RC20_2: // ES72
case EMSdevice::EMS_DEVICE_FLAG_RC20_N: // ES72
offset = EMS_OFFSET_RC20_2_Set_mode;
validate_typeid = set_typeids[hc_p];
break;
@@ -1501,7 +1551,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
validate_typeid = set_typeids[hc_p];
break;
case EMSdevice::EMS_DEVICE_FLAG_RC35:
case EMSdevice::EMS_DEVICE_FLAG_RC30_1:
case EMSdevice::EMS_DEVICE_FLAG_RC30_N:
offset = EMS_OFFSET_RC35Set_mode;
validate_typeid = set_typeids[hc_p];
break;
@@ -1555,7 +1605,7 @@ bool Thermostat::set_summermode(const char * value, const int8_t id) {
return false;
}
uint8_t set = 0xFF;
if (!Helpers::value2enum(value, set, {F("summer"), F("auto"), F("winter")})) {
if (!Helpers::value2enum(value, set, FL_(enum_summermode))) {
LOG_WARNING(F("Setting summer mode: Invalid mode"));
return false;
}
@@ -1573,7 +1623,7 @@ bool Thermostat::set_reducemode(const char * value, const int8_t id) {
return false;
}
uint8_t set = 0xFF;
if (!Helpers::value2enum(value, set, {F("nofrost"), F("reduce"), F("room"), F("outdoor")})) {
if (!Helpers::value2enum(value, set, FL_(enum_reducemode))) {
LOG_WARNING(F("Setting reduce mode: Invalid mode"));
return false;
}
@@ -1592,13 +1642,13 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
}
uint8_t set = 0xFF;
if (model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) {
if (Helpers::value2enum(value, set, {F("off"), F("outdoor"), F("simple"), F("MPC"), F("room"), F("power"), F("const")})) {
if (Helpers::value2enum(value, set, FL_(enum_controlmode))) {
LOG_INFO(F("Setting control mode to %d for heating circuit %d"), set, hc->hc_num());
write_command(curve_typeids[hc->hc_num() - 1], 0, set, curve_typeids[hc->hc_num() - 1]);
return true;
}
} else if (model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1) {
if (Helpers::value2enum(value, set, {F("outdoor"), F("room")})) {
} else if (model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N) {
if (Helpers::value2enum(value, set, FL_(enum_controlmode2))) {
LOG_INFO(F("Setting control mode to %d for heating circuit %d"), set, hc->hc_num());
write_command(set_typeids[hc->hc_num() - 1], 33, set, set_typeids[hc->hc_num() - 1]);
return true;
@@ -1617,7 +1667,17 @@ bool Thermostat::set_switchtime(const char * value, const int8_t id) {
LOG_WARNING(F("Setting switchtime: Heating Circuit %d not found or activated"), hc_num);
return false;
}
if (strlen(value) != 12) {
if (value == nullptr) {
return false;
} else if (strlen(value) == 2) { // query point 01?
uint8_t no = (value[0] - '0') * 10 + (value[1] - '0');
if (no < 42) {
read_command(timer_typeids[hc->hc_num() - 1], 2 * no, 2);
return true;
}
return false;
} else if (strlen(value) != 12) {
LOG_WARNING(F("Setting switchtime: Invalid data"));
return false;
}
@@ -1637,7 +1697,7 @@ bool Thermostat::set_switchtime(const char * value, const int8_t id) {
return false;
}
if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1)) {
if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N)) {
write_command(timer_typeids[hc->hc_num() - 1], no * 2, (uint8_t *)&data, 2, timer_typeids[hc->hc_num() - 1]);
} else {
LOG_WARNING(F("Setting switchtime: thermostat not supported"));
@@ -1666,9 +1726,9 @@ bool Thermostat::set_program(const char * value, const int8_t id) {
return false;
}
if (model() == EMS_DEVICE_FLAG_RC20_2 && set > 0 && set < 10) {
if (model() == EMS_DEVICE_FLAG_RC20_N && set > 0 && set < 10) {
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
} else if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1) && set < 11) {
} else if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N) && set < 11) {
write_command(timer_typeids[hc->hc_num() - 1], 84, set, timer_typeids[hc->hc_num() - 1]);
} else if ((model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) && (set == 1 || set == 2)) {
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
@@ -1717,6 +1777,12 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
case HeatingCircuit::Mode::MANUAL:
offset = 0x0A; // manual offset
break;
case HeatingCircuit::Mode::TEMPAUTO:
offset = 0x08; // manual offset
if (temperature == -1) {
factor = 0xFF; // use factor as value
}
break;
case HeatingCircuit::Mode::COMFORT:
offset = 0x02; // comfort offset level 2
break;
@@ -1783,7 +1849,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
break;
}
} else if (model == EMS_DEVICE_FLAG_RC20_2) {
} else if (model == EMS_DEVICE_FLAG_RC20_N) {
switch (mode) {
case HeatingCircuit::Mode::NIGHT: // change the night temp
offset = EMS_OFFSET_RC20_2_Set_temp_night;
@@ -1798,7 +1864,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
break;
}
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_1)) {
} else if ((model == EMS_DEVICE_FLAG_RC35) || (model == EMS_DEVICE_FLAG_RC30_N)) {
validate_typeid = set_typeids[hc->hc_num() - 1];
switch (mode) {
case HeatingCircuit::Mode::NIGHT: // change the night temp
@@ -1837,6 +1903,13 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = 4;
factor = 1;
break;
case HeatingCircuit::Mode::NOREDUCE:
offset = EMS_OFFSET_RC35Set_noreducetemp;
factor = 1;
break;
case HeatingCircuit::Mode::TEMPAUTO:
offset = EMS_OFFSET_RC35Set_seltemp;
break;
case HeatingCircuit::Mode::MINFLOW:
offset = 16;
factor = 1;
@@ -2005,6 +2078,14 @@ bool Thermostat::set_manualtemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::MANUAL);
}
bool Thermostat::set_tempautotemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::TEMPAUTO);
}
bool Thermostat::set_noreducetemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::NOREDUCE);
}
bool Thermostat::set_flowtempoffset(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET);
}
@@ -2021,142 +2102,64 @@ bool Thermostat::set_roominfluence(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE);
}
// API commands for MQTT and Console
void Thermostat::add_commands() {
// if this thermostat doesn't support write, don't add the commands
if (has_flags(EMS_DEVICE_FLAG_NO_WRITE)) {
return;
}
// register main device values, top level for all thermostats (excluding heating circuits)
// as these are done in void Thermostat::register_device_values_hc()
void Thermostat::register_device_values() {
// Common for all thermostats
register_device_value(TAG_THERMOSTAT_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
register_device_value(TAG_THERMOSTAT_DATA, &errorCode_, DeviceValueType::TEXT, nullptr, FL_(errorCode), DeviceValueUOM::NONE);
register_device_value(TAG_THERMOSTAT_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, FL_(lastCode), DeviceValueUOM::NONE);
// common to all thermostats
register_mqtt_cmd(F("temp"), MAKE_CF_CB(set_temp), FLAG_HC);
register_mqtt_cmd(F("mode"), MAKE_CF_CB(set_mode), FLAG_HC);
if (model() == EMS_DEVICE_FLAG_RC35) { // section is together with RC30
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
}
switch (model()) {
switch (this->model()) {
case EMS_DEVICE_FLAG_RC100:
case EMS_DEVICE_FLAG_RC300:
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
register_mqtt_cmd(F("manualtemp"), MAKE_CF_CB(set_manualtemp), FLAG_HC);
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC);
register_mqtt_cmd(F("comforttemp"), MAKE_CF_CB(set_comforttemp), FLAG_HC);
register_mqtt_cmd(F("summermode"), MAKE_CF_CB(set_summermode), FLAG_HC);
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC);
register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode));
register_mqtt_cmd(F("wwsettemp"), MAKE_CF_CB(set_wwtemp));
register_mqtt_cmd(F("wwsettemplow"), MAKE_CF_CB(set_wwtemplow));
register_mqtt_cmd(F("wwonetime"), MAKE_CF_CB(set_wwonetime));
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode));
register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building));
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC);
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC);
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC);
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC);
register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp));
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC);
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC);
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC);
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
register_device_value(TAG_THERMOSTAT_DATA, &floordrystatus_, DeviceValueType::ENUM, FL_(enum_floordrystatus), FL_(floordrystatus), DeviceValueUOM::NONE);
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp2_, DeviceValueType::SHORT, FL_(div10), FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &floordrytemp_, DeviceValueType::UINT, nullptr, FL_(floordrytemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemp));
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
register_device_value(TAG_DEVICE_DATA_WW, &wwSetTempLow_, DeviceValueType::UINT, nullptr, FL_(wwSetTempLow), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemplow));
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
register_device_value(TAG_DEVICE_DATA_WW, &wwExtra1_, DeviceValueType::UINT, nullptr, FL_(wwExtra1), DeviceValueUOM::DEGREES);
register_device_value(TAG_DEVICE_DATA_WW, &wwExtra2_, DeviceValueType::UINT, nullptr, FL_(wwExtra2), DeviceValueUOM::DEGREES);
break;
case EMS_DEVICE_FLAG_RC20_2:
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp));
register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp));
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program));
case EMS_DEVICE_FLAG_RC20_N:
case EMS_DEVICE_FLAG_RC20:
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE); // can't set datetime
break;
case EMS_DEVICE_FLAG_RC30_1: // only RC30_1
register_mqtt_cmd(F("clockoffset"), MAKE_CF_CB(set_clockoffset));
register_mqtt_cmd(F("language"), MAKE_CF_CB(set_language));
register_mqtt_cmd(F("display"), MAKE_CF_CB(set_display));
case EMS_DEVICE_FLAG_RC35: // RC30 and RC35
register_mqtt_cmd(F("nighttemp"), MAKE_CF_CB(set_nighttemp), FLAG_HC);
register_mqtt_cmd(F("daytemp"), MAKE_CF_CB(set_daytemp), FLAG_HC);
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
register_mqtt_cmd(F("remotetemp"), MAKE_CF_CB(set_remotetemp), FLAG_HC);
register_mqtt_cmd(F("minexttemp"), MAKE_CF_CB(set_minexttemp));
register_mqtt_cmd(F("calinttemp"), MAKE_CF_CB(set_calinttemp));
register_mqtt_cmd(F("building"), MAKE_CF_CB(set_building));
register_mqtt_cmd(F("control"), MAKE_CF_CB(set_control), FLAG_HC);
register_mqtt_cmd(F("pause"), MAKE_CF_CB(set_pause), FLAG_HC);
register_mqtt_cmd(F("party"), MAKE_CF_CB(set_party), FLAG_HC);
register_mqtt_cmd(F("holiday"), MAKE_CF_CB(set_holiday), FLAG_HC);
register_mqtt_cmd(F("summertemp"), MAKE_CF_CB(set_summertemp), FLAG_HC);
register_mqtt_cmd(F("designtemp"), MAKE_CF_CB(set_designtemp), FLAG_HC);
register_mqtt_cmd(F("offsettemp"), MAKE_CF_CB(set_offsettemp), FLAG_HC);
register_mqtt_cmd(F("holidaytemp"), MAKE_CF_CB(set_holidaytemp), FLAG_HC);
register_mqtt_cmd(F("wwmode"), MAKE_CF_CB(set_wwmode));
register_mqtt_cmd(F("wwcircmode"), MAKE_CF_CB(set_wwcircmode));
register_mqtt_cmd(F("roominfluence"), MAKE_CF_CB(set_roominfluence), FLAG_HC);
register_mqtt_cmd(F("flowtempoffset"), MAKE_CF_CB(set_flowtempoffset), FLAG_HC);
register_mqtt_cmd(F("minflowtemp"), MAKE_CF_CB(set_minflowtemp), FLAG_HC);
register_mqtt_cmd(F("maxflowtemp"), MAKE_CF_CB(set_maxflowtemp), FLAG_HC);
register_mqtt_cmd(F("reducemode"), MAKE_CF_CB(set_reducemode), FLAG_HC);
register_mqtt_cmd(F("program"), MAKE_CF_CB(set_program), FLAG_HC);
register_mqtt_cmd(F("switchtime"), MAKE_CF_CB(set_switchtime), FLAG_HC);
register_mqtt_cmd(F("controlmode"), MAKE_CF_CB(set_controlmode), FLAG_HC);
case EMS_DEVICE_FLAG_RC30_N:
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE); // can't set datetime
register_device_value(TAG_THERMOSTAT_DATA, &ibaMainDisplay_, DeviceValueType::ENUM, FL_(enum_ibaMainDisplay), FL_(ibaMainDisplay), DeviceValueUOM::NONE);
register_device_value(TAG_THERMOSTAT_DATA, &ibaLanguage_, DeviceValueType::ENUM, FL_(enum_ibaLanguage), FL_(ibaLanguage), DeviceValueUOM::NONE);
register_device_value(TAG_THERMOSTAT_DATA, &ibaClockOffset_, DeviceValueType::UINT, nullptr, FL_(ibaClockOffset), DeviceValueUOM::NONE); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_calinttemp));
register_device_value(TAG_THERMOSTAT_DATA, &ibaMinExtTemperature_, DeviceValueType::INT, nullptr, FL_(ibaMinExtTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minexttemp));
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
break;
case EMS_DEVICE_FLAG_RC35:
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), FL_(ibaCalIntTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_calinttemp));
register_device_value(TAG_THERMOSTAT_DATA, &ibaMinExtTemperature_, DeviceValueType::INT, nullptr, FL_(ibaMinExtTemperature), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minexttemp));
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor1_, DeviceValueType::USHORT, FL_(div10), FL_(tempsensor1), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor2_, DeviceValueType::USHORT, FL_(div10), FL_(tempsensor2), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, FL_(dampedoutdoortemp), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building));
register_device_value(TAG_DEVICE_DATA_WW, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));
register_device_value(TAG_DEVICE_DATA_WW, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), FL_(wWCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcircmode));
break;
case EMS_DEVICE_FLAG_JUNKERS:
register_mqtt_cmd(F("datetime"), MAKE_CF_CB(set_datetime));
register_mqtt_cmd(F("nofrosttemp"), MAKE_CF_CB(set_nofrosttemp), FLAG_HC);
register_mqtt_cmd(F("ecotemp"), MAKE_CF_CB(set_ecotemp), FLAG_HC);
register_mqtt_cmd(F("heattemp"), MAKE_CF_CB(set_heattemp), FLAG_HC);
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, FL_(dateTime), DeviceValueUOM::NONE, MAKE_CF_CB(set_datetime));
break;
default:
break;
}
}
// register main device values, top level for all thermostats (excluding heating circuits)
void Thermostat::register_device_values() {
uint8_t model = this->model();
// Common for all thermostats
register_device_value(TAG_THERMOSTAT_DATA, &id_, DeviceValueType::UINT, nullptr, F("id"), nullptr); // empty full name to prevent being shown in web or console
register_device_value(TAG_THERMOSTAT_DATA, &dateTime_, DeviceValueType::TEXT, nullptr, F("dateTime"), F("date/time"));
register_device_value(TAG_THERMOSTAT_DATA, &errorCode_, DeviceValueType::TEXT, nullptr, F("errorCode"), F("error code"));
register_device_value(TAG_THERMOSTAT_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, F("lastCode"), F("last error"));
// RC30 only
if (model == EMSdevice::EMS_DEVICE_FLAG_RC30_1) {
register_device_value(TAG_THERMOSTAT_DATA, &ibaMainDisplay_, DeviceValueType::ENUM, FL_(enum_ibaMainDisplay), F("display"), F("display"));
register_device_value(TAG_THERMOSTAT_DATA, &ibaLanguage_, DeviceValueType::ENUM, FL_(enum_ibaLanguage), F("language"), F("language"));
register_device_value(TAG_THERMOSTAT_DATA, &ibaClockOffset_, DeviceValueType::UINT, nullptr, F("offsetclock"), F("clock offset")); // offset (in sec) to clock, 0xff=-1s, 0x02=2s
}
// RC300 and RC100
if (model == EMS_DEVICE_FLAG_RC300 || model == EMS_DEVICE_FLAG_RC100) {
register_device_value(TAG_THERMOSTAT_DATA, &floordrystatus_, DeviceValueType::ENUM, FL_(enum_floordrystatus), F("floordry"), F("floor drying"));
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp2_, DeviceValueType::SHORT, FL_(div10), F("dampedoutdoortemp"), F("damped outdoor temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &floordrytemp_, DeviceValueType::UINT, nullptr, F("floordrytemp"), F("floor drying temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType), F("building"), F("building"));
register_device_value(TAG_THERMOSTAT_DATA, &wwSetTemp_, DeviceValueType::UINT, nullptr, F("wwsettemp"), F("warm water set temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), F("wwmode"), F("warm water mode"));
register_device_value(TAG_THERMOSTAT_DATA, &wwSetTempLow_, DeviceValueType::UINT, nullptr, F("wwsettemplow"), F("warm water set temperature low"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), F("wwcircmode"), F("warm water circulation mode"));
register_device_value(TAG_THERMOSTAT_DATA, &wwExtra1_, DeviceValueType::UINT, nullptr, F("wwextra1"), F("warm water circuit 1 extra"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &wwExtra2_, DeviceValueType::UINT, nullptr, F("wwextra2"), F("warm water circuit 2 extra"), DeviceValueUOM::DEGREES);
}
// RC30 and RC35
if (model == EMS_DEVICE_FLAG_RC35 || model == EMS_DEVICE_FLAG_RC30_1) {
register_device_value(TAG_THERMOSTAT_DATA, &ibaCalIntTemperature_, DeviceValueType::INT, FL_(div2), F("intoffset"), F("offset internal temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA,
&ibaMinExtTemperature_,
DeviceValueType::INT,
nullptr,
F("minexttemp"),
F("min external temperature"),
DeviceValueUOM::DEGREES); // min ext temp for heating curve, in deg.
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor1_, DeviceValueType::USHORT, FL_(div10), F("inttemp1"), F("temperature sensor 1"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &tempsensor2_, DeviceValueType::USHORT, FL_(div10), F("inttemp2"), F("temperature sensor 2"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &dampedoutdoortemp_, DeviceValueType::INT, nullptr, F("dampedoutdoortemp"), F("damped outdoor temperature"), DeviceValueUOM::DEGREES);
register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, FL_(enum_ibaBuildingType2), F("building"), F("building"));
register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), F("wwmode"), F("warm water mode"));
register_device_value(TAG_THERMOSTAT_DATA, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode2), F("wwcircmode"), F("warm water circulation mode"));
}
}
// registers the values for a heating circuit
void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::HeatingCircuit> hc) {
uint8_t model = hc->get_model();
@@ -2178,8 +2181,13 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
setpoint_temp_divider = FL_(div2);
curr_temp_divider = FL_(div10);
}
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, F("seltemp"), F("setpoint room temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, F("currtemp"), F("current room temperature"), DeviceValueUOM::DEGREES);
if (has_flags(EMS_DEVICE_FLAG_NO_WRITE) || device_id() != EMSESP::actual_master_thermostat()) {
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(setpoint_roomTemp), DeviceValueUOM::DEGREES);
} else {
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(setpoint_roomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(temp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 5, 29);
}
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, FL_(curr_roomTemp), DeviceValueUOM::DEGREES);
if (device_id() != EMSESP::actual_master_thermostat()) {
return;
@@ -2190,81 +2198,94 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
if (Mqtt::ha_enabled()) {
uint8_t option = Mqtt::ha_climate_format();
if (option == Mqtt::HA_Climate_Format::CURRENT) {
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, F("hatemp"), nullptr);
register_device_value(tag, &hc->curr_roomTemp, DeviceValueType::SHORT, curr_temp_divider, FL_(hatemp), DeviceValueUOM::NONE);
} else if (option == Mqtt::HA_Climate_Format::SETPOINT) {
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, F("hatemp"), nullptr);
register_device_value(tag, &hc->setpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, FL_(hatemp), DeviceValueUOM::NONE);
} else if (option == Mqtt::HA_Climate_Format::ZERO) {
register_device_value(tag, &zero_value_, DeviceValueType::UINT, nullptr, F("hatemp"), nullptr);
register_device_value(tag, &zero_value_, DeviceValueType::UINT, nullptr, FL_(hatemp), DeviceValueUOM::NONE);
}
// if we're sending to HA the only valid mode types are heat, auto and off
// manual & day = heat
// night & off = off
// everything else auto
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_hamode), F("hamode"), nullptr);
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_hamode), FL_(hamode), DeviceValueUOM::NONE);
}
if (model == EMSdevice::EMS_DEVICE_FLAG_RC300 || model == EMSdevice::EMS_DEVICE_FLAG_RC100) {
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode), F("mode"), F("mode"));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype), F("modetype"), F("mode type"));
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("ecotemp"), F("eco temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->manualtemp, DeviceValueType::UINT, FL_(div2), F("manualtemp"), F("manual temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("comforttemp"), F("comfort temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, F("summertemp"), F("summer temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, F("designtemp"), F("design temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, nullptr, F("offsettemp"), F("offset temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, F("minflowtemp"), F("min flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, F("maxflowtemp"), F("max flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, F("roominfluence"), F("room influence"));
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, F("nofrosttemp"), F("nofrost temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, F("targetflowtemp"), F("target flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), F("heatingtype"), F("heating type"));
register_device_value(tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), F("summermode"), F("summer mode"));
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), F("controlmode"), F("control mode"));
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, F("program"), F("program"));
}
if (model == EMS_DEVICE_FLAG_RC20) {
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), F("mode"), F("mode"));
}
if (model == EMS_DEVICE_FLAG_RC20_2) {
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), F("mode"), F("mode"));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype2), F("modetype"), F("mode type"));
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("daytemp"), F("day temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("nighttemp"), F("night temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, F("program"), F("program"));
}
if (model == EMS_DEVICE_FLAG_RC35 || model == EMS_DEVICE_FLAG_RC30_1) {
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), F("mode"), F("mode"));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), F("modetype"), F("mode type"));
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("daytemp"), F("day temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("nighttemp"), F("night temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, F("designtemp"), F("design temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, FL_(div2), F("offsettemp"), F("offset temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->holidaytemp, DeviceValueType::UINT, FL_(div2), F("holidaytemp"), F("holiday temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, F("targetflowtemp"), F("target flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, F("summertemp"), F("summer temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, F("summermode"), F("summer mode"));
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, F("holidaymode"), F("holiday mode"));
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, F("nofrosttemp"), F("nofrost temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, F("roominfluence"), F("room influence"));
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, F("minflowtemp"), F("min flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, F("maxflowtemp"), F("max flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, nullptr, F("flowtempoffset"), F("flow temperature offset"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), F("heatingtype"), F("heating type"));
register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), F("reducemode"), F("reduce mode"));
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), F("controlmode"), F("control mode"));
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, F("program"), F("program"));
}
if (model == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode4), F("mode"), F("mode"));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype4), F("modetype"), F("mode type"));
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("heattemp"), F("heat temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("ecotemp"), F("eco temperature"), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(div2), F("nofrosttemp"), F("nofrost temperature"), DeviceValueUOM::DEGREES);
switch (model) {
case EMS_DEVICE_FLAG_RC100:
case EMS_DEVICE_FLAG_RC300:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype), FL_(modetype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(ecotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ecotemp));
register_device_value(tag, &hc->manualtemp, DeviceValueType::UINT, FL_(div2), FL_(manualtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_manualtemp));
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(comforttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_comforttemp));
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp));
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp));
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, nullptr, FL_(offsettemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_offsettemp));
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence));
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summermode), DeviceValueUOM::NONE, MAKE_CF_CB(set_summermode));
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE);
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp));
break;
case EMS_DEVICE_FLAG_CRF:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode5), FL_(mode), DeviceValueUOM::NONE);
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype5), FL_(modetype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
break;
case EMS_DEVICE_FLAG_RC20:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
break;
case EMS_DEVICE_FLAG_RC20_N:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype2), FL_(modetype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
break;
case EMS_DEVICE_FLAG_RC30_N:
case EMS_DEVICE_FLAG_RC35:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), FL_(modetype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp));
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp));
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp));
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, FL_(div2), FL_(offsettemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_offsettemp));
register_device_value(tag, &hc->holidaytemp, DeviceValueType::UINT, FL_(div2), FL_(holidaytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_holidaytemp));
register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, nullptr, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp));
register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::NONE);
register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, nullptr, FL_(holidaymode), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday));
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence));
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, nullptr, FL_(flowtempoffset), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowtempoffset));
register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(reducemode), DeviceValueUOM::NONE, MAKE_CF_CB(set_reducemode));
register_device_value(tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode));
register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control));
register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, &hc->pause, DeviceValueType::UINT, nullptr, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause));
register_device_value(tag, &hc->party, DeviceValueType::UINT, nullptr, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party));
register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp));
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, nullptr, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp));
register_device_value(tag, &hc->remotetemp, DeviceValueType::SHORT, FL_(div10), FL_(remotetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_remotetemp));
register_device_value(tag, &dummychar_, DeviceValueType::TEXT, nullptr, FL_(switchtime), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime));
break;
case EMS_DEVICE_FLAG_JUNKERS:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode4), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype4), FL_(modetype), DeviceValueUOM::NONE);
register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(heattemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heattemp));
register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(ecotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ecotemp));
register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(div2), FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp));
break;
}
}

View File

@@ -36,6 +36,8 @@ class Thermostat : public EMSdevice {
int16_t setpoint_roomTemp;
int16_t curr_roomTemp;
int16_t remotetemp; // for readback
uint8_t tempautotemp;
uint8_t mode;
uint8_t modetype;
uint8_t summermode;
@@ -58,6 +60,10 @@ class Thermostat : public EMSdevice {
uint8_t reducemode;
uint8_t program;
uint8_t controlmode;
uint8_t control;
uint8_t pause;
uint8_t party;
int8_t noreducetemp; // signed -20°C to +10°C
uint8_t hc_num() const {
return hc_num_;
@@ -93,6 +99,9 @@ class Thermostat : public EMSdevice {
MINFLOW,
MAXFLOW,
ROOMINFLUENCE,
TEMPAUTO,
NOREDUCE,
ON,
UNKNOWN
};
@@ -114,8 +123,6 @@ class Thermostat : public EMSdevice {
private:
static uuid::log::Logger logger_;
void add_commands();
void register_device_values();
void register_device_values(uint8_t hc_num);
@@ -137,7 +144,8 @@ class Thermostat : public EMSdevice {
char dateTime_[25]; // date and time stamp
char errorCode_[15]; // code from 0xA2 as string i.e. "A22(816)"
uint16_t errorNumber_; // used internally to build error code
char lastCode_[30];
char lastCode_[30]; // error log
char dummychar_[5]; // for commands with no output
// Installation parameters
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
@@ -200,6 +208,7 @@ class Thermostat : public EMSdevice {
static constexpr uint8_t EMS_OFFSET_RC35Set_heatingtype = 0; // e.g. floor heating = 3
static constexpr uint8_t EMS_OFFSET_RC35Set_targetflowtemp = 14; // target flow temperature
static constexpr uint8_t EMS_OFFSET_RC35Set_seltemp = 37; // selected temp
static constexpr uint8_t EMS_OFFSET_RC35Set_noreducetemp = 38; // temp to stop reducing
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_offset = 6;
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_flowoffset = 24;
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_design = 17;
@@ -268,6 +277,7 @@ class Thermostat : public EMSdevice {
void process_RC20Set_2(std::shared_ptr<const Telegram> telegram);
void process_RC10Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC10Set(std::shared_ptr<const Telegram> telegram);
void process_CRFMonitor(std::shared_ptr<const Telegram> telegram);
void process_RC300Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC300Set(std::shared_ptr<const Telegram> telegram);
void process_RC300Summer(std::shared_ptr<const Telegram> telegram);
@@ -309,6 +319,8 @@ class Thermostat : public EMSdevice {
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_tempautotemp(const char * value, const int8_t id);
bool set_noreducetemp(const char * value, const int8_t id);
bool set_remotetemp(const char * value, const int8_t id);
bool set_roominfluence(const char * value, const int8_t id);
bool set_flowtempoffset(const char * value, const int8_t id);

View File

@@ -33,7 +33,8 @@ static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__
F_(hours),
F_(minutes),
F_(ua),
F_(bar)
F_(bar),
F_(kw)
};
@@ -44,7 +45,7 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = {
F_(tag_none), // ""
F_(tag_heartbeat), // ""
F_(tag_boiler_data), // ""
F_(tag_boiler_data_ww), // "warm water"
F_(tag_device_data_ww), // "warm water"
F_(tag_thermostat_data), // ""
F_(tag_hc1), // "hc1"
F_(tag_hc2), // "hc2"
@@ -77,9 +78,9 @@ static const __FlashStringHelper * const DeviceValueTAG_s[] PROGMEM = {
static const __FlashStringHelper * const DeviceValueTAG_mqtt[] PROGMEM = {
F_(tag_none), // ""
F_(tag_heartbeat_mqtt), // "heartbeat"
F_(heartbeat), // "heartbeat"
F_(tag_boiler_data_mqtt), // ""
F_(tag_boiler_data_ww_mqtt), // "ww"
F_(tag_device_data_ww_mqtt), // "ww"
F_(tag_thermostat_data), // ""
F_(tag_hc1), // "hc1"
F_(tag_hc2), // "hc2"
@@ -149,7 +150,7 @@ std::string EMSdevice::brand_to_string() const {
break;
case EMSdevice::Brand::NO_BRAND:
default:
return read_flash_string(F("---"));
return read_flash_string(F(""));
break;
}
@@ -211,31 +212,38 @@ std::string EMSdevice::device_type_2_device_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_P(topic, reinterpret_cast<PGM_P>(F_(boiler)))) {
// convert topic to lowercase and compare
char lowtopic[20];
strlcpy(lowtopic, topic, sizeof(lowtopic));
for (char * p = lowtopic; *p; p++) {
*p = tolower(*p);
}
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(boiler)))) {
return DeviceType::BOILER;
}
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(thermostat)))) {
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(thermostat)))) {
return DeviceType::THERMOSTAT;
}
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(system)))) {
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(system)))) {
return DeviceType::SYSTEM;
}
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(heatpump)))) {
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(heatpump)))) {
return DeviceType::HEATPUMP;
}
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(solar)))) {
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(solar)))) {
return DeviceType::SOLAR;
}
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(mixer)))) {
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(mixer)))) {
return DeviceType::MIXER;
}
if (!strcmp_P(topic, reinterpret_cast<PGM_P>(F_(dallassensor)))) {
if (!strcmp_P(lowtopic, reinterpret_cast<PGM_P>(F_(dallassensor)))) {
return DeviceType::DALLASSENSOR;
}
@@ -405,9 +413,9 @@ 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, uint8_t flag) {
Command::add(device_type_, cmd, f, flag);
}
// void EMSdevice::register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag) {
// Command::add(device_type_, cmd, f, flag);
// }
// register a callback function for a specific telegram type
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) {
@@ -423,7 +431,16 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __
// short_name: used in Mqtt as keys
// full name: used in Web and Console unless empty (nullptr)
// uom: unit of measure from DeviceValueUOM
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * short_name, const __FlashStringHelper * full_name, uint8_t uom) {
void EMSdevice::register_device_value(uint8_t tag,
void * value_p,
uint8_t type,
const __FlashStringHelper * const * options,
const __FlashStringHelper * short_name,
const __FlashStringHelper * full_name,
uint8_t uom,
bool has_cmd,
int32_t min,
uint32_t max) {
// init the value depending on it's type
if (type == DeviceValueType::TEXT) {
*(char *)(value_p) = {'\0'};
@@ -435,8 +452,10 @@ void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type,
*(uint16_t *)(value_p) = EMS_VALUE_USHORT_NOTSET;
} else if ((type == DeviceValueType::ULONG) || (type == DeviceValueType::TIME)) {
*(uint32_t *)(value_p) = EMS_VALUE_ULONG_NOTSET;
} else if (type == DeviceValueType::BOOL) {
*(int8_t *)(value_p) = EMS_VALUE_BOOL_NOTSET; // bool is uint8_t, but other initial value
} else {
*(uint8_t *)(value_p) = EMS_VALUE_UINT_NOTSET; // enums, uint8_t, bool behave as uint8_t
*(uint8_t *)(value_p) = EMS_VALUE_UINT_NOTSET; // enums behave as uint8_t
}
// count #options
@@ -448,14 +467,37 @@ void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type,
};
}
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom);
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom, 0, has_cmd, min, max);
}
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f, int32_t min, uint32_t max) {
register_device_value(tag, value_p, type, options, name[0], name[1], uom, (f != nullptr), min, max);
if (f != nullptr) {
if (tag >= TAG_HC1 && tag <= TAG_HC4) {
Command::add(device_type_, name[0], f, FLAG_HC);
} else {
Command::add(device_type_, name[0], f, 0);
}
}
}
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f) {
register_device_value(tag, value_p, type, options, name, uom, f, 0, 0);
}
// void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max) {
// register_device_value(tag, value_p, type, options, name, uom, nullptr, min, max);
// }
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom) {
register_device_value(tag, value_p, type, options, name, uom, nullptr, 0, 0);
}
// looks up the uom (suffix) for a given key from the device value table
std::string EMSdevice::get_value_uom(const char * key) {
// the key may have a suffix at the start which is between brackets. remove it.
char new_key[80];
strncpy(new_key, key, sizeof(new_key));
strlcpy(new_key, key, sizeof(new_key));
char * p = new_key;
if (key[0] == '(') {
while ((*p++ != ')') && (*p != '\0'))
@@ -566,7 +608,7 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
if (sz > num_elements) {
// add the unit of measure (uom)
if (dv.uom == DeviceValueUOM::MINUTES) {
data.add(nullptr);
data.add(nullptr); // use null for time/date
} else {
data.add(uom_to_string(dv.uom));
}
@@ -579,7 +621,15 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
snprintf_P(name, sizeof(name), "(%s) %s", tag_to_string(dv.tag).c_str(), uuid::read_flash_string(dv.full_name).c_str());
data.add(name);
}
num_elements = sz + 2;
// add the name of the Command function if it exists
if (dv.has_cmd) {
data.add(dv.short_name);
} else {
data.add("");
}
num_elements = sz + 3; // increase count by 3
}
}
}
@@ -587,19 +637,181 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
return (num_elements != 0);
}
bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t id) {
JsonObject json = root;
int8_t tag = id;
// check if we have hc or wwc
if (id >= 1 && id <= 4) {
tag = DeviceValueTAG::TAG_HC1 + id - 1;
} else if (id >= 8 && id <= 11) {
tag = DeviceValueTAG::TAG_WWC1 + id - 8;
}
// search device value with this tag
for (auto & dv : devicevalues_) {
if (strcmp(cmd, Helpers::toLower(uuid::read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) {
uint8_t divider = (dv.options_size == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0;
const char * type = "type";
const char * min = "min";
const char * max = "max";
const char * value = "value";
json["name"] = dv.short_name;
if (!tag_to_mqtt(dv.tag).empty()) {
json["circuit"] = tag_to_mqtt(dv.tag);
}
switch (dv.type) {
case DeviceValueType::ENUM: {
if (Helpers::hasValue((uint8_t)(*(uint8_t *)(dv.value_p)))) {
if (Mqtt::bool_format() == BOOL_FORMAT_10) {
json[value] = (uint8_t)(*(uint8_t *)(dv.value_p));
} else {
json[value] = dv.options[*(uint8_t *)(dv.value_p)]; // text
}
}
json[type] = F_(enum);
uint8_t min_ = (uuid::read_flash_string(dv.options[0]) == "") ? 1 : 0;
json[min] = min_;
json[max] = dv.options_size - 1;
JsonArray enum_ = json.createNestedArray(F_(enum));
for (uint8_t i = min_; i < dv.options_size; i++) {
enum_.add(dv.options[i]);
}
break;
}
case DeviceValueType::USHORT:
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
json[value] = Helpers::round2(*(uint16_t *)(dv.value_p), divider);
}
json[type] = F_(number);
json[min] = 0;
json[max] = divider ? EMS_VALUE_USHORT_NOTSET / divider : EMS_VALUE_USHORT_NOTSET;
break;
case DeviceValueType::UINT:
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
json[value] = Helpers::round2(*(uint8_t *)(dv.value_p), divider);
}
json[type] = F_(number);
json[min] = 0;
if (dv.uom == DeviceValueUOM::PERCENT) {
json[max] = 100;
} else {
json[max] = divider ? EMS_VALUE_UINT_NOTSET / divider : EMS_VALUE_UINT_NOTSET;
}
break;
case DeviceValueType::SHORT:
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
json[value] = Helpers::round2(*(int16_t *)(dv.value_p), divider);
}
json[type] = F_(number);
json[min] = divider ? -EMS_VALUE_SHORT_NOTSET / divider : -EMS_VALUE_SHORT_NOTSET;
json[max] = divider ? EMS_VALUE_SHORT_NOTSET / divider : EMS_VALUE_SHORT_NOTSET;
break;
case DeviceValueType::INT:
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
json[value] = Helpers::round2(*(int8_t *)(dv.value_p), divider);
}
json[type] = F_(number);
if (dv.uom == DeviceValueUOM::PERCENT) {
json[min] = -100;
json[max] = 100;
} else {
json[min] = divider ? -EMS_VALUE_INT_NOTSET / divider : -EMS_VALUE_INT_NOTSET;
json[max] = divider ? EMS_VALUE_INT_NOTSET / divider : EMS_VALUE_INT_NOTSET;
}
break;
case DeviceValueType::ULONG:
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
json[value] = Helpers::round2(*(uint32_t *)(dv.value_p), divider);
}
json[type] = F_(number);
json[min] = 0;
json[max] = divider ? EMS_VALUE_ULONG_NOTSET / divider : EMS_VALUE_ULONG_NOTSET;
break;
case DeviceValueType::BOOL: {
if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
if (dv.options_size == 2) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? dv.options[0] : dv.options[1];
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(on) : F_(off);
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(ON) : F_(OFF);
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
} else {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
}
}
json[type] = F("boolean");
json[min] = 0;
json[max] = 1;
JsonArray enum_ = json.createNestedArray(F_(enum));
if (dv.options_size == 2) {
enum_.add(dv.options[1]);
enum_.add(dv.options[0]);
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
enum_.add(F_(off));
enum_.add(F_(on));
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
enum_.add(F_(OFF));
enum_.add(F_(ON));
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
enum_.add(false);
enum_.add(true);
} else {
enum_.add(0);
enum_.add(1);
}
break;
}
case DeviceValueType::TIME:
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
json[value] = (divider) ? *(uint32_t *)(dv.value_p) / divider : *(uint32_t *)(dv.value_p);
}
json[type] = F_(number);
json[min] = 0;
json[max] = divider ? EMS_VALUE_ULONG_NOTSET / divider : EMS_VALUE_ULONG_NOTSET;
break;
case DeviceValueType::TEXT:
if (Helpers::hasValue((char *)(dv.value_p))) {
json[value] = (char *)(dv.value_p);
}
json[type] = F_(text);
break;
default:
json[type] = F_(unknown);
break;
}
if (dv.uom != DeviceValueUOM::NONE) {
json["unit"] = EMSdevice::uom_to_string(dv.uom);
}
json["writeable"] = dv.has_cmd;
// if we have individual limits, overwrite the common limits
if (dv.min != 0 || dv.max != 0) {
json[min] = dv.min;
json[max] = dv.max;
}
return true;
}
}
return false;
}
// For each value in the device create the json object pair and add it to given json
// return false if empty
// this is used to create both the MQTT payloads and Console messages (console = true)
bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter, const bool nested, const bool console) {
bool has_value = false; // to see if we've added a value. it's faster than doing a json.size() at the end
uint8_t old_tag = 255; // NAN
JsonObject json = root;
bool has_values = false; // to see if we've added a value. it's faster than doing a json.size() at the end
uint8_t old_tag = 255; // NAN
JsonObject json = root;
for (const auto & dv : devicevalues_) {
for (auto & dv : devicevalues_) {
bool has_value = false;
// only show if tag is either empty (TAG_NONE) or matches a value
// and don't show if full_name is empty unless we're outputing for mqtt payloads
// for nested we use all values
if (((nested) || tag_filter == DeviceValueTAG::TAG_NONE || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !console)) {
// for nested we use all values, dont show command only (have_cmd and no fulname)
if (((nested) || tag_filter == DeviceValueTAG::TAG_NONE || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !console) && !(dv.full_name == nullptr && dv.has_cmd)) {
// we have a tag if it matches the filter given, and that the tag name is not empty/""
bool have_tag = ((dv.tag != tag_filter) && !tag_to_string(dv.tag).empty());
@@ -626,7 +838,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
// handle Booleans (true, false)
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
// see if we have options for the bool's
if (dv.options_size == 2) {
if (dv.options_size == 2 && Mqtt::bool_format() != BOOL_FORMAT_10) {
json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1];
has_value = true;
} else {
@@ -727,20 +939,34 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
}
}
}
dv.ha |= has_value ? DeviceValueHA::HA_VALUE : DeviceValueHA::HA_NONE;
has_values |= has_value;
}
return has_value;
return has_values;
}
// create the Home Assistant configs for each value
// this is called when an MQTT publish is done via an EMS Device, and only done once
// this is called when an MQTT publish is done via an EMS Device
void EMSdevice::publish_mqtt_ha_sensor() {
for (const auto & dv : devicevalues_) {
Mqtt::publish_mqtt_ha_sensor(dv.type, dv.tag, dv.full_name, device_type_, dv.short_name, dv.uom);
for (auto & dv : devicevalues_) {
if (dv.ha == DeviceValueHA::HA_VALUE) {
Mqtt::publish_mqtt_ha_sensor(dv.type, dv.tag, dv.full_name, device_type_, dv.short_name, dv.uom);
dv.ha |= DeviceValueHA::HA_DONE;
}
}
if (!ha_config_done()) {
bool ok = publish_ha_config();
ha_config_done(ok); // see if it worked
}
}
bool ok = publish_ha_config();
ha_config_done(ok); // see if it worked
void EMSdevice::ha_config_clear() {
for (auto & dv : devicevalues_) {
// dv.ha &= ~DeviceValueHA::HA_DONE; // repubish all with values
dv.ha = DeviceValueHA::HA_NONE; // also wait for new value
}
ha_config_done(false);
}
// return the name of the telegram type
@@ -800,8 +1026,8 @@ void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, cons
}
// send Tx read command to the device
void EMSdevice::read_command(const uint16_t type_id) {
EMSESP::send_read_request(type_id, device_id());
void EMSdevice::read_command(const uint16_t type_id, const uint8_t offset, const uint8_t length) {
EMSESP::send_read_request(type_id, device_id(), offset, length);
}
} // namespace emsesp

View File

@@ -55,61 +55,16 @@ enum DeviceValueType : uint8_t {
};
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
// sequence is important!
// uom - also used with HA
MAKE_PSTR(percent, "%")
MAKE_PSTR(degrees, "°C")
MAKE_PSTR(kwh, "kWh")
MAKE_PSTR(wh, "Wh")
MAKE_PSTR(bar, "bar")
MAKE_PSTR(minutes, "minutes")
MAKE_PSTR(hours, "hours")
MAKE_PSTR(ua, "uA")
MAKE_PSTR(lmin, "l/min")
enum DeviceValueUOM : uint8_t { NONE = 0, DEGREES, PERCENT, LMIN, KWH, WH, HOURS, MINUTES, UA, BAR, PUMP };
// sequence is important!
enum DeviceValueUOM : uint8_t { NONE = 0, DEGREES, PERCENT, LMIN, KWH, WH, HOURS, MINUTES, UA, BAR, KW, PUMP };
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
// use empty string if want to suppress showing tags
MAKE_PSTR(tag_none, "")
MAKE_PSTR(tag_heartbeat, "")
MAKE_PSTR(tag_boiler_data, "")
MAKE_PSTR(tag_boiler_data_ww, "warm water")
MAKE_PSTR(tag_thermostat_data, "")
MAKE_PSTR(tag_hc1, "hc1")
MAKE_PSTR(tag_hc2, "hc2")
MAKE_PSTR(tag_hc3, "hc3")
MAKE_PSTR(tag_hc4, "hc4")
MAKE_PSTR(tag_wwc1, "wwc1")
MAKE_PSTR(tag_wwc2, "wwc2")
MAKE_PSTR(tag_wwc3, "wwc3")
MAKE_PSTR(tag_wwc4, "wwc4")
MAKE_PSTR(tag_hs1, "hs1")
MAKE_PSTR(tag_hs2, "hs2")
MAKE_PSTR(tag_hs3, "hs3")
MAKE_PSTR(tag_hs4, "hs4")
MAKE_PSTR(tag_hs5, "hs5")
MAKE_PSTR(tag_hs6, "hs6")
MAKE_PSTR(tag_hs7, "hs7")
MAKE_PSTR(tag_hs8, "hs8")
MAKE_PSTR(tag_hs9, "hs9")
MAKE_PSTR(tag_hs10, "hs10")
MAKE_PSTR(tag_hs11, "hs11")
MAKE_PSTR(tag_hs12, "hs12")
MAKE_PSTR(tag_hs13, "hs13")
MAKE_PSTR(tag_hs14, "hs14")
MAKE_PSTR(tag_hs15, "hs15")
MAKE_PSTR(tag_hs16, "hs16")
// MQTT topic names
MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
MAKE_PSTR(tag_boiler_data_mqtt, "")
MAKE_PSTR(tag_boiler_data_ww_mqtt, "ww")
enum DeviceValueTAG : uint8_t {
TAG_NONE = 0, // wild card
TAG_HEARTBEAT,
TAG_BOILER_DATA,
TAG_BOILER_DATA_WW,
TAG_DEVICE_DATA_WW,
TAG_THERMOSTAT_DATA,
TAG_HC1,
TAG_HC2,
@@ -139,12 +94,10 @@ enum DeviceValueTAG : uint8_t {
};
// mqtt flags for command subscriptions
enum MqttSubFlag : uint8_t {
FLAG_NORMAL = 0,
FLAG_HC,
FLAG_WWC,
FLAG_NOSUB
};
enum MqttSubFlag : uint8_t { FLAG_NORMAL = 0, FLAG_HC, FLAG_WWC, FLAG_NOSUB };
// mqtt-HA flags
enum DeviceValueHA : uint8_t { HA_NONE = 0, HA_VALUE, HA_DONE };
class EMSdevice {
public:
@@ -265,6 +218,7 @@ class EMSdevice {
bool handle_telegram(std::shared_ptr<const Telegram> telegram);
std::string get_value_uom(const char * key);
bool get_value_info(JsonObject & root, const char * cmd, const int8_t id);
bool generate_values_json(JsonObject & json, const uint8_t tag_filter, const bool nested, const bool console = false);
bool generate_values_json_web(JsonObject & json);
@@ -274,15 +228,22 @@ class EMSdevice {
const __FlashStringHelper * const * options,
const __FlashStringHelper * short_name,
const __FlashStringHelper * full_name,
uint8_t uom = DeviceValueUOM::NONE);
uint8_t uom,
bool has_cmd,
int32_t min,
uint32_t max);
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f, int32_t min, uint32_t max);
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, cmdfunction_p f);
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom);
// void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max);
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
void write_command(const uint16_t type_id, const uint8_t offset, 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 read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0);
void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f);
void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0);
// void register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0);
void publish_mqtt_ha_sensor();
@@ -300,6 +261,8 @@ class EMSdevice {
ha_config_done_ = v;
}
void ha_config_clear();
enum Brand : uint8_t {
NO_BRAND = 0, // 0
BOSCH, // 1
@@ -338,6 +301,12 @@ class EMSdevice {
// device flags: The lower 4 bits hold the unique identifier, the upper 4 bits are used for specific flags
static constexpr uint8_t EMS_DEVICE_FLAG_NONE = 0;
// Boiler
static constexpr uint8_t EMS_DEVICE_FLAG_EMS = 1;
static constexpr uint8_t EMS_DEVICE_FLAG_EMSPLUS = 2;
static constexpr uint8_t EMS_DEVICE_FLAG_HT3 = 3;
static constexpr uint8_t EMS_DEVICE_FLAG_HEATPUMP = 4;
// Solar Module
static constexpr uint8_t EMS_DEVICE_FLAG_SM10 = 1;
static constexpr uint8_t EMS_DEVICE_FLAG_SM100 = 2;
@@ -354,13 +323,14 @@ class EMSdevice {
static constexpr uint8_t EMS_DEVICE_FLAG_EASY = 1;
static constexpr uint8_t EMS_DEVICE_FLAG_RC10 = 2;
static constexpr uint8_t EMS_DEVICE_FLAG_RC20 = 3;
static constexpr uint8_t EMS_DEVICE_FLAG_RC20_2 = 4; // Variation on RC20, Older, like ES72
static constexpr uint8_t EMS_DEVICE_FLAG_RC30_1 = 5; // variation on RC30, Newer models
static constexpr uint8_t EMS_DEVICE_FLAG_RC20_N = 4; // Variation on RC20, Older, like ES72
static constexpr uint8_t EMS_DEVICE_FLAG_RC30_N = 5; // variation on RC30, Newer models
static constexpr uint8_t EMS_DEVICE_FLAG_RC30 = 6;
static constexpr uint8_t EMS_DEVICE_FLAG_RC35 = 7;
static constexpr uint8_t EMS_DEVICE_FLAG_RC300 = 8;
static constexpr uint8_t EMS_DEVICE_FLAG_RC100 = 9;
static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS = 10;
static constexpr uint8_t EMS_DEVICE_FLAG_CRF = 11; // CRF200 only monitor
void reserve_device_values(uint8_t elements) {
devicevalues_.reserve(elements);
@@ -407,6 +377,10 @@ class EMSdevice {
const __FlashStringHelper * short_name; // used in MQTT
const __FlashStringHelper * full_name; // used in Web and Console
uint8_t uom; // DeviceValueUOM::*
uint8_t ha; // DevcieValueHA::
bool has_cmd; // true if there is a Console/MQTT command which matches the short_name
int32_t min;
uint32_t max;
DeviceValue(uint8_t device_type,
uint8_t tag,
@@ -416,7 +390,11 @@ class EMSdevice {
uint8_t options_size,
const __FlashStringHelper * short_name,
const __FlashStringHelper * full_name,
uint8_t uom)
uint8_t uom,
uint8_t ha,
bool has_cmd,
int32_t min,
uint32_t max)
: device_type(device_type)
, tag(tag)
, value_p(value_p)
@@ -425,7 +403,11 @@ class EMSdevice {
, options_size(options_size)
, short_name(short_name)
, full_name(full_name)
, uom(uom) {
, uom(uom)
, ha(ha)
, has_cmd(has_cmd)
, min(min)
, max(max) {
}
};
const std::vector<DeviceValue> devicevalues() const;

View File

@@ -423,7 +423,7 @@ void EMSESP::reset_mqtt_ha() {
}
for (const auto & emsdevice : emsdevices) {
emsdevice->ha_config_done(false);
emsdevice->ha_config_clear();
}
dallassensor_.reload();
}
@@ -435,13 +435,13 @@ void EMSESP::publish_device_values(uint8_t device_type) {
JsonObject json = doc.to<JsonObject>();
bool need_publish = false;
bool nested = Mqtt::nested_format();
uint8_t nested = Mqtt::nested_format();
// group by device type
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_type)) {
// if we're using HA and it's not already done, send the config topics first. only do this once
if (Mqtt::ha_enabled() && (!emsdevice->ha_config_done())) {
// if we're using HA, done is checked for each sensor in devices
if (Mqtt::ha_enabled()) {
emsdevice->publish_mqtt_ha_sensor(); // create the configs for each value as a sensor
}
@@ -450,8 +450,8 @@ void EMSESP::publish_device_values(uint8_t device_type) {
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA, false);
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA), json);
json.clear();
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA_WW, false);
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA_WW), json);
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_DEVICE_DATA_WW, false);
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_DEVICE_DATA_WW), json);
need_publish = false;
}
@@ -511,6 +511,10 @@ void EMSESP::publish_other_values() {
}
void EMSESP::publish_sensor_values(const bool time, const bool force) {
if (!dallas_enabled()) {
return;
}
if (dallassensor_.updated_values() || time || force) {
dallassensor_.publish_values(force);
}
@@ -540,7 +544,37 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
doc["value"] = value;
}
Mqtt::publish(F("response"), doc.as<JsonObject>());
Mqtt::publish(F_(response), doc.as<JsonObject>());
}
bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype) {
for (const auto & emsdevice : emsdevices) {
if (emsdevice->device_type() == devicetype) {
return emsdevice->get_value_info(root, cmd, id);
}
}
if (devicetype == DeviceType::DALLASSENSOR) {
uint8_t i = 1;
for (const auto & sensor : EMSESP::sensor_devices()) {
char sensorID[10];
snprintf_P(sensorID, 10, PSTR("sensor%d"), i++);
if ((strcmp(cmd, sensorID) == 0) || (strcmp(cmd, Helpers::toLower(sensor.to_string()).c_str()) == 0)) {
root["name"] = sensor.to_string();
if (Helpers::hasValue(sensor.temperature_c)) {
root["value"] = (float)(sensor.temperature_c) / 10;
}
root["type"] = F_(number);
root["min"] = -55;
root["max"] = 125;
root["unit"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
root["writeable"] = false;
return true;
}
}
}
return false;
}
// search for recognized device_ids : Me, All, otherwise print hex value
@@ -947,14 +981,9 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & json, const int8_t i
return has_value;
}
// send a read request, passing it into to the Tx Service, with offset
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset) {
txservice_.read_request(type_id, dest, offset);
}
// send a read request, passing it into to the Tx Service, with no offset
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest) {
txservice_.read_request(type_id, dest, 0); // 0 = no offset
// send a read request, passing it into to the Tx Service, with optional offset and length
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
txservice_.read_request(type_id, dest, offset, length);
}
// sends write request

View File

@@ -61,7 +61,7 @@
#define EMSESP_JSON_SIZE_MEDIUM_DYN 1024 // for large json docs, using DynamicJsonDocument
#define EMSESP_JSON_SIZE_LARGE_DYN 2048 // for very large json docs, using DynamicJsonDocument
#define EMSESP_JSON_SIZE_XLARGE_DYN 4096 // for very very large json docs, using DynamicJsonDocument
#define EMSESP_JSON_SIZE_XXLARGE_DYN 5120 // for extra very very large json docs, using DynamicJsonDocument
#define EMSESP_JSON_SIZE_XXLARGE_DYN 8192 // for extra very very large json docs, using DynamicJsonDocument
// helpers for callback functions
#define MAKE_PF_CB(__f) [&](std::shared_ptr<const Telegram> t) { __f(t); } // for process function callbacks to register_telegram_type()
@@ -92,8 +92,7 @@ class EMSESP {
static bool process_telegram(std::shared_ptr<const Telegram> telegram);
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
static void send_read_request(const uint16_t type_id, const uint8_t dest);
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset);
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid);
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
@@ -107,6 +106,8 @@ class EMSESP {
static void actual_master_thermostat(const uint8_t device_id);
static uint8_t check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read);
static bool get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype);
static void show_device_values(uuid::console::Shell & shell);
static void show_sensor_values(uuid::console::Shell & shell);
@@ -129,6 +130,10 @@ class EMSESP {
return dallassensor_.fails();
}
static bool dallas_enabled() {
return (dallassensor_.dallas_enabled());
}
enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW, WATCH_UNKNOWN };
static void watch_id(uint16_t id);
static uint16_t watch_id() {

View File

@@ -22,6 +22,7 @@
#include "mqtt.h"
#include "dallassensor.h"
#include "version.h"
#include "default_settings.h"
// forward declarators
// used to bind EMS-ESP functions to external frameworks

View File

@@ -126,9 +126,9 @@ char * Helpers::smallitoa(char * result, const uint16_t value) {
char * Helpers::render_boolean(char * result, bool value) {
uint8_t bool_format_ = Mqtt::bool_format();
if (bool_format_ == BOOL_FORMAT_ONOFF) {
strlcpy(result, value ? "on" : "off", 5);
strlcpy(result, value ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str(), 5);
} else if (bool_format_ == BOOL_FORMAT_ONOFF_CAP) {
strlcpy(result, value ? "ON" : "OFF", 5);
strlcpy(result, value ? uuid::read_flash_string(F_(ON)).c_str() : uuid::read_flash_string(F_(OFF)).c_str(), 5);
} else if (bool_format_ == BOOL_FORMAT_TRUEFALSE) {
strlcpy(result, value ? "true" : "false", 7);
} else {
@@ -457,12 +457,12 @@ bool Helpers::value2bool(const char * v, bool & value) {
std::string bool_str = toLower(v); // convert to lower case
if ((bool_str == "on") || (bool_str == "1") or (bool_str == "true")) {
if ((bool_str == uuid::read_flash_string(F_(on))) || (bool_str == "1") or (bool_str == "true")) {
value = true;
return true; // is a bool
}
if ((bool_str == "off") || (bool_str == "0") or (bool_str == "false")) {
if ((bool_str == uuid::read_flash_string(F_(off))) || (bool_str == "0") or (bool_str == "false")) {
value = false;
return true; // is a bool
}
@@ -471,14 +471,14 @@ bool Helpers::value2bool(const char * v, bool & value) {
}
// checks to see if a string is member of a vector and return the index, also allow true/false for on/off
bool Helpers::value2enum(const char * v, uint8_t & value, const flash_string_vector & strs) {
bool Helpers::value2enum(const char * v, uint8_t & value, const __FlashStringHelper * const * strs) {
if ((v == nullptr) || (strlen(v) == 0)) {
return false;
}
std::string str = toLower(v);
for (value = 0; value < strs.size(); value++) {
std::string str1 = uuid::read_flash_string(strs[value]);
if ((str1 == "off" && str == "false") || (str1 == "on" && str == "true") || (str == str1) || (v[0] == ('0' + value) && v[1] == '\0')) {
for (value = 0; strs[value]; value++) {
std::string str1 = toLower(uuid::read_flash_string(strs[value]));
if ((str1 == uuid::read_flash_string(F_(off)) && str == "false") || (str1 == uuid::read_flash_string(F_(on)) && str == "true") || (str == str1) || (v[0] == ('0' + value) && v[1] == '\0')) {
return true;
}
}

View File

@@ -65,7 +65,7 @@ class Helpers {
static bool value2float(const char * v, float & value);
static bool value2bool(const char * v, bool & value);
static bool value2string(const char * v, std::string & value);
static bool value2enum(const char * v, uint8_t & value, const flash_string_vector & strs);
static bool value2enum(const char * v, uint8_t & value, const __FlashStringHelper * const * strs);
#ifdef EMSESP_STANDALONE
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);

View File

@@ -95,22 +95,25 @@ MAKE_PSTR_WORD(generic)
MAKE_PSTR_WORD(dallassensor)
MAKE_PSTR_WORD(unknown)
// strings
// format strings
MAKE_PSTR(EMSESP, "EMS-ESP")
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat Device ID = %s")
MAKE_PSTR(host_fmt, "Host = %s")
MAKE_PSTR(port_fmt, "Port = %d")
MAKE_PSTR(hostname_fmt, "Hostname = %s")
MAKE_PSTR(board_profile_fmt, "Board Profile = %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(ethernet_option_fmt, "Ethernet option = %d")
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat Device ID: %s")
MAKE_PSTR(host_fmt, "Host: %s")
MAKE_PSTR(port_fmt, "Port: %d")
MAKE_PSTR(hostname_fmt, "Hostname: %s")
MAKE_PSTR(board_profile_fmt, "Board Profile: %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(ethernet_option_fmt, "Ethernet option: %d")
MAKE_PSTR(tx_mode_fmt, "Tx mode: %d")
MAKE_PSTR(bus_id_fmt, "Bus ID: %02X")
MAKE_PSTR(log_level_fmt, "Log level: %s")
//strings
MAKE_PSTR(cmd_optional, "[cmd]")
MAKE_PSTR(ha_optional, "[ha]")
MAKE_PSTR(deep_optional, "[deep]")
MAKE_PSTR(tx_mode_fmt, "Tx mode = %d")
MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
MAKE_PSTR(watchid_optional, "[ID]")
MAKE_PSTR(watch_format_optional, "[off | on | raw | unknown]")
MAKE_PSTR(invalid_watch, "Invalid watch type")
@@ -124,7 +127,6 @@ MAKE_PSTR(typeid_mandatory, "<type ID>")
MAKE_PSTR(deviceid_mandatory, "<device ID>")
MAKE_PSTR(device_type_optional, "[device]")
MAKE_PSTR(invalid_log_level, "Invalid log level")
MAKE_PSTR(log_level_fmt, "Log level = %s")
MAKE_PSTR(log_level_optional, "[level]")
MAKE_PSTR(name_mandatory, "<name>")
MAKE_PSTR(name_optional, "[name]")
@@ -133,16 +135,69 @@ MAKE_PSTR(new_password_prompt2, "Retype new password: ")
MAKE_PSTR(password_prompt, "Password: ")
MAKE_PSTR(unset, "<unset>")
MAKE_PSTR_WORD(2);
MAKE_PSTR_WORD(10);
MAKE_PSTR_WORD(100);
MAKE_PSTR_WORD(60);
MAKE_PSTR_WORD(number)
MAKE_PSTR_WORD(enum)
MAKE_PSTR_WORD(text)
MAKE_PSTR_WORD(2)
MAKE_PSTR_WORD(10)
MAKE_PSTR_WORD(100)
MAKE_PSTR_WORD(60)
MAKE_PSTR_LIST(div2, F_(2))
MAKE_PSTR_LIST(div10, F_(10))
MAKE_PSTR_LIST(div100, F_(100))
MAKE_PSTR_LIST(div60, F_(60))
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
// uom - also used with HA
MAKE_PSTR(percent, "%")
MAKE_PSTR(degrees, "°C")
MAKE_PSTR(kwh, "kWh")
MAKE_PSTR(wh, "Wh")
MAKE_PSTR(bar, "bar")
MAKE_PSTR(minutes, "minutes")
MAKE_PSTR(hours, "hours")
MAKE_PSTR(ua, "uA")
MAKE_PSTR(lmin, "l/min")
MAKE_PSTR(kw, "kW")
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
// use empty string if want to suppress showing tags
MAKE_PSTR(tag_none, "")
MAKE_PSTR(tag_heartbeat, "")
MAKE_PSTR(tag_boiler_data, "")
MAKE_PSTR(tag_device_data_ww, "warm water")
MAKE_PSTR(tag_thermostat_data, "")
MAKE_PSTR(tag_hc1, "hc1")
MAKE_PSTR(tag_hc2, "hc2")
MAKE_PSTR(tag_hc3, "hc3")
MAKE_PSTR(tag_hc4, "hc4")
MAKE_PSTR(tag_wwc1, "wwc1")
MAKE_PSTR(tag_wwc2, "wwc2")
MAKE_PSTR(tag_wwc3, "wwc3")
MAKE_PSTR(tag_wwc4, "wwc4")
MAKE_PSTR(tag_hs1, "hs1")
MAKE_PSTR(tag_hs2, "hs2")
MAKE_PSTR(tag_hs3, "hs3")
MAKE_PSTR(tag_hs4, "hs4")
MAKE_PSTR(tag_hs5, "hs5")
MAKE_PSTR(tag_hs6, "hs6")
MAKE_PSTR(tag_hs7, "hs7")
MAKE_PSTR(tag_hs8, "hs8")
MAKE_PSTR(tag_hs9, "hs9")
MAKE_PSTR(tag_hs10, "hs10")
MAKE_PSTR(tag_hs11, "hs11")
MAKE_PSTR(tag_hs12, "hs12")
MAKE_PSTR(tag_hs13, "hs13")
MAKE_PSTR(tag_hs14, "hs14")
MAKE_PSTR(tag_hs15, "hs15")
MAKE_PSTR(tag_hs16, "hs16")
// MQTT topic names
// MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
MAKE_PSTR(tag_boiler_data_mqtt, "")
MAKE_PSTR(tag_device_data_ww_mqtt, "ww")
// boiler
MAKE_PSTR_WORD(time)
@@ -153,7 +208,7 @@ MAKE_PSTR_WORD(3x3min)
MAKE_PSTR_WORD(4x3min)
MAKE_PSTR_WORD(5x3min)
MAKE_PSTR_WORD(6x3min)
MAKE_PSTR_WORD(continuos);
MAKE_PSTR_WORD(continuos)
MAKE_PSTR(3wayvalve, "3-way valve")
MAKE_PSTR(chargepump, "charge pump")
MAKE_PSTR_WORD(hot)
@@ -163,6 +218,8 @@ MAKE_PSTR_WORD(flow)
MAKE_PSTR_WORD(buffer)
MAKE_PSTR(bufferedflow, "buffered flow")
MAKE_PSTR(layeredbuffer, "layered buffer")
MAKE_PSTR_WORD(maintenance)
MAKE_PSTR_WORD(error)
// boiler lists
MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date))
@@ -170,6 +227,7 @@ MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min
MAKE_PSTR_LIST(enum_charge, F_(3wayvalve), F_(chargepump))
MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent))
MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer))
MAKE_PSTR_LIST(enum_reset, F_(maintenance), F_(error))
// thermostat
MAKE_PSTR_WORD(light)
@@ -182,10 +240,10 @@ MAKE_PSTR_WORD(heat)
MAKE_PSTR_WORD(hold)
MAKE_PSTR_WORD(cool)
MAKE_PSTR_WORD(end)
MAKE_PSTR_WORD(German)
MAKE_PSTR_WORD(Dutch)
MAKE_PSTR_WORD(French)
MAKE_PSTR_WORD(Italian)
MAKE_PSTR_WORD(german)
MAKE_PSTR_WORD(dutch)
MAKE_PSTR_WORD(french)
MAKE_PSTR_WORD(italian)
MAKE_PSTR_WORD(high)
MAKE_PSTR_WORD(low)
MAKE_PSTR_WORD(radiator)
@@ -194,11 +252,12 @@ MAKE_PSTR_WORD(floor)
MAKE_PSTR_WORD(summer)
MAKE_PSTR_WORD(winter)
MAKE_PSTR_WORD(outdoor)
MAKE_PSTR_WORD(MPC)
MAKE_PSTR_WORD(mpc)
MAKE_PSTR_WORD(room)
MAKE_PSTR_WORD(power)
MAKE_PSTR_WORD(constant)
MAKE_PSTR_WORD(simple)
MAKE_PSTR_WORD(optimized)
MAKE_PSTR_WORD(nofrost)
MAKE_PSTR_WORD(comfort)
MAKE_PSTR_WORD(manual)
@@ -206,22 +265,31 @@ MAKE_PSTR_WORD(night)
MAKE_PSTR_WORD(day)
MAKE_PSTR_WORD(holiday)
MAKE_PSTR_WORD(reduce)
MAKE_PSTR_WORD(noreduce)
MAKE_PSTR_WORD(offset)
MAKE_PSTR_WORD(design)
MAKE_PSTR_WORD(tempauto)
MAKE_PSTR_WORD(minflow)
MAKE_PSTR_WORD(maxflow)
MAKE_PSTR_WORD(rc3x)
MAKE_PSTR_WORD(rc20)
MAKE_PSTR(internal_temperature, "internal temperature")
MAKE_PSTR(internal_setpoint, "internal setpoint")
MAKE_PSTR(external_temperature, "external temperature")
MAKE_PSTR(burner_temperature, "burner temperature")
MAKE_PSTR(WW_temperature, "WW temperature")
MAKE_PSTR(ww_temperature, "ww temperature")
MAKE_PSTR(functioning_mode, "functioning mode")
MAKE_PSTR(smoke_temperature, "smoke temperature")
// thermostat lists
MAKE_PSTR_LIST(enum_ibaMainDisplay, F_(internal_temperature), F_(internal_setpoint), F_(external_temperature), F_(burner_temperature), F_(WW_temperature), F_(functioning_mode), F_(time), F_(date), F_(smoke_temperature))
MAKE_PSTR_LIST(enum_ibaLanguage, F_(German), F_(Dutch), F_(French), F_(Italian))
MAKE_PSTR_LIST(enum_ibaMainDisplay, F_(internal_temperature), F_(internal_setpoint), F_(external_temperature), F_(burner_temperature), F_(ww_temperature), F_(functioning_mode), F_(time), F_(date), F_(smoke_temperature))
MAKE_PSTR_LIST(enum_ibaLanguage, F_(german), F_(dutch), F_(french), F_(italian))
MAKE_PSTR_LIST(enum_floordrystatus, F_(off), F_(start), F_(heat), F_(hold), F_(cool), F_(end))
MAKE_PSTR_LIST(enum_ibaBuildingType, F_(blank), F_(light), F_(medium), F_(heavy))
MAKE_PSTR_LIST(enum_ibaBuildingType, F_(blank), F_(light), F_(medium), F_(heavy)) // RC300
MAKE_PSTR_LIST(enum_wwMode, F_(off), F_(low), F_(high), F_(auto), F_(own_prog))
MAKE_PSTR_LIST(enum_wwCircMode, F_(off), F_(on), F_(auto), F_(own_prog))
MAKE_PSTR_LIST(enum_ibaBuildingType2, F_(light), F_(medium), F_(heavy))
MAKE_PSTR_LIST(enum_ibaBuildingType2, F_(light), F_(medium), F_(heavy)) // RC30, RC35
MAKE_PSTR_LIST(enum_wwMode2, F_(off), F_(on), F_(auto))
MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto))
MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor))
@@ -231,15 +299,262 @@ MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto))
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto))
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto))
MAKE_PSTR_LIST(enum_mode4, F_(blank), F_(manual), F_(auto), F_(holiday))
MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off))
MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort))
MAKE_PSTR_LIST(enum_modetype2, F_(day))
MAKE_PSTR_LIST(enum_modetype3, F_(night), F_(day))
MAKE_PSTR_LIST(enum_modetype4, F_(blank), F_(nofrost), F_(eco), F_(heat))
MAKE_PSTR_LIST(enum_modetype5, F_(off), F_(on))
MAKE_PSTR_LIST(enum_reducemode, F_(nofrost), F_(reduce), F_(room), F_(outdoor))
MAKE_PSTR_LIST(enum_controlmode, F_(off), F_(outdoor), F_(simple), F_(MPC), F_(room), F_(power), F_(constant))
MAKE_PSTR_LIST(enum_controlmode, F_(off), F_(optimized), F_(simple), F_(mpc), F_(room), F_(power), F_(constant))
MAKE_PSTR_LIST(enum_controlmode2, F_(outdoor), F_(room))
MAKE_PSTR_LIST(enum_controlmode3, F_(off), F_(room), F_(outdoor), F("room+outdoor"))
MAKE_PSTR_LIST(enum_control, F_(off), F_(rc20), F_(rc3x))
MAKE_PSTR_LIST(enum_hamode, F_(off), F_(heat), F_(auto), F_(heat), F_(off), F_(heat), F_(auto), F_(auto), F_(auto), F_(auto))
/*
* MQTT topics and full text for values and commands
*/
MAKE_PSTR(homeassistant, "homeassistant/")
// id for all devices
// empty full name to prevent being shown in web or console
MAKE_PSTR_LIST(ID, F_(id))
// Boiler
// extra commands, no output
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"))
MAKE_PSTR_LIST(reset, F("reset"))
// single mqtt topics
MAKE_PSTR_WORD(heating_active)
MAKE_PSTR_WORD(tapwater_active)
MAKE_PSTR_WORD(response)
// mqtt, commands and text
MAKE_PSTR_LIST(heatingActive, F("heatingactive"), F("heating active"))
MAKE_PSTR_LIST(tapwaterActive, F("tapwateractive"), F("warm water active"))
MAKE_PSTR_LIST(selFlowTemp, F("selflowtemp"), F("selected flow temperature"))
MAKE_PSTR_LIST(selBurnPow, F("selburnpow"), F("burner selected max power"))
MAKE_PSTR_LIST(heatingPumpMod, F("heatingpumpmod"), F("heating pump modulation"))
MAKE_PSTR_LIST(heatingPump2Mod, F("heatingpump2mod"), F("heating pump 2 modulation"))
MAKE_PSTR_LIST(outdoorTemp, F("outdoortemp"), F("outside temperature"))
MAKE_PSTR_LIST(curFlowTemp, F("curflowtemp"), F("current flow temperature"))
MAKE_PSTR_LIST(retTemp, F("rettemp"), F("return temperature"))
MAKE_PSTR_LIST(switchTemp, F("switchtemp"), F("mixing switch temperature"))
MAKE_PSTR_LIST(sysPress, F("syspress"), F("system pressure"))
MAKE_PSTR_LIST(boilTemp, F("boiltemp"), F("max boiler temperature"))
MAKE_PSTR_LIST(exhaustTemp, F("exhausttemp"), F("exhaust temperature"))
MAKE_PSTR_LIST(burnGas, F("burngas"), F("gas"))
MAKE_PSTR_LIST(flameCurr, F("flamecurr"), F("flame current"))
MAKE_PSTR_LIST(heatingPump, F("heatingpump"), F("heating pump"))
MAKE_PSTR_LIST(fanWork, F("fanwork"), F("fan"))
MAKE_PSTR_LIST(ignWork, F("ignwork"), F("ignition"))
MAKE_PSTR_LIST(heatingActivated, F("heatingactivated"), F("heating activated"))
MAKE_PSTR_LIST(heatingTemp, F("heatingtemp"), F("heating temperature"))
MAKE_PSTR_LIST(pumpModMax, F("pumpmodmax"), F("burner pump max power"))
MAKE_PSTR_LIST(pumpModMin, F("pumpmodmin"), F("burner pump min power"))
MAKE_PSTR_LIST(pumpDelay, F("pumpdelay"), F("pump delay"))
MAKE_PSTR_LIST(burnMinPeriod, F("burnminperiod"), F("burner min period"))
MAKE_PSTR_LIST(burnMinPower, F("burnminpower"), F("burner min power"))
MAKE_PSTR_LIST(burnMaxPower, F("burnmaxpower"), F("burner max power"))
MAKE_PSTR_LIST(boilHystOn, F("boilhyston"), F("hysteresis on temperature"))
MAKE_PSTR_LIST(boilHystOff, F("boilhystoff"), F("hysteresis off temperature"))
MAKE_PSTR_LIST(setFlowTemp, F("setflowtemp"), F("set flow temperature"))
MAKE_PSTR_LIST(setBurnPow, F("setburnpow"), F("burner set power"))
MAKE_PSTR_LIST(curBurnPow, F("curburnpow"), F("burner current power"))
MAKE_PSTR_LIST(burnStarts, F("burnstarts"), F("burner # starts"))
MAKE_PSTR_LIST(burnWorkMin, F("burnworkmin"), F("total burner operating time"))
MAKE_PSTR_LIST(heatWorkMin, F("heatworkmin"), F("total heat operating time"))
MAKE_PSTR_LIST(UBAuptime, F("ubauptime"), F("total UBA operating time"))
MAKE_PSTR_LIST(lastCode, F("lastcode"), F("last error code"))
MAKE_PSTR_LIST(serviceCode, F("servicecode"), F("service code"))
MAKE_PSTR_LIST(serviceCodeNumber, F("servicecodenumber"), F("service code number"))
MAKE_PSTR_LIST(maintenanceMessage, F("maintenancemessage"), F("maintenance message"))
MAKE_PSTR_LIST(maintenanceDate, F("maintenancedate"), F("maintenance set date"))
MAKE_PSTR_LIST(maintenanceType, F_(maintenance), F("maintenance scheduled"))
MAKE_PSTR_LIST(maintenanceTime, F("maintenancetime"), F("maintenance set time"))
MAKE_PSTR_LIST(upTimeControl, F("uptimecontrol"), F("operating time total heat"))
MAKE_PSTR_LIST(upTimeCompHeating, F("uptimecompheating"), F("operating time compressor heating"))
MAKE_PSTR_LIST(upTimeCompCooling, F("uptimecompcooling"), F("operating time compressor cooling"))
MAKE_PSTR_LIST(upTimeCompWw, F("uptimecompww"), F("operating time compressor warm water"))
MAKE_PSTR_LIST(heatingStarts, F("heatingstarts"), F("# heating control starts"))
MAKE_PSTR_LIST(coolingStarts, F("coolingstarts"), F("# cooling control starts"))
MAKE_PSTR_LIST(nrgConsTotal, F("nrgconstotal"), F("total energy consumption"))
MAKE_PSTR_LIST(nrgConsCompTotal, F("nrgconscomptotal"), F("energy consumption compressor total"))
MAKE_PSTR_LIST(nrgConsCompHeating, F("nrgconscompheating"), F("energy consumption compressor heating"))
MAKE_PSTR_LIST(nrgConsCompWw, F("nrgconscompww"), F("energy consumption compressor warm water"))
MAKE_PSTR_LIST(nrgConsCompCooling, F("nrgconscompcooling"), F("energy consumption compressor cooling"))
MAKE_PSTR_LIST(nrgSuppTotal, F("nrgsupptotal"), F("total energy supplied"))
MAKE_PSTR_LIST(nrgSuppHeating, F("nrgsuppheating"), F("total energy supplied heating"))
MAKE_PSTR_LIST(nrgSuppWw, F("nrgsuppww"), F("total energy warm supplied warm water"))
MAKE_PSTR_LIST(nrgSuppCooling, F("nrgsuppcooling"), F("total energy supplied cooling"))
MAKE_PSTR_LIST(auxElecHeatNrgConsTotal, F("auxelecheatnrgconstotal"), F("auxiliary electrical heater energy consumption total"))
MAKE_PSTR_LIST(auxElecHeatNrgConsHeating, F("auxelecheatnrgconsheating"), F("auxiliary electrical heater energy consumption heating"))
MAKE_PSTR_LIST(auxElecHeatNrgConsWW, F("auxelecheatnrgconsww"), F("auxiliary electrical heater energy consumption"))
MAKE_PSTR_LIST(hpPower, F("hppower"), F("heatpump power"))
MAKE_PSTR_LIST(hpTc0, F("hptc0"), F("water temperature condenser inlet (TC0)"))
MAKE_PSTR_LIST(hpTc1, F("hptc1"), F("water temperture condenser output (TC1)"))
MAKE_PSTR_LIST(hpTc3, F("hptc3"), F("condenser temperature (TC3)"))
MAKE_PSTR_LIST(hpTr3, F("hptr3"), F("refrigerant temperature liquid side (condenser output) (TR3)"))
MAKE_PSTR_LIST(hpTr4, F("hptr4"), F("evaporator inlet temperature (TR4)"))
MAKE_PSTR_LIST(hpTr5, F("hptr5"), F("compressor Inlet temperature (TR5)"))
MAKE_PSTR_LIST(hpTr6, F("hptr6"), F("compressor outlet temperature (TR6)"))
MAKE_PSTR_LIST(hpTr7, F("hptr7"), F("refrigerant temperature gas side (condenser input) (TR7)"))
MAKE_PSTR_LIST(hpTl2, F("hptl2"), F("air inlet temperature (TL2)"))
MAKE_PSTR_LIST(hpPl1, F("hppl1"), F("low pressure side temperature (PL1)"))
MAKE_PSTR_LIST(hpPh1, F("hpph1"), F("high pressure side temperature (PH1)"))
MAKE_PSTR_LIST(wWSelTemp, F("wwseltemp"), F("selected temperature"))
MAKE_PSTR_LIST(wWSetTemp, F("wwsettemp"), F("set temperature"))
MAKE_PSTR_LIST(wWType, F("wwtype"), F("type"))
MAKE_PSTR_LIST(wWComfort, F("wwcomfort"), F("comfort"))
MAKE_PSTR_LIST(wWFlowTempOffset, F("wwflowtempoffset"), F("flow temperature offset"))
MAKE_PSTR_LIST(wWMaxPower, F("wwmaxpower"), F("max power"))
MAKE_PSTR_LIST(wWCircPump, F("wwcircpump"), F("circulation pump available"))
MAKE_PSTR_LIST(wWChargeType, F("wwchargetype"), F("charging type"))
MAKE_PSTR_LIST(wWDisinfectionTemp, F("wwdisinfectiontemp"), F("disinfection temperature"))
MAKE_PSTR_LIST(wWCircMode, F("wwcircmode"), F("circulation pump freq"))
MAKE_PSTR_LIST(wWCirc, F("wwcirc"), F("circulation active"))
MAKE_PSTR_LIST(wWCurTemp, F("wwcurtemp"), F("current intern temperature"))
MAKE_PSTR_LIST(wWCurTemp2, F("wwcurtemp2"), F("current extern temperature"))
MAKE_PSTR_LIST(wWCurFlow, F("wwcurflow"), F("current tap water flow"))
MAKE_PSTR_LIST(wWStorageTemp1, F("wwstoragetemp1"), F("storage intern temperature"))
MAKE_PSTR_LIST(wWStorageTemp2, F("wwstoragetemp2"), F("storage extern temperature"))
MAKE_PSTR_LIST(wWActivated, F("wwactivated"), F("activated"))
MAKE_PSTR_LIST(wWOneTime, F("wwonetime"), F("one time charging"))
MAKE_PSTR_LIST(wWDisinfecting, F("wwdisinfecting"), F("disinfecting"))
MAKE_PSTR_LIST(wWCharging, F("wwcharging"), F("charging"))
MAKE_PSTR_LIST(wWRecharging, F("wwrecharging"), F("recharging"))
MAKE_PSTR_LIST(wWTempOK, F("wwtempok"), F("temperature ok"))
MAKE_PSTR_LIST(wWActive, F("wwactive"), F("active"))
MAKE_PSTR_LIST(wWHeat, F("wwheat"), F("heating"))
MAKE_PSTR_LIST(wWSetPumpPower, F("wwsetpumppower"), F("pump set power"))
MAKE_PSTR_LIST(mixerTemp, F("mixertemp"), F("mixer temperature"))
MAKE_PSTR_LIST(tankMiddleTemp, F("tankmiddletemp"), F("tank middle temperature (TS3)"))
MAKE_PSTR_LIST(wWStarts, F("wwstarts"), F("# starts"))
MAKE_PSTR_LIST(wWStarts2, F("wwstarts2"), F("# control starts"))
MAKE_PSTR_LIST(wWWorkM, F("wwworkm"), F("active time"))
// thermostat
// extra commands, no long name, does not show on web
MAKE_PSTR_LIST(switchtime, F("switchtime"))
MAKE_PSTR_LIST(temp, F("temp"))
MAKE_PSTR_LIST(hatemp, F("hatemp"))
MAKE_PSTR_LIST(hamode, F("hamode"))
// mqtt values / commands
MAKE_PSTR_LIST(dateTime, F("datetime"), F("date/time"))
MAKE_PSTR_LIST(errorCode, F("errorcode"), F("error code"))
MAKE_PSTR_LIST(ibaMainDisplay, F("display"), F("display"))
MAKE_PSTR_LIST(ibaLanguage, F("language"), F("language"))
MAKE_PSTR_LIST(ibaClockOffset, F("clockoffset"), F("clock offset"))
MAKE_PSTR_LIST(ibaBuildingType, F("building"), F("building"))
MAKE_PSTR_LIST(ibaCalIntTemperature, F("intoffset"), F("offset internal temperature"))
MAKE_PSTR_LIST(ibaMinExtTemperature, F("minexttemp"), F("min external temperature"))
MAKE_PSTR_LIST(tempsensor1, F("inttemp1"), F("temperature sensor 1"))
MAKE_PSTR_LIST(tempsensor2, F("inttemp2"), F("temperature sensor 2"))
MAKE_PSTR_LIST(dampedoutdoortemp, F("dampedoutdoortemp"), F("damped outdoor temperature"))
MAKE_PSTR_LIST(floordrystatus, F("floordry"), F("floor drying"))
MAKE_PSTR_LIST(dampedoutdoortemp2, F("dampedoutdoortemp"), F("damped outdoor temperature"))
MAKE_PSTR_LIST(floordrytemp, F("floordrytemp"), F("floor drying temperature"))
MAKE_PSTR_LIST(wwMode, F("wwmode"), F("mode"))
MAKE_PSTR_LIST(wwSetTemp, F("wwsettemp"), F("set temperature"))
MAKE_PSTR_LIST(wwSetTempLow, F("wwsettemplow"), F("set temperature low"))
MAKE_PSTR_LIST(wwExtra1, F("wwextra1"), F("circuit 1 extra"))
MAKE_PSTR_LIST(wwExtra2, F("wwextra2"), F("circuit 2 extra"))
MAKE_PSTR_LIST(setpoint_roomTemp, F("seltemp"), F("selected room temperature"))
MAKE_PSTR_LIST(curr_roomTemp, F("currtemp"), F("current room temperature"))
MAKE_PSTR_LIST(mode, F("mode"), F("mode"))
MAKE_PSTR_LIST(modetype, F("modetype"), F("mode type"))
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("day temperature"))
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("heat temperature"))
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("night temperature"))
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco temperature"))
MAKE_PSTR_LIST(manualtemp, F("manualtemp"), F("manual temperature"))
MAKE_PSTR_LIST(tempautotemp, F("tempautotemp"), F("temporary room temperature automode"))
MAKE_PSTR_LIST(comforttemp, F("comforttemp"), F("comfort temperature"))
MAKE_PSTR_LIST(summertemp, F("summertemp"), F("summer temperature"))
MAKE_PSTR_LIST(designtemp, F("designtemp"), F("design temperature"))
MAKE_PSTR_LIST(offsettemp, F("offsettemp"), F("offset temperature"))
MAKE_PSTR_LIST(minflowtemp, F("minflowtemp"), F("min flow temperature"))
MAKE_PSTR_LIST(maxflowtemp, F("maxflowtemp"), F("max flow temperature"))
MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("room influence"))
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("nofrost temperature"))
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("target flow temperature"))
MAKE_PSTR_LIST(heatingtype, F("heatingtype"), F("heating type"))
MAKE_PSTR_LIST(summersetmode, F("summersetmode"), F("summer set mode"))
MAKE_PSTR_LIST(controlmode, F("controlmode"), F("control mode"))
MAKE_PSTR_LIST(control, F("control"), F("control device"))
MAKE_PSTR_LIST(program, F("program"), F("program"))
MAKE_PSTR_LIST(pause, F("pause"), F("pause time"))
MAKE_PSTR_LIST(party, F("party"), F("party time"))
MAKE_PSTR_LIST(holidaytemp, F("holidaytemp"), F("holiday temperature"))
MAKE_PSTR_LIST(summermode, F("summermode"), F("summer mode"))
MAKE_PSTR_LIST(holidaymode, F("holidaymode"), F("holiday mode"))
MAKE_PSTR_LIST(flowtempoffset, F("flowtempoffset"), F("flow temperature offset"))
MAKE_PSTR_LIST(reducemode, F("reducemode"), F("reduce mode"))
MAKE_PSTR_LIST(noreducetemp, F("noreducetemp"), F("no reduce below temperature"))
MAKE_PSTR_LIST(remotetemp, F("remotetemp"), F("room temperature from remote"))
// heatpump
MAKE_PSTR_LIST(airHumidity, F("airhumidity"), F("relative air humidity"))
MAKE_PSTR_LIST(dewTemperature, F("dewtemperature"), F("dew point temperature"))
// mixer
MAKE_PSTR_LIST(flowSetTemp, F("flowsettemp"), F("setpoint flow temperature"))
MAKE_PSTR_LIST(flowTempHc, F("flowtemphc"), F("flow temperature in assigned hc (TC1)"))
MAKE_PSTR_LIST(pumpStatus, F("pumpstatus"), F("pump status in assigned hc (PC1)"))
MAKE_PSTR_LIST(mixerStatus, F("valvestatus"), F("mixing valve actuator in assigned hc (VC1)"))
MAKE_PSTR_LIST(flowTempVf, F("flowtempvf"), F("flow temperature in header (T0/Vf)"))
MAKE_PSTR_LIST(wwPumpStatus, F("pumpstatus"), F("pump status in assigned wwc (PC1)"))
MAKE_PSTR_LIST(wwTempStatus, F("wwtempstatus"), F("temperature switch in assigned wwc (MC1)"))
MAKE_PSTR_LIST(wwTemp, F("wwtemp"), F("current warm water temperature"))
// solar
MAKE_PSTR_LIST(type, F("type"), F("type"))
MAKE_PSTR_LIST(collectorTemp, F("collectortemp"), F("collector temperature (TS1)"))
MAKE_PSTR_LIST(tankBottomTemp, F("tankbottomtemp"), F("tank bottom temperature (TS2)"))
MAKE_PSTR_LIST(tank2BottomTemp, F("tank2bottomtemp"), F("second tank bottom temperature (TS5)"))
MAKE_PSTR_LIST(heatExchangerTemp, F("heatexchangertemp"), F("heat exchanger temperature (TS6)"))
MAKE_PSTR_LIST(tankMaxTemp, F("tankmaxtemp"), F("maximum tank temperature"))
MAKE_PSTR_LIST(solarPumpModulation, F("solarpumpmodulation"), F("pump modulation (PS1)"))
MAKE_PSTR_LIST(cylinderPumpModulation, F("cylinderpumpmodulation"), F("cylinder pump modulation (PS5)"))
MAKE_PSTR_LIST(solarPump, F("solarpump"), F("pump (PS1)"))
MAKE_PSTR_LIST(valveStatus, F("valvestatus"), F("valve status"))
MAKE_PSTR_LIST(tankHeated, F("tankheated"), F("tank heated"))
MAKE_PSTR_LIST(collectorShutdown, F("collectorshutdown"), F("collector shutdown"))
MAKE_PSTR_LIST(pumpWorkTime, F("pumpWorktime"), F("pump working time"))
MAKE_PSTR_LIST(energyLastHour, F("energylasthour"), F("energy last hour"))
MAKE_PSTR_LIST(energyTotal, F("energytotal"), F("energy total"))
MAKE_PSTR_LIST(energyToday, F("energytoday"), F("energy today"))
MAKE_PSTR_LIST(wwTemp1, F("wwtemp1"), F("temperature 1"))
MAKE_PSTR_LIST(wwTemp3, F("wwtemp3"), F("temperature 3"))
MAKE_PSTR_LIST(wwTemp4, F("wwtemp4"), F("temperature 4"))
MAKE_PSTR_LIST(wwTemp5, F("wwtemp5"), F("temperature 5"))
MAKE_PSTR_LIST(wwTemp7, F("wwtemp7"), F("temperature 7"))
MAKE_PSTR_LIST(wwPump, F("wwpump"), F("pump"))
// switch
MAKE_PSTR_LIST(activated, F("activated"), F("activated"))
MAKE_PSTR_LIST(status, F("status"), F("status"))

View File

@@ -39,7 +39,7 @@ uint8_t Mqtt::dallas_format_;
uint8_t Mqtt::bool_format_;
uint8_t Mqtt::ha_climate_format_;
bool Mqtt::ha_enabled_;
bool Mqtt::nested_format_;
uint8_t Mqtt::nested_format_;
uint8_t Mqtt::subscribe_format_;
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
@@ -313,6 +313,7 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
if (mf.mqtt_subfunction_) {
if (!(mf.mqtt_subfunction_)(message)) {
LOG_ERROR(F("MQTT error: invalid payload %s for this topic %s"), message, topic);
Mqtt::publish(F_(response), "invalid");
}
return;
}
@@ -327,14 +328,15 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
}
cmd_only++; // skip the /
int8_t id = -1;
// check for hcx/ prefix
if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') {
id = cmd_only[2] - '0';
cmd_only += 4;
}
// check for hcx/ prefix, commented out, this is now in command::call
// if (cmd_only[0] == 'h' && cmd_only[1] == 'c' && cmd_only[3] == '/') {
// id = cmd_only[2] - '0';
// cmd_only += 4;
// }
// LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s, id = %d"), mf.device_type_, topic, cmd_only, message, id);
if (!Command::call(mf.device_type_, cmd_only, message, id)) {
LOG_ERROR(F("No matching cmd (%s) in topic %s, id %d, or invalid data"), cmd_only, topic, id);
Mqtt::publish(F_(response), "unknown");
}
return;
}
@@ -374,11 +376,18 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len)
char data_str[10];
cmd_known = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as<float>(), 2), n);
} else if (data.isNull()) {
cmd_known = Command::call(mf.device_type_, command, "", n);
DynamicJsonDocument resp(EMSESP_JSON_SIZE_XLARGE_DYN);
JsonObject json = resp.to<JsonObject>();
cmd_known = Command::call(mf.device_type_, command, "", n, json);
if (cmd_known && json.size()) {
Mqtt::publish(F_(response), resp.as<JsonObject>());
return;
}
}
if (!cmd_known) {
LOG_ERROR(F("No matching cmd (%s) or invalid data"), command);
Mqtt::publish(F_(response), "unknown");
}
return;
@@ -672,20 +681,21 @@ void Mqtt::ha_status() {
ids.add("ems-esp");
char topic[MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/system/config"), mqtt_base_.c_str());
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/system/config"), mqtt_base_.c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
// create the sensors
// must match the MQTT payload keys
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("WiFi strength"), EMSdevice::DeviceType::SYSTEM, F("rssi"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime"), EMSdevice::DeviceType::SYSTEM, F("uptime"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime (sec)"), EMSdevice::DeviceType::SYSTEM, F("uptime_sec"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free heap memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Failed MQTT publishes"), EMSdevice::DeviceType::SYSTEM, F("mqttfails"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx Sent"), EMSdevice::DeviceType::SYSTEM, F("rxsent"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx Fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx Reads"), EMSdevice::DeviceType::SYSTEM, F("txread"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx Writes"), EMSdevice::DeviceType::SYSTEM, F("txwrite"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx Fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# MQTT fails"), EMSdevice::DeviceType::SYSTEM, F("mqttfails"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx received"), EMSdevice::DeviceType::SYSTEM, F("rxreceived"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Rx fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx reads"), EMSdevice::DeviceType::SYSTEM, F("txread"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx writes"), EMSdevice::DeviceType::SYSTEM, F("txwrite"));
publish_mqtt_ha_sensor(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("# Tx fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"));
}
// add sub or pub task to the queue.
@@ -788,14 +798,15 @@ void Mqtt::publish_ha(const std::string & topic, const JsonObject & payload) {
payload_text.reserve(measureJson(payload) + 1);
serializeJson(payload, payload_text); // convert json to string
std::string fulltopic = uuid::read_flash_string(F_(homeassistant)) + topic;
#if defined(EMSESP_STANDALONE)
LOG_DEBUG(F("Publishing HA topic=%s, payload=%s"), topic.c_str(), payload_text.c_str());
LOG_DEBUG(F("Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
#elif defined(EMSESP_DEBUG)
LOG_DEBUG(F("[debug] Publishing HA topic=%s, payload=%s"), topic.c_str(), payload_text.c_str());
LOG_DEBUG(F("[debug] Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
#endif
// queue messages if the MQTT connection is not yet established. to ensure we don't miss messages
queue_publish_message(topic, payload_text, true); // with retain true
queue_publish_message(fulltopic, payload_text, true); // with retain true
}
// take top from queue and perform the publish or subscribe action
@@ -809,7 +820,7 @@ void Mqtt::process_queue() {
auto mqtt_message = mqtt_messages_.front();
auto message = mqtt_message.content_;
char topic[MQTT_TOPIC_MAX_SIZE];
if ((strncmp(message->topic.c_str(), "homeassistant/", 13) == 0)) {
if (message->topic.find(uuid::read_flash_string(F_(homeassistant))) == 0) {
// leave topic as it is
strcpy(topic, message->topic.c_str());
} else {
@@ -833,7 +844,7 @@ void Mqtt::process_queue() {
// it will have a real packet ID
if (mqtt_message.packet_id_ > 0) {
#if defined(EMSESP_DEBUG)
LOG_DEBUG(F("[DEBUG] Waitig for QOS-ACK"));
LOG_DEBUG(F("[DEBUG] Waiting for QOS-ACK"));
#endif
return;
}
@@ -892,7 +903,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
bool have_tag = !EMSdevice::tag_to_string(tag).empty() && (device_type != EMSdevice::DeviceType::BOILER); // ignore boiler
bool is_nested = nested_format_ || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested
bool is_nested = (nested_format_ == 1) || (device_type == EMSdevice::DeviceType::BOILER); // boiler never uses nested
// create entity by add the tag if present, seperating with a .
char new_entity[50];
@@ -904,7 +915,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
// device name
char device_name[50];
strncpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
strlcpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
// build unique identifier which will be used in the topic
// and replacing all . with _ as not to break HA
@@ -944,7 +955,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
// look at the device value type
if (type == DeviceValueType::BOOL) {
// binary sensor
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/binary_sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
snprintf_P(topic, sizeof(topic), PSTR("binary_sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
// how to render boolean. HA only accepts String values
char result[10];
@@ -952,7 +963,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
doc[F("payload_off")] = Helpers::render_boolean(result, false);
} else {
// normal HA sensor, not a boolean one
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/%s/config"), mqtt_base_.c_str(), uniq.c_str()); // topic
// unit of measure and map the HA icon
if (uom != DeviceValueUOM::NONE && uom != DeviceValueUOM::PUMP) {
@@ -986,6 +997,7 @@ void Mqtt::publish_mqtt_ha_sensor(uint8_t type, // EMSdevice
ids.add(ha_device);
}
doc.shrinkToFit();
publish_ha(topic, doc.as<JsonObject>());
}
@@ -999,7 +1011,7 @@ const std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
}
// if there is a tag add it
if ((EMSdevice::tag_to_mqtt(tag).empty()) || (nested_format_ && (device_type != EMSdevice::DeviceType::BOILER))) {
if ((EMSdevice::tag_to_mqtt(tag).empty()) || ((nested_format_ == 1) && (device_type != EMSdevice::DeviceType::BOILER))) {
return EMSdevice::device_type_2_device_name(device_type) + "_data";
} else {
return EMSdevice::device_type_2_device_name(device_type) + "_data_" + EMSdevice::tag_to_mqtt(tag);

View File

@@ -158,11 +158,11 @@ class Mqtt {
return bool_format_;
}
static bool nested_format() {
static uint8_t nested_format() {
return nested_format_;
}
static void nested_format(bool nested_format) {
static void nested_format(uint8_t nested_format) {
nested_format_ = nested_format;
}
@@ -274,7 +274,7 @@ class Mqtt {
static uint8_t bool_format_;
static uint8_t ha_climate_format_;
static bool ha_enabled_;
static bool nested_format_;
static uint8_t nested_format_;
static uint8_t subscribe_format_;
};

View File

@@ -56,6 +56,7 @@ void Shower::loop() {
if (!shower_on_ && (time_now - timer_start_) > SHOWER_MIN_DURATION) {
shower_on_ = true;
send_mqtt_stat(true);
publish_values();
LOG_DEBUG(F("[Shower] hot water still running, starting shower timer"));
}
// check if the shower has been on too long
@@ -83,12 +84,19 @@ void Shower::loop() {
}
// reset everything
timer_start_ = 0;
timer_pause_ = 0;
shower_on_ = false;
shower_alert_stop();
timer_start_ = 0;
timer_pause_ = 0;
shower_on_ = false;
doing_cold_shot_ = false;
alert_timer_start_ = 0;
}
}
return;
}
// at this point we're in the shower cold shot (doing_cold_shot_ == true)
// keep repeating until the time is up
if ((time_now - alert_timer_start_) > SHOWER_COLDSHOT_DURATION) {
}
}
@@ -115,7 +123,7 @@ void Shower::send_mqtt_stat(bool state, bool force) {
ids.add("ems-esp");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/binary_sensor/%s/shower_active/config"), Mqtt::base().c_str());
snprintf_P(topic, sizeof(topic), PSTR("binary_sensor/%s/shower_active/config"), Mqtt::base().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
}
}
@@ -124,20 +132,17 @@ void Shower::send_mqtt_stat(bool state, bool force) {
void Shower::shower_alert_stop() {
if (doing_cold_shot_) {
LOG_DEBUG(F("Shower Alert stopped"));
// Boiler::set_tapwarmwater_activated(true);
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true");
doing_cold_shot_ = false;
// showerColdShotStopTimer.detach(); // disable the timer
}
}
// turn off hot water to send a shot of cold
void Shower::shower_alert_start() {
if (shower_alert_) {
LOG_DEBUG(F("Shower Alert started!"));
// Boiler::set_tapwarmwater_activated(false);
doing_cold_shot_ = true;
// start the timer for n seconds which will reset the water back to hot
// showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
LOG_DEBUG(F("Shower Alert started"));
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
doing_cold_shot_ = true;
alert_timer_start_ = uuid::get_uptime(); // timer starts now
}
}

View File

@@ -52,7 +52,7 @@ class Shower {
static constexpr uint32_t SHOWER_PAUSE_TIME = 15000; // in ms. 15 seconds, max time if water is switched off & on during a shower
static constexpr uint32_t SHOWER_MIN_DURATION = 120000; // in ms. 2 minutes, before recognizing its a shower
static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower
static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10; // in seconds. 10 seconds for cold water before turning back hot water
static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10000; // 10 seconds for cold water before turning back hot water
static constexpr uint32_t SHOWER_MAX_DURATION = 420000; // in ms. 7 minutes, before trigger a shot of cold water
void publish_values();
@@ -63,10 +63,13 @@ class Shower {
bool shower_alert_; // true if we want the alert of cold water
bool ha_configdone_ = false; // for HA MQTT Discovery
bool shower_on_;
uint32_t timer_start_; // ms
uint32_t timer_pause_; // ms
uint32_t duration_; // ms
bool doing_cold_shot_; // true if we've just sent a jolt of cold water
uint32_t timer_start_; // ms
uint32_t timer_pause_; // ms
uint32_t duration_; // ms
// cold shot
uint32_t alert_timer_start_; // ms
bool doing_cold_shot_; // true if we've just sent a jolt of cold water
};
} // namespace emsesp

View File

@@ -19,7 +19,7 @@
#include "system.h"
#include "emsesp.h" // for send_raw_telegram() command
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
#include "test/test.h"
#endif
@@ -40,6 +40,7 @@ PButton System::myPButton_;
// e.g. http://ems-esp/api?device=system&cmd=pin&data=1&id=2
bool System::command_pin(const char * value, const int8_t id) {
if (!is_valid_gpio(id)) {
LOG_INFO(F("invalid GPIO number"));
return false;
}
@@ -69,12 +70,30 @@ bool System::command_fetch(const char * value, const int8_t id) {
// mqtt publish
bool System::command_publish(const char * value, const int8_t id) {
std::string ha(10, '\0');
std::string ha(14, '\0');
if (Helpers::value2string(value, ha)) {
if (ha == "ha") {
EMSESP::publish_all(true); // includes HA
LOG_INFO(F("Publishing all data to MQTT, including HA configs"));
return true;
} else if (ha == "boiler") {
EMSESP::publish_device_values(EMSdevice::DeviceType::BOILER);
return true;
} else if (ha == "thermostat") {
EMSESP::publish_device_values(EMSdevice::DeviceType::THERMOSTAT);
return true;
} else if (ha == "solar") {
EMSESP::publish_device_values(EMSdevice::DeviceType::SOLAR);
return true;
} else if (ha == "mixer") {
EMSESP::publish_device_values(EMSdevice::DeviceType::MIXER);
return true;
} else if (ha == "other") {
EMSESP::publish_other_values();
return true;
} else if (ha == "dallassensor") {
EMSESP::publish_sensor_values(true);
return true;
}
}
@@ -123,6 +142,7 @@ void System::syslog_start() {
if (syslog_enabled_) {
#ifndef EMSESP_STANDALONE
syslog_.start();
syslog_.log_level((uuid::log::Level)syslog_level_);
#endif
EMSESP::logger().info(F("Starting Syslog"));
}
@@ -292,7 +312,7 @@ void System::button_OnVLongPress(PButton & b) {
LOG_WARNING(F("Performing factory reset..."));
EMSESP::console_.loop();
#ifdef EMSESP_TEST
#ifdef EMSESP_DEBUG
Test::listDir(LITTLEFS, FS_CONFIG_DIRECTORY, 3);
#endif
@@ -397,23 +417,16 @@ void System::show_mem(const char * note) {
#endif
}
// send periodic MQTT message with system information
void System::send_heartbeat() {
// don't send heartbeat if WiFi or MQTT is not connected
if (!Mqtt::connected()) {
return;
}
// create the json for heartbeat
bool System::heartbeat_json(JsonObject & doc) {
int8_t rssi;
if (!ethernet_connected_) {
rssi = wifi_quality();
if (rssi == -1) {
return;
return false;
}
}
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
uint8_t ems_status = EMSESP::bus_status();
if (ems_status == EMSESP::BUS_STATUS_TX_ERRORS) {
doc["status"] = FJSON("txerror");
@@ -426,15 +439,20 @@ void System::send_heartbeat() {
if (!ethernet_connected_) {
doc["rssi"] = rssi;
}
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
doc["uptime_sec"] = uuid::get_uptime_sec();
doc["mqttfails"] = Mqtt::publish_fails();
doc["rxreceived"] = EMSESP::rxservice_.telegram_count();
doc["rxfails"] = EMSESP::rxservice_.telegram_error_count();
doc["txread"] = EMSESP::txservice_.telegram_read_count();
doc["txwrite"] = EMSESP::txservice_.telegram_write_count();
doc["txfails"] = EMSESP::txservice_.telegram_fail_count();
doc["dallasfails"] = EMSESP::sensor_fails();
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
doc["uptime_sec"] = uuid::get_uptime_sec();
doc["rxreceived"] = EMSESP::rxservice_.telegram_count();
doc["rxfails"] = EMSESP::rxservice_.telegram_error_count();
doc["txread"] = EMSESP::txservice_.telegram_read_count();
doc["txwrite"] = EMSESP::txservice_.telegram_write_count();
doc["txfails"] = EMSESP::txservice_.telegram_fail_count();
if (Mqtt::enabled()) {
doc["mqttfails"] = Mqtt::publish_fails();
}
if (EMSESP::dallas_enabled()) {
doc["dallasfails"] = EMSESP::sensor_fails();
}
#ifndef EMSESP_STANDALONE
doc["freemem"] = ESP.getFreeHeap();
#endif
@@ -443,7 +461,22 @@ void System::send_heartbeat() {
doc["adc"] = analog_;
}
Mqtt::publish(F("heartbeat"), doc.as<JsonObject>()); // send to MQTT with retain off. This will add to MQTT queue.
return (doc.size() > 0);
}
// send periodic MQTT message with system information
void System::send_heartbeat() {
// don't send heartbeat if WiFi or MQTT is not connected
if (!Mqtt::connected()) {
return;
}
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
JsonObject json = doc.to<JsonObject>();
if (heartbeat_json(json)) {
Mqtt::publish(F_(heartbeat), doc.as<JsonObject>()); // send to MQTT with retain off. This will add to MQTT queue.
}
}
// measure and moving average adc
@@ -572,7 +605,7 @@ void System::commands_init() {
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch);
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info);
Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings);
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test);
#endif
}
@@ -635,12 +668,12 @@ void System::show_users(uuid::console::Shell & shell) {
}
void System::show_system(uuid::console::Shell & shell) {
shell.printfln(F("Uptime: %s"), uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str());
shell.printfln(F("Uptime: %s"), uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str());
#ifndef EMSESP_STANDALONE
shell.printfln(F("SDK version: %s"), ESP.getSdkVersion());
shell.printfln(F("CPU frequency: %u MHz"), ESP.getCpuFreqMHz());
shell.printfln(F("Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap());
shell.printfln(F("SDK version: %s"), ESP.getSdkVersion());
shell.printfln(F("CPU frequency: %lu MHz"), ESP.getCpuFreqMHz());
shell.printfln(F("Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap());
shell.println();
switch (WiFi.status()) {
@@ -656,7 +689,7 @@ void System::show_system(uuid::console::Shell & shell) {
shell.printfln(F("WiFi: Network scan complete"));
break;
case WL_CONNECTED: {
case WL_CONNECTED:
shell.printfln(F("WiFi: Connected"));
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
@@ -666,7 +699,7 @@ void System::show_system(uuid::console::Shell & shell) {
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str());
shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str());
} break;
break;
case WL_CONNECT_FAILED:
shell.printfln(F("WiFi: Connection failed"));
@@ -718,150 +751,6 @@ void System::show_system(uuid::console::Shell & shell) {
#endif
}
// console commands to add
void System::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(restart)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.restart(); });
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(wifi), F_(reconnect)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::system_.wifi_reconnect(); });
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(format)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
if (completed) {
EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
if (securitySettings.jwtSecret.equals(password.c_str())) {
EMSESP::system_.format(shell);
} else {
shell.println(F("incorrect password"));
}
});
}
});
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(passwd)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
if (completed) {
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
if (completed) {
if (password1 == password2) {
EMSESP::esp8266React.getSecuritySettingsService()->update(
[&](SecuritySettings & securitySettings) {
securitySettings.jwtSecret = password2.c_str();
return StateUpdateResult::CHANGED;
},
"local");
shell.println(F("su password updated"));
} else {
shell.println(F("Passwords do not match"));
}
}
});
}
});
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::USER, flash_string_vector{F_(show)}, [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.show_system(shell);
shell.println();
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(hostname)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
shell.println("The network connection will be reset...");
Shell::loop_all();
delay(1000); // wait a second
EMSESP::esp8266React.getNetworkSettingsService()->update(
[&](NetworkSettings & networkSettings) {
networkSettings.hostname = arguments.front().c_str();
return StateUpdateResult::CHANGED;
},
"local");
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(wifi), F_(ssid)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
networkSettings.ssid = arguments.front().c_str();
return StateUpdateResult::CHANGED;
});
shell.println("Use `wifi reconnect` to save and apply the new settings");
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(set), F_(wifi), F_(password)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
if (completed) {
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
if (completed) {
if (password1 == password2) {
EMSESP::esp8266React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
networkSettings.password = password2.c_str();
return StateUpdateResult::CHANGED;
});
shell.println("Use `wifi reconnect` to save and apply the new settings");
} else {
shell.println(F("Passwords do not match"));
}
}
});
}
});
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(board_profile)},
flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
std::vector<uint8_t> data; // led, dallas, rx, tx, button
std::string board_profile = Helpers::toUpper(arguments.front());
if (!load_board_profile(data, board_profile)) {
shell.println(F("Invalid board profile"));
return;
}
EMSESP::webSettingsService.update(
[&](WebSettings & settings) {
settings.board_profile = board_profile.c_str();
settings.led_gpio = data[0];
settings.dallas_gpio = data[1];
settings.rx_gpio = data[2];
settings.tx_gpio = data[3];
settings.pbutton_gpio = data[4];
return StateUpdateResult::CHANGED;
},
"local");
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4]);
EMSESP::system_.network_init(true);
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::USER, flash_string_vector{F_(set)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
shell.printfln(F_(hostname_fmt), networkSettings.hostname.isEmpty() ? uuid::read_flash_string(F_(unset)).c_str() : networkSettings.hostname.c_str());
shell.printfln(F_(wifi_ssid_fmt), networkSettings.ssid.isEmpty() ? uuid::read_flash_string(F_(unset)).c_str() : networkSettings.ssid.c_str());
shell.printfln(F_(wifi_password_fmt), networkSettings.ssid.isEmpty() ? F_(unset) : F_(asterisks));
});
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(F_(board_profile_fmt), settings.board_profile.c_str()); });
});
EMSESPShell::commands->add_command(ShellContext::SYSTEM, CommandFlags::ADMIN, flash_string_vector{F_(show), F_(users)}, [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::system_.show_users(shell);
});
// enter the context
Console::enter_custom_context(shell, context);
}
// upgrade from previous versions of EMS-ESP
// returns true if an upgrade was done
bool System::check_upgrade() {
@@ -971,12 +860,20 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
// export status information including some basic settings
// e.g. http://ems-esp/api?device=system&cmd=info
bool System::command_info(const char * value, const int8_t id, JsonObject & json) {
if (id == 0) {
return EMSESP::system_.heartbeat_json(json);
}
JsonObject node;
node = json.createNestedObject("System");
node["version"] = EMSESP_APP_VERSION;
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
#ifndef EMSESP_STANDALONE
node["freemem"] = ESP.getFreeHeap();
#endif
node = json.createNestedObject("Status");
@@ -999,12 +896,16 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
node["#read requests sent"] = EMSESP::txservice_.telegram_read_count();
node["#write requests sent"] = EMSESP::txservice_.telegram_write_count();
node["#incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
node["#tx fails"] = TxService::MAXIMUM_TX_RETRIES, EMSESP::txservice_.telegram_fail_count();
node["#tx fails"] = EMSESP::txservice_.telegram_fail_count();
node["rx line quality"] = EMSESP::rxservice_.quality();
node["tx line quality"] = EMSESP::txservice_.quality();
node["#MQTT publish fails"] = Mqtt::publish_fails();
node["#dallas sensors"] = EMSESP::sensor_devices().size();
node["#dallas fails"] = EMSESP::sensor_fails();
if (Mqtt::enabled()) {
node["#MQTT publish fails"] = Mqtt::publish_fails();
}
if (EMSESP::dallas_enabled()) {
node["#dallas sensors"] = EMSESP::sensor_devices().size();
node["#dallas fails"] = EMSESP::sensor_fails();
}
}
JsonArray devices2 = json.createNestedArray("Devices");
@@ -1020,11 +921,16 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
}
}
}
if (EMSESP::sensor_devices().size()) {
JsonObject obj = devices2.createNestedObject();
obj["type"] = F("Dallassensor");
obj["name"] = F("Dallassensor");
}
return true;
}
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
// run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
bool System::command_test(const char * value, const int8_t id) {
Test::run_test(value, id);
@@ -1037,11 +943,11 @@ bool System::command_test(const char * value, const int8_t id) {
// returns false if profile is not found
bool System::load_board_profile(std::vector<uint8_t> & data, const std::string & board_profile) {
if (board_profile == "S32") {
data = {2, 3, 23, 5, 0}; // BBQKees Gateway S32
data = {2, 18, 23, 5, 0}; // BBQKees Gateway S32
} else if (board_profile == "E32") {
data = {2, 4, 5, 17, 33}; // BBQKees Gateway E32
} else if (board_profile == "MT-ET") {
data = {2, 18, 23, 5, 0}; // MT-ET Live D1 Mini
} else if (board_profile == "MH-ET") {
data = {2, 18, 23, 5, 0}; // MH-ET Live D1 Mini
} else if (board_profile == "NODEMCU") {
data = {2, 18, 23, 5, 0}; // NodeMCU 32S
} else if (board_profile == "LOLIN") {

View File

@@ -48,14 +48,13 @@ class System {
void loop();
// commands
static void console_commands(Shell & shell, unsigned int context);
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_publish(const char * value, const int8_t id);
static bool command_fetch(const char * value, const int8_t id);
static bool command_info(const char * value, const int8_t id, JsonObject & json);
static bool command_settings(const char * value, const int8_t id, JsonObject & json);
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
static bool command_test(const char * value, const int8_t id);
#endif
@@ -68,6 +67,7 @@ class System {
void wifi_tweak();
void syslog_start();
bool check_upgrade();
bool heartbeat_json(JsonObject & json);
void send_heartbeat();
void led_init(bool refresh);
@@ -103,6 +103,9 @@ class System {
return true;
#endif
}
void show_system(uuid::console::Shell & shell);
void wifi_reconnect();
void show_users(uuid::console::Shell & shell);
private:
static uuid::log::Logger logger_;
@@ -135,9 +138,6 @@ class System {
void system_check();
void measure_analog();
void show_system(uuid::console::Shell & shell);
void show_users(uuid::console::Shell & shell);
void wifi_reconnect();
int8_t wifi_quality();
bool system_healthy_ = false;

View File

@@ -419,7 +419,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
}
// build header. src, dest and offset have fixed positions
uint8_t src = data[0];
uint8_t src = ems_bus_id(); // data[0]; we can only send data with own bus_id.
uint8_t dest = data[1];
uint8_t offset = data[3];
@@ -485,11 +485,16 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
}
// send a Tx telegram to request data from an EMS device
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset) {
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
LOG_DEBUG(F("Tx read request to device 0x%02X for type ID 0x%02X"), dest, type_id);
uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
add(Telegram::Operation::TX_READ, dest, type_id, offset, message_data, 1, 0);
// if length set, publish result and set telegram to front
if (length) {
message_data[0] = length;
EMSESP::set_read_id(type_id);
}
add(Telegram::Operation::TX_READ, dest, type_id, offset, message_data, 1, 0, length != 0);
}
// Send a raw telegram to the bus, telegram is a text string of hex values
@@ -541,7 +546,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
reset_retry_count(); // give up
increment_telegram_fail_count(); // another Tx fail
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request."), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), MAXIMUM_TX_RETRIES);
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), MAXIMUM_TX_RETRIES), telegram_last_->to_string().c_str();
return;
}

View File

@@ -264,7 +264,7 @@ class TxService : public EMSbus {
void send();
void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validateid, const bool front = false);
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false);
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0);
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
void send_raw(const char * telegram_data);
void send_poll();
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);

View File

@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
#include "test.h"
@@ -191,7 +191,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
std::string command(20, '\0');
if ((cmd.empty()) || (cmd == "default")) {
command = EMSESP_TEST_DEFAULT;
command = EMSESP_DEBUG_DEFAULT;
} else {
command = cmd;
}
@@ -376,7 +376,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
if (command == "boiler") {
shell.printfln(F("Testing boiler..."));
Mqtt::ha_enabled(false);
Mqtt::nested_format(true);
Mqtt::nested_format(1);
run_test("boiler");
shell.invoke_command("show devices");
@@ -399,6 +399,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
shell.invoke_command("show mqtt");
}
if (command == "shower_alert") {
shell.printfln(F("Testing Shower Alert..."));
run_test("boiler");
// device type, command, data
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
}
if (command == "fr120") {
shell.printfln(F("Testing adding a thermostat FR120..."));
@@ -414,8 +423,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
if (command == "ha") {
shell.printfln(F("Testing HA discovery"));
Mqtt::ha_enabled(true);
// Mqtt::nested_format(true);
Mqtt::nested_format(false);
// Mqtt::nested_format(1);
Mqtt::nested_format(2);
// run_test("boiler");
run_test("thermostat");
@@ -436,11 +445,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
run_test("mixer");
// first with nested
Mqtt::nested_format(true);
Mqtt::nested_format(1);
shell.invoke_command("call system publish");
// then without nested
Mqtt::nested_format(false);
Mqtt::nested_format(2);
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");
}

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(EMSESP_TEST)
#if defined(EMSESP_DEBUG)
#ifndef EMSESP_TEST_H
#define EMSESP_TEST_H
@@ -25,16 +25,18 @@
namespace emsesp {
// #define EMSESP_TEST_DEFAULT "thermostat"
// #define EMSESP_TEST_DEFAULT "solar"
// #define EMSESP_TEST_DEFAULT "mixer"
// #define EMSESP_TEST_DEFAULT "web"
// #define EMSESP_TEST_DEFAULT "general"
// #define EMSESP_TEST_DEFAULT "boiler"
// #define EMSESP_TEST_DEFAULT "mqtt2"
// #define EMSESP_TEST_DEFAULT "mqtt_nested"
// #define EMSESP_TEST_DEFAULT "ha"
#define EMSESP_TEST_DEFAULT "board_profile"
// #define EMSESP_DEBUG_DEFAULT "thermostat"
// #define EMSESP_DEBUG_DEFAULT "solar"
// #define EMSESP_DEBUG_DEFAULT "mixer"
// #define EMSESP_DEBUG_DEFAULT "web"
// #define EMSESP_DEBUG_DEFAULT "general"
// #define EMSESP_DEBUG_DEFAULT "boiler"
// #define EMSESP_DEBUG_DEFAULT "mqtt2"
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
// #define EMSESP_DEBUG_DEFAULT "ha"
// #define EMSESP_DEBUG_DEFAULT "board_profile"
#define EMSESP_DEBUG_DEFAULT "shower_alert"
class Test {
public:

View File

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