mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 17:29:50 +03:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
@@ -71,7 +71,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
||||
id = "-1";
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_DYN);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool ok = false;
|
||||
|
||||
@@ -98,10 +98,10 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
||||
cmd.c_str(),
|
||||
data.c_str(),
|
||||
id.c_str(),
|
||||
ok ? F("OK") : F("Invalid"));
|
||||
ok ? PSTR("OK") : PSTR("Invalid"));
|
||||
EMSESP::logger().info(debug.c_str());
|
||||
if (json.size()) {
|
||||
char buffer2[EMSESP_MAX_JSON_SIZE_LARGE];
|
||||
char buffer2[EMSESP_MAX_JSON_SIZE_DYN];
|
||||
serializeJson(doc, buffer2);
|
||||
EMSESP::logger().info("json (max 255 chars): %s", buffer2);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
|
||||
// if we have returned data in JSON format, send this to the WEB
|
||||
if (json.size()) {
|
||||
doc.shrinkToFit();
|
||||
char buffer[EMSESP_MAX_JSON_SIZE_LARGE];
|
||||
char buffer[EMSESP_MAX_JSON_SIZE_DYN];
|
||||
serializeJsonPretty(doc, buffer);
|
||||
request->send(200, "text/plain", buffer);
|
||||
} else {
|
||||
|
||||
@@ -30,6 +30,7 @@ WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, Securit
|
||||
void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
root["tx_mode"] = settings.tx_mode;
|
||||
root["ems_bus_id"] = settings.ems_bus_id;
|
||||
root["syslog_enabled"] = settings.syslog_enabled;
|
||||
root["syslog_level"] = settings.syslog_level;
|
||||
root["syslog_mark_interval"] = settings.syslog_mark_interval;
|
||||
root["syslog_host"] = settings.syslog_host;
|
||||
@@ -50,6 +51,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) {
|
||||
settings.tx_mode = root["tx_mode"] | EMSESP_DEFAULT_TX_MODE;
|
||||
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
settings.syslog_enabled = root["syslog_enabled"] | EMSESP_DEFAULT_SYSLOG_ENABLED;
|
||||
settings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL;
|
||||
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
|
||||
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
|
||||
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
||||
#define EMSESP_DEFAULT_SYSLOG_LEVEL -1 // OFF
|
||||
#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_MASTER_THERMOSTAT 0 // not set
|
||||
@@ -67,6 +68,7 @@ class WebSettings {
|
||||
uint8_t master_thermostat;
|
||||
bool shower_timer;
|
||||
bool shower_alert;
|
||||
bool syslog_enabled;
|
||||
int8_t syslog_level; // uuid::log::Level
|
||||
uint32_t syslog_mark_interval;
|
||||
String syslog_host;
|
||||
|
||||
@@ -68,7 +68,9 @@ void Command::add(const uint8_t device_type, const uint8_t device_id, const __Fl
|
||||
cmdfunctions_.emplace_back(device_type, cmd, cb, nullptr);
|
||||
|
||||
// see if we need to subscribe
|
||||
Mqtt::register_command(device_type, device_id, cmd, cb);
|
||||
if (Mqtt::enabled()) {
|
||||
Mqtt::register_command(device_type, device_id, cmd, cb);
|
||||
}
|
||||
}
|
||||
|
||||
// add a command to the list, which does return json object as output
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "emsesp.h"
|
||||
#include "version.h"
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
#include "test/test.h"
|
||||
#endif
|
||||
|
||||
@@ -110,28 +110,6 @@ void EMSESPShell::add_console_commands() {
|
||||
// commands->remove_context_commands(ShellContext::MAIN);
|
||||
commands->remove_all_commands();
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(fetch)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("Requesting data from EMS devices"));
|
||||
EMSESP::fetch_device_values();
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(publish)},
|
||||
flash_string_vector{F_(ha_optional)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
EMSESP::publish_all();
|
||||
shell.printfln(F("Published all data to MQTT"));
|
||||
} else {
|
||||
EMSESP::publish_all(true);
|
||||
shell.printfln(F("Published all data to MQTT, including HA configs"));
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show)},
|
||||
@@ -302,13 +280,17 @@ void EMSESPShell::add_console_commands() {
|
||||
"local");
|
||||
});
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(send), F_(telegram)},
|
||||
flash_string_vector{F_(data_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
EMSESP::send_raw_telegram(arguments.front().c_str());
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(set), F_(timeout)},
|
||||
flash_string_vector{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint16_t value = Helpers::atoint(arguments.front().c_str());
|
||||
telnet_.initial_idle_timeout(value * 60);
|
||||
shell.printfln(F("Telnet timout is %d minutes"), value);
|
||||
});
|
||||
#endif
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
@@ -446,11 +428,7 @@ void EMSESPShell::add_console_commands() {
|
||||
return {};
|
||||
});
|
||||
|
||||
/*
|
||||
* add all the submenu contexts...
|
||||
*/
|
||||
|
||||
// System
|
||||
// System context menu
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(system)},
|
||||
@@ -491,33 +469,21 @@ void Console::enter_custom_context(Shell & shell, unsigned int 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_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
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 __attribute__((unused))) {
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::run_test(shell, "default");
|
||||
Test::run_test_shell(shell, "default");
|
||||
} else {
|
||||
Test::run_test(shell, arguments.front());
|
||||
Test::run_test_shell(shell, arguments.front());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F("t")},
|
||||
flash_string_vector{F_(name_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::run_test(shell, "default");
|
||||
} else {
|
||||
Test::run_test(shell, arguments.front());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
context,
|
||||
|
||||
@@ -84,12 +84,12 @@ void DallasSensor::loop() {
|
||||
bus_.reset_search();
|
||||
state_ = State::SCANNING;
|
||||
} else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
|
||||
LOG_ERROR(F("Sensor read timeout"));
|
||||
LOG_WARNING(F("Dallas sensor read timeout"));
|
||||
state_ = State::IDLE;
|
||||
}
|
||||
} else if (state_ == State::SCANNING) {
|
||||
if (time_now - last_activity_ > SCAN_TIMEOUT_MS) {
|
||||
LOG_ERROR(F("Sensor scan timeout"));
|
||||
LOG_ERROR(F("Dallas sensor scan timeout"));
|
||||
state_ = State::IDLE;
|
||||
} else {
|
||||
uint8_t addr[ADDR_LEN] = {0};
|
||||
@@ -129,11 +129,11 @@ void DallasSensor::loop() {
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(F("Unknown sensor %s"), Sensor(addr).to_string().c_str());
|
||||
LOG_ERROR(F("Unknown dallas sensor %s"), Sensor(addr).to_string().c_str());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(F("Invalid sensor %s"), Sensor(addr).to_string().c_str());
|
||||
LOG_ERROR(F("Invalid dallas sensor %s"), Sensor(addr).to_string().c_str());
|
||||
}
|
||||
} else {
|
||||
if (!parasite_) {
|
||||
@@ -309,7 +309,7 @@ bool DallasSensor::export_values(JsonObject & json) {
|
||||
}
|
||||
|
||||
// send all dallas sensor values as a JSON package to MQTT
|
||||
void DallasSensor::publish_values() {
|
||||
void DallasSensor::publish_values(const bool force) {
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
|
||||
if (num_sensors == 0) {
|
||||
@@ -340,7 +340,7 @@ void DallasSensor::publish_values() {
|
||||
// create the HA MQTT config
|
||||
// to e.g. homeassistant/sensor/ems-esp/dallas_28-233D-9497-0C03/config
|
||||
if (mqtt_format_ == Mqtt::Format::HA) {
|
||||
if (!(registered_ha_[sensor_no - 1])) {
|
||||
if (!(registered_ha_[sensor_no - 1]) || force) {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> config;
|
||||
config["dev_cla"] = F("temperature");
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class DallasSensor {
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
void publish_values();
|
||||
void publish_values(const bool force);
|
||||
void reload();
|
||||
bool updated_values();
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
{107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
|
||||
{108, DeviceType::THERMOSTAT, F("FR110"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
|
||||
{111, DeviceType::THERMOSTAT, F("FR10"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{147, DeviceType::THERMOSTAT, F("FR50"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2},
|
||||
{191, DeviceType::THERMOSTAT, F("FR120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
|
||||
{192, DeviceType::THERMOSTAT, F("FW120"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
|
||||
|
||||
@@ -74,11 +74,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
|
||||
// create the config topics for Home Assistant MQTT Discovery
|
||||
// for each of the main elements
|
||||
void Boiler::register_mqtt_ha_config(bool force) {
|
||||
if ((mqtt_ha_config_ && !force)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Boiler::register_mqtt_ha_config() {
|
||||
if (!Mqtt::connected()) {
|
||||
return;
|
||||
}
|
||||
@@ -141,8 +137,17 @@ void Boiler::register_mqtt_ha_config(bool force) {
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(burnWorkMin), this->device_type(), "burnWorkMin", F_(min), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(heatWorkMin), this->device_type(), "heatWorkMin", F_(min), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(UBAuptime), this->device_type(), "UBAuptime", F_(min), nullptr);
|
||||
mqtt_ha_config_ = true; // done
|
||||
}
|
||||
|
||||
// ww
|
||||
// create the config topics for Home Assistant MQTT Discovery
|
||||
// for each of the ww elements
|
||||
void Boiler::register_mqtt_ha_config_ww() {
|
||||
|
||||
if (!Mqtt::connected()) {
|
||||
return;
|
||||
}
|
||||
// ww
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWSelTemp), this->device_type(), "wWSelTemp", F_(degrees), F_(iconcruise));
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWSetTemp), this->device_type(), "wWSetTemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWDisinfectionTemp), this->device_type(), "wWDisinfectionTemp", F_(degrees), F_(icontemperature));
|
||||
@@ -170,14 +175,15 @@ void Boiler::register_mqtt_ha_config(bool force) {
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWStarts), this->device_type(), "wWStarts", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, F_(mqtt_suffix_ww), F_(wWWorkM), this->device_type(), "wWWorkM", F_(min), nullptr);
|
||||
|
||||
mqtt_ha_config_ = true; // done
|
||||
mqtt_ha_config_ww_ = true; // done
|
||||
}
|
||||
|
||||
// send stuff to the Web UI
|
||||
void Boiler::device_info_web(JsonArray & root) {
|
||||
// fetch the values into a JSON document
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
// DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (!export_values_main(json)) {
|
||||
return; // empty
|
||||
}
|
||||
@@ -618,19 +624,29 @@ bool Boiler::export_values_main(JsonObject & json) {
|
||||
void Boiler::publish_values(JsonObject & json, bool force) {
|
||||
// handle HA first
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
|
||||
register_mqtt_ha_config(force);
|
||||
if (force) {
|
||||
mqtt_ha_config_ = false;
|
||||
mqtt_ha_config_ww_ = false;
|
||||
}
|
||||
// register ww in next cycle if both unregistered
|
||||
if (!mqtt_ha_config_) {
|
||||
register_mqtt_ha_config();
|
||||
return;
|
||||
} else if (!mqtt_ha_config_ww_) {
|
||||
register_mqtt_ha_config_ww();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc_main(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
JsonObject json_main = doc_main.to<JsonObject>();
|
||||
if (export_values_main(json_main)) {
|
||||
Mqtt::publish(F("boiler_data"), doc_main.as<JsonObject>());
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
|
||||
JsonObject json_data = doc.to<JsonObject>();
|
||||
if (export_values_main(json_data)) {
|
||||
Mqtt::publish(F("boiler_data"), json_data);
|
||||
}
|
||||
json_data.clear();
|
||||
|
||||
DynamicJsonDocument doc_ww(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
JsonObject json_ww = doc_ww.to<JsonObject>();
|
||||
if (export_values_ww(json_ww)) {
|
||||
Mqtt::publish(F("boiler_data_ww"), doc_ww.as<JsonObject>());
|
||||
if (export_values_ww(json_data)) {
|
||||
Mqtt::publish(F("boiler_data_ww"), json_data);
|
||||
}
|
||||
|
||||
// send out heating and tapwater status
|
||||
@@ -651,13 +667,14 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
EMSdevice::show_values(shell); // for showing the header
|
||||
|
||||
// fetch the values into a JSON document
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
// DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (!export_values_main(json)) {
|
||||
return; // empty
|
||||
}
|
||||
export_values_ww(json); // append ww values
|
||||
doc.shrinkToFit();
|
||||
// doc.shrinkToFit();
|
||||
|
||||
print_value_json(shell, F("heatingActive"), nullptr, F_(heatingActive), nullptr, json);
|
||||
print_value_json(shell, F("tapwaterActive"), nullptr, F_(tapwaterActive), nullptr, json);
|
||||
|
||||
@@ -47,13 +47,15 @@ class Boiler : public EMSdevice {
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void register_mqtt_ha_config(bool force);
|
||||
void register_mqtt_ha_config();
|
||||
void register_mqtt_ha_config_ww();
|
||||
void check_active();
|
||||
bool export_values_main(JsonObject & doc);
|
||||
bool export_values_ww(JsonObject & doc);
|
||||
|
||||
bool changed_ = false;
|
||||
bool mqtt_ha_config_ = false; // HA MQTT Discovery
|
||||
bool changed_ = false;
|
||||
bool mqtt_ha_config_ = false; // HA MQTT Discovery
|
||||
bool mqtt_ha_config_ww_ = false; // HA MQTT Discovery
|
||||
|
||||
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
|
||||
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
|
||||
|
||||
@@ -49,8 +49,8 @@ bool Heatpump::export_values(JsonObject & json) {
|
||||
|
||||
void Heatpump::device_info_web(JsonArray & root) {
|
||||
// fetch the values into a JSON document
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (!export_values(json)) {
|
||||
return; // empty
|
||||
}
|
||||
@@ -64,8 +64,8 @@ void Heatpump::show_values(uuid::console::Shell & shell) {
|
||||
EMSdevice::show_values(shell); // always call this to show header
|
||||
|
||||
// fetch the values into a JSON document
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (!export_values(json)) {
|
||||
return; // empty
|
||||
}
|
||||
@@ -78,27 +78,26 @@ void Heatpump::show_values(uuid::console::Shell & shell) {
|
||||
void Heatpump::publish_values(JsonObject & json, bool force) {
|
||||
// handle HA first
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
|
||||
register_mqtt_ha_config(force);
|
||||
if (!mqtt_ha_config_ || force) {
|
||||
register_mqtt_ha_config();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json_data = doc.to<JsonObject>();
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json_data = doc.to<JsonObject>();
|
||||
if (export_values(json_data)) {
|
||||
Mqtt::publish(F("heatpump_data"), doc.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
|
||||
void Heatpump::register_mqtt_ha_config(bool force) {
|
||||
if ((mqtt_ha_config_ && !force)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Heatpump::register_mqtt_ha_config() {
|
||||
if (!Mqtt::connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the Master device
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
doc["name"] = F_(EMSESP);
|
||||
doc["uniq_id"] = F_(heatpump);
|
||||
doc["ic"] = F_(iconheatpump);
|
||||
|
||||
@@ -45,7 +45,7 @@ class Heatpump : public EMSdevice {
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void register_mqtt_ha_config(bool force);
|
||||
void register_mqtt_ha_config();
|
||||
|
||||
uint8_t airHumidity_ = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t dewTemperature_ = EMS_VALUE_UINT_NOTSET;
|
||||
|
||||
@@ -62,7 +62,7 @@ void Mixer::device_info_web(JsonArray & root) {
|
||||
}
|
||||
|
||||
// fetch the values into a JSON document
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (!export_values_format(Mqtt::Format::SINGLE, json)) {
|
||||
return; // empty
|
||||
@@ -101,7 +101,7 @@ void Mixer::show_values(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
// fetch the values into a JSON document
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (!export_values_format(Mqtt::Format::SINGLE, json)) {
|
||||
return; // empty
|
||||
@@ -128,13 +128,16 @@ void Mixer::show_values(uuid::console::Shell & shell) {
|
||||
void Mixer::publish_values(JsonObject & json, bool force) {
|
||||
// handle HA first
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
|
||||
register_mqtt_ha_config(force);
|
||||
if (!mqtt_ha_config_ || force) {
|
||||
register_mqtt_ha_config();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::SINGLE) {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
if (export_values_format(Mqtt::mqtt_format(), json)) {
|
||||
JsonObject json_data = doc.to<JsonObject>();
|
||||
if (export_values_format(Mqtt::mqtt_format(), json_data)) {
|
||||
char topic[30];
|
||||
if (type() == Type::HC) {
|
||||
snprintf_P(topic, 30, PSTR("mixer_data_hc%d"), hc_);
|
||||
@@ -150,18 +153,13 @@ void Mixer::publish_values(JsonObject & json, bool force) {
|
||||
}
|
||||
|
||||
// publish config topic for HA MQTT Discovery
|
||||
void Mixer::register_mqtt_ha_config(bool force) {
|
||||
if ((mqtt_ha_config_ && !force)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Mixer::register_mqtt_ha_config() {
|
||||
if (!Mqtt::connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we don't have valid values for this HC don't add it ever again
|
||||
if (!Helpers::hasValue(status_)) {
|
||||
mqtt_ha_config_ = true;
|
||||
if (!Helpers::hasValue(pumpStatus_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,7 +192,7 @@ void Mixer::register_mqtt_ha_config(bool force) {
|
||||
|
||||
std::string topic(100, '\0');
|
||||
if (this->type() == Type::HC) {
|
||||
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/mixer_hc%d/config"), hc_);
|
||||
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/mixer_hc%d/config"), hc_);
|
||||
Mqtt::publish_retain(topic, doc.as<JsonObject>(), true); // publish the config payload with retain flag
|
||||
char hc_name[10];
|
||||
snprintf_P(hc_name, sizeof(hc_name), PSTR("hc%d"), hc_);
|
||||
@@ -204,7 +202,7 @@ void Mixer::register_mqtt_ha_config(bool force) {
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(valveStatus), this->device_type(), "valveStatus", nullptr, nullptr);
|
||||
} else {
|
||||
// WWC
|
||||
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/climate/ems-esp/mixer_wwc%d/config"), hc_);
|
||||
snprintf_P(&topic[0], topic.capacity() + 1, PSTR("homeassistant/sensor/ems-esp/mixer_wwc%d/config"), hc_);
|
||||
Mqtt::publish_retain(topic, doc.as<JsonObject>(), true); // publish the config payload with retain flag
|
||||
char wwc_name[10];
|
||||
snprintf_P(wwc_name, sizeof(wwc_name), PSTR("wwc%d"), hc_);
|
||||
|
||||
@@ -46,7 +46,7 @@ class Mixer : public EMSdevice {
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
bool export_values_format(uint8_t mqtt_format, JsonObject & doc);
|
||||
void register_mqtt_ha_config(bool force);
|
||||
void register_mqtt_ha_config();
|
||||
|
||||
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
@@ -49,6 +49,7 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
register_telegram_type(0x036A, F("SM100Status2"), false, [&](std::shared_ptr<const Telegram> t) { process_SM100Status2(t); });
|
||||
register_telegram_type(0x0380, F("SM100CollectorConfig"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100CollectorConfig(t); });
|
||||
register_telegram_type(0x038E, F("SM100Energy"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Energy(t); });
|
||||
register_telegram_type(0x0391, F("SM100Time"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Time(t); });
|
||||
|
||||
register_mqtt_cmd(F("SM100Tank1MaxTemp"), [&](const char * value, const int8_t id) { return set_SM100Tank1MaxTemp(value, id); });
|
||||
}
|
||||
@@ -83,6 +84,7 @@ void Solar::device_info_web(JsonArray & root) {
|
||||
print_value_json(root, F("energyLastHour"), nullptr, F_(energyLastHour), F_(wh), json);
|
||||
print_value_json(root, F("energyToday"), nullptr, F_(energyToday), F_(wh), json);
|
||||
print_value_json(root, F("energyTotal"), nullptr, F_(energyTotal), F_(kwh), json);
|
||||
print_value_json(root, F("pumpWorkMin"), nullptr, F_(pumpWorkMin), F_(min), json);
|
||||
|
||||
if (Helpers::hasValue(pumpWorkMin_)) {
|
||||
JsonObject dataElement = root.createNestedObject();
|
||||
@@ -118,6 +120,7 @@ void Solar::show_values(uuid::console::Shell & shell) {
|
||||
print_value_json(shell, F("energyLastHour"), nullptr, F_(energyLastHour), F_(wh), json);
|
||||
print_value_json(shell, F("energyToday"), nullptr, F_(energyToday), F_(wh), json);
|
||||
print_value_json(shell, F("energyTotal"), nullptr, F_(energyTotal), F_(kwh), json);
|
||||
print_value_json(shell, F("pumpWorkMin"), nullptr, F_(pumpWorkMin), F_(min), json);
|
||||
|
||||
if (Helpers::hasValue(pumpWorkMin_)) {
|
||||
shell.printfln(F(" %s: %d days %d hours %d minutes"),
|
||||
@@ -132,7 +135,10 @@ void Solar::show_values(uuid::console::Shell & shell) {
|
||||
void Solar::publish_values(JsonObject & json, bool force) {
|
||||
// handle HA first
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
|
||||
register_mqtt_ha_config(force);
|
||||
if ((!mqtt_ha_config_ || force)) {
|
||||
register_mqtt_ha_config();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
@@ -147,17 +153,13 @@ void Solar::publish_values(JsonObject & json, bool force) {
|
||||
}
|
||||
|
||||
// publish config topic for HA MQTT Discovery
|
||||
void Solar::register_mqtt_ha_config(bool force) {
|
||||
if ((mqtt_ha_config_ && !force)) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Solar::register_mqtt_ha_config() {
|
||||
if (!Mqtt::connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the Master device
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
doc["name"] = F_(EMSESP);
|
||||
doc["uniq_id"] = F_(solar);
|
||||
doc["ic"] = F_(iconthermostat);
|
||||
@@ -183,10 +185,10 @@ void Solar::register_mqtt_ha_config(bool force) {
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(heatExchangerTemp), this->device_type(), "heatExchangerTemp", F_(degrees), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(solarPumpModulation), this->device_type(), "solarPumpModulation", F_(percent), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(cylinderPumpModulation), this->device_type(), "cylinderPumpModulation", F_(percent), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(pumpWorkMin), this->device_type(), "pumpWorkMin", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyLastHour), this->device_type(), "energyLastHour", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyToday), this->device_type(), "energyToday", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyTotal), this->device_type(), "energyTotal", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(pumpWorkMin), this->device_type(), "pumpWorkMin", F_(min), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyLastHour), this->device_type(), "energyLastHour", F_(wh), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyToday), this->device_type(), "energyToday", F_(wh), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(energyTotal), this->device_type(), "energyTotal", F_(kwh), nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(solarPump), this->device_type(), "solarPump", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(valveStatus), this->device_type(), "valveStatus", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(tankHeated), this->device_type(), "tankHeated", nullptr, nullptr);
|
||||
@@ -450,6 +452,13 @@ void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
|
||||
changed_ |= telegram->read_value(energyTotal_, 8); // total / 10 in kWh
|
||||
}
|
||||
|
||||
/*
|
||||
* SM100Time - type 0x0391 EMS+ for pump working time
|
||||
*/
|
||||
void Solar::process_SM100Time(std::shared_ptr<const Telegram> telegram) {
|
||||
changed_ |= telegram->read_value(pumpWorkMin_, 1, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Junkers ISM1 Solar Module - type 0x0103 EMS+ for energy readings
|
||||
* e.g. B0 00 FF 00 00 03 32 00 00 00 00 13 00 D6 00 00 00 FB D0 F0
|
||||
|
||||
@@ -44,7 +44,7 @@ class Solar : public EMSdevice {
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
void register_mqtt_ha_config(bool force);
|
||||
void register_mqtt_ha_config();
|
||||
|
||||
int16_t collectorTemp_ = EMS_VALUE_SHORT_NOTSET; // TS1: Temperature sensor for collector array 1
|
||||
int16_t tankBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // TS2: Temperature sensor 1 cylinder, bottom (solar thermal system)
|
||||
@@ -106,6 +106,7 @@ class Solar : public EMSdevice {
|
||||
void process_SM100Status2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100CollectorConfig(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Energy(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Time(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwStatus(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
@@ -114,10 +114,12 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
||||
set_typeids = {0x02B9, 0x02BA, 0x02BB, 0x02BC};
|
||||
summer_typeids = {0x02AF, 0x02B0, 0x02B1, 0x02B2};
|
||||
curve_typeids = {0x029B, 0x029C, 0x029D, 0x029E};
|
||||
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
|
||||
register_telegram_type(monitor_typeids[i], F("RC300Monitor"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Monitor(t); });
|
||||
register_telegram_type(set_typeids[i], F("RC300Set"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Set(t); });
|
||||
register_telegram_type(summer_typeids[i], F("RC300Summer"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Summer(t); });
|
||||
register_telegram_type(curve_typeids[i], F("RC300Curves"), false, [&](std::shared_ptr<const Telegram> t) { process_RC300Curve(t); });
|
||||
}
|
||||
register_telegram_type(0x2F5, F("RC300WWmode"), true, [&](std::shared_ptr<const Telegram> t) { process_RC300WWmode(t); });
|
||||
register_telegram_type(0x31B, F("RC300WWtemp"), true, [&](std::shared_ptr<const Telegram> t) { process_RC300WWtemp(t); });
|
||||
@@ -171,12 +173,15 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
for (uint8_t i = 0; i < summer_typeids.size(); i++) {
|
||||
EMSESP::send_read_request(summer_typeids[i], device_id);
|
||||
}
|
||||
} // namespace emsesp
|
||||
for (uint8_t i = 0; i < curve_typeids.size(); i++) {
|
||||
EMSESP::send_read_request(curve_typeids[i], device_id);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for Web UI
|
||||
void Thermostat::device_info_web(JsonArray & root) {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc_main;
|
||||
JsonObject json_main = doc_main.to<JsonObject>();
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc_main;
|
||||
JsonObject json_main = doc_main.to<JsonObject>();
|
||||
if (export_values_main(json_main)) {
|
||||
print_value_json(root, F("time"), nullptr, F_(time), nullptr, json_main);
|
||||
print_value_json(root, F("errorcode"), nullptr, F_(error), nullptr, json_main);
|
||||
@@ -194,6 +199,7 @@ void Thermostat::device_info_web(JsonArray & root) {
|
||||
print_value_json(root, F("wwmode"), nullptr, F_(wwmode), nullptr, json_main);
|
||||
print_value_json(root, F("wwtemp"), nullptr, F_(wwtemp), nullptr, json_main);
|
||||
print_value_json(root, F("wwtemplow"), nullptr, F_(wwtemplow), nullptr, json_main);
|
||||
print_value_json(root, F("wwextra1"), nullptr, F_(wwextra1), nullptr, json_main);
|
||||
print_value_json(root, F("wwcircmode"), nullptr, F_(wwcircmode), nullptr, json_main);
|
||||
}
|
||||
|
||||
@@ -226,6 +232,8 @@ void Thermostat::device_info_web(JsonArray & root) {
|
||||
print_value_json(root, F("designtemp"), FPSTR(prefix_str), F_(designtemp), F_(degrees), json);
|
||||
print_value_json(root, F("roominfluence"), FPSTR(prefix_str), F_(roominfluence), F_(degrees), json);
|
||||
print_value_json(root, F("flowtempoffset"), FPSTR(prefix_str), F_(flowtempoffset), F_(degrees), json);
|
||||
print_value_json(root, F("minflowtemp"), FPSTR(prefix_str), F_(minflowtemp), F_(degrees), json);
|
||||
print_value_json(root, F("maxflowtemp"), FPSTR(prefix_str), F_(maxflowtemp), F_(degrees), json);
|
||||
print_value_json(root, F("summertemp"), FPSTR(prefix_str), F_(summertemp), F_(degrees), json);
|
||||
print_value_json(root, F("summermode"), FPSTR(prefix_str), F_(summermode), F_(degrees), json);
|
||||
print_value_json(root, F("mode"), FPSTR(prefix_str), F_(mode), nullptr, json);
|
||||
@@ -278,6 +286,7 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
|
||||
print_value_json(shell, F("wwmode"), nullptr, F_(wwmode), nullptr, json_main);
|
||||
print_value_json(shell, F("wwtemp"), nullptr, F_(wwtemp), nullptr, json_main);
|
||||
print_value_json(shell, F("wwtemplow"), nullptr, F_(wwtemplow), nullptr, json_main);
|
||||
print_value_json(shell, F("wwextra1"), nullptr, F_(wwextra1), nullptr, json_main);
|
||||
print_value_json(shell, F("wwcircmode"), nullptr, F_(wwcircmode), nullptr, json_main);
|
||||
}
|
||||
|
||||
@@ -310,6 +319,8 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
|
||||
print_value_json(shell, F("designtemp"), F_(2spaces), F_(designtemp), F_(degrees), json);
|
||||
print_value_json(shell, F("roominfluence"), F_(2spaces), F_(roominfluence), F_(degrees), json);
|
||||
print_value_json(shell, F("flowtempoffset"), F_(2spaces), F_(flowtempoffset), F_(degrees), json);
|
||||
print_value_json(shell, F("minflowtemp"), F_(2spaces), F_(minflowtemp), F_(degrees), json);
|
||||
print_value_json(shell, F("maxflowtemp"), F_(2spaces), F_(maxflowtemp), F_(degrees), json);
|
||||
print_value_json(shell, F("summertemp"), F_(2spaces), F_(summertemp), F_(degrees), json);
|
||||
print_value_json(shell, F("summermode"), F_(2spaces), F_(summermode), F_(degrees), json);
|
||||
print_value_json(shell, F("mode"), F_(2spaces), F_(mode), nullptr, json);
|
||||
@@ -325,10 +336,16 @@ void Thermostat::publish_values(JsonObject & json, bool force) {
|
||||
if (EMSESP::actual_master_thermostat() != this->device_id()) {
|
||||
return;
|
||||
}
|
||||
// see if we have already registered this with HA MQTT Discovery, if not send the config
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
|
||||
if (!ha_config(force)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json_data = doc.to<JsonObject>();
|
||||
bool has_data = false;
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
|
||||
JsonObject json_data = doc.to<JsonObject>();
|
||||
bool has_data = false;
|
||||
|
||||
// if MQTT is in single mode send out the main data to the thermostat_data topic
|
||||
has_data |= export_values_main(json_data);
|
||||
@@ -343,10 +360,6 @@ void Thermostat::publish_values(JsonObject & json, bool force) {
|
||||
|
||||
// if we're in HA or CUSTOM, send out the complete topic with all the data
|
||||
if (Mqtt::mqtt_format() != Mqtt::Format::SINGLE && has_data) {
|
||||
// see if we have already registered this with HA MQTT Discovery, if not send the config
|
||||
if (Mqtt::mqtt_format() == Mqtt::Format::HA) {
|
||||
ha_config(force);
|
||||
}
|
||||
Mqtt::publish(F("thermostat_data"), json_data);
|
||||
}
|
||||
}
|
||||
@@ -473,6 +486,16 @@ bool Thermostat::export_values_main(JsonObject & rootThermostat) {
|
||||
rootThermostat["wwtemplow"] = wwTempLow_;
|
||||
}
|
||||
|
||||
// Warm water extra1
|
||||
if (Helpers::hasValue(wwExtra1_)) {
|
||||
rootThermostat["wwextra1"] = wwExtra1_;
|
||||
}
|
||||
|
||||
// Warm water extra2
|
||||
if (Helpers::hasValue(wwExtra2_)) {
|
||||
rootThermostat["wwextra2"] = wwExtra2_;
|
||||
}
|
||||
|
||||
// Warm Water circulation mode
|
||||
if (Helpers::hasValue(wwCircMode_)) {
|
||||
char s[7];
|
||||
@@ -600,6 +623,16 @@ bool Thermostat::export_values_hc(uint8_t mqtt_format, JsonObject & rootThermost
|
||||
dataThermostat["flowtempoffset"] = hc->flowtempoffset;
|
||||
}
|
||||
|
||||
// Min Flow temperature offset
|
||||
if (Helpers::hasValue(hc->minflowtemp)) {
|
||||
dataThermostat["minflowtemp"] = hc->minflowtemp;
|
||||
}
|
||||
|
||||
// Max Flow temperature offset
|
||||
if (Helpers::hasValue(hc->maxflowtemp)) {
|
||||
dataThermostat["maxflowtemp"] = hc->maxflowtemp;
|
||||
}
|
||||
|
||||
// Summer temperature
|
||||
if (Helpers::hasValue(hc->summertemp)) {
|
||||
dataThermostat["summertemp"] = hc->summertemp;
|
||||
@@ -654,23 +687,32 @@ bool Thermostat::export_values_hc(uint8_t mqtt_format, JsonObject & rootThermost
|
||||
}
|
||||
|
||||
// set up HA MQTT Discovery
|
||||
void Thermostat::ha_config(bool force) {
|
||||
bool Thermostat::ha_config(bool force) {
|
||||
if (!Mqtt::connected()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (force) {
|
||||
for (const auto & hc : heating_circuits_) {
|
||||
hc->ha_registered(false);
|
||||
}
|
||||
ha_registered(false);
|
||||
}
|
||||
|
||||
if (force || !ha_registered()) {
|
||||
if (!ha_registered()) {
|
||||
register_mqtt_ha_config();
|
||||
ha_registered(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check to see which heating circuits need publishing
|
||||
for (const auto & hc : heating_circuits_) {
|
||||
if (force || (hc->is_active() && !hc->ha_registered())) {
|
||||
if (hc->is_active() && !hc->ha_registered()) {
|
||||
register_mqtt_ha_config(hc->hc_num());
|
||||
hc->ha_registered(true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the heating circuit object based on the hc number
|
||||
@@ -679,13 +721,15 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const ui
|
||||
// if hc_num is 0 then return the first existing hc in the list
|
||||
if (hc_num == AUTO_HEATING_CIRCUIT) {
|
||||
for (const auto & heating_circuit : heating_circuits_) {
|
||||
return heating_circuit;
|
||||
if (heating_circuit->is_active()) {
|
||||
return heating_circuit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise find a match
|
||||
for (const auto & heating_circuit : heating_circuits_) {
|
||||
if (heating_circuit->hc_num() == hc_num) {
|
||||
if ((heating_circuit->hc_num() == hc_num) && heating_circuit->is_active()) {
|
||||
return heating_circuit;
|
||||
}
|
||||
}
|
||||
@@ -773,10 +817,10 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
||||
return heating_circuits_.back(); // even after sorting, this should still point back to the newly created HC
|
||||
}
|
||||
|
||||
// publish config topic for HA MQTT Discovery
|
||||
// publish config topic for HA MQTT Discovery for main thermostat values
|
||||
// homeassistant/climate/ems-esp/thermostat/config
|
||||
void Thermostat::register_mqtt_ha_config() {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
doc["uniq_id"] = F("thermostat");
|
||||
doc["ic"] = F("mdi:home-thermometer-outline");
|
||||
|
||||
@@ -808,6 +852,7 @@ void Thermostat::register_mqtt_ha_config() {
|
||||
if (model == EMS_DEVICE_FLAG_RC300 || model == EMS_DEVICE_FLAG_RC100) {
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(dampedtemp), this->device_type(), "dampedtemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(building), this->device_type(), "building", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(minexttemp), this->device_type(), "minexttemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(floordry), this->device_type(), "floordry", nullptr, nullptr);
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(floordrytemp), this->device_type(), "floordrytemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(nullptr, nullptr, F_(wwmode), this->device_type(), "wwmode", nullptr, nullptr);
|
||||
@@ -826,18 +871,18 @@ void Thermostat::register_mqtt_ha_config() {
|
||||
}
|
||||
}
|
||||
|
||||
// publish config topic for HA MQTT Discovery
|
||||
// publish config topic for HA MQTT Discovery for each of the heating circuit
|
||||
// e.g. homeassistant/climate/ems-esp/thermostat_hc1/config
|
||||
void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
|
||||
char str1[40];
|
||||
char str1[20];
|
||||
snprintf_P(str1, sizeof(str1), PSTR("Thermostat hc%d"), hc_num);
|
||||
|
||||
char str2[40];
|
||||
char str2[20];
|
||||
snprintf_P(str2, sizeof(str2), PSTR("thermostat_hc%d"), hc_num);
|
||||
|
||||
char str3[40];
|
||||
char str3[25];
|
||||
snprintf_P(str3, sizeof(str3), PSTR("~/%s"), str2);
|
||||
doc["mode_cmd_t"] = str3;
|
||||
doc["temp_cmd_t"] = str3;
|
||||
@@ -909,7 +954,7 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
|
||||
// for each of the heating circuits
|
||||
std::string topic2(100, '\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); });
|
||||
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);
|
||||
@@ -918,6 +963,9 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
|
||||
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(mode), this->device_type(), "mode", nullptr, nullptr);
|
||||
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(seltemp), this->device_type(), "seltemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(currtemp), this->device_type(), "currtemp", F_(degrees), F_(icontemperature));
|
||||
|
||||
uint8_t model = this->model();
|
||||
switch (model) {
|
||||
case EMS_DEVICE_FLAG_RC100:
|
||||
@@ -927,6 +975,12 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(manualtemp), this->device_type(), "manualtemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(comforttemp), this->device_type(), "comforttemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(summertemp), this->device_type(), "summertemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(designtemp), this->device_type(), "designtemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(offsettemp), this->device_type(), "offsettemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(minflowtemp), this->device_type(), "minflowtemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(maxflowtemp), this->device_type(), "maxflowtemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(roominfluence), this->device_type(), "roominfluence", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(nofrosttemp), this->device_type(), "nofrosttemp", F_(degrees), F_(icontemperature));
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_RC20_2:
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(daytemp), this->device_type(), "daytemp", F_(degrees), F_(icontemperature));
|
||||
@@ -944,6 +998,8 @@ void Thermostat::register_mqtt_ha_config(uint8_t hc_num) {
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(summertemp), this->device_type(), "summertemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(nofrosttemp), this->device_type(), "nofrosttemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(roominfluence), this->device_type(), "roominfluence", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(minflowtemp), this->device_type(), "minflowtemp", F_(degrees), F_(icontemperature));
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(maxflowtemp), this->device_type(), "maxflowtemp", F_(degrees), F_(icontemperature));
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_JUNKERS:
|
||||
Mqtt::register_mqtt_ha_sensor(hc_name, nullptr, F_(modetype), this->device_type(), "modetype", nullptr, nullptr);
|
||||
@@ -1094,6 +1150,15 @@ std::string Thermostat::mode_tostring(uint8_t mode) {
|
||||
case HeatingCircuit::Mode::DESIGN:
|
||||
return read_flash_string(F("design"));
|
||||
break;
|
||||
case HeatingCircuit::Mode::MINFLOW:
|
||||
return read_flash_string(F("minflow"));
|
||||
break;
|
||||
case HeatingCircuit::Mode::MAXFLOW:
|
||||
return read_flash_string(F("maxflow"));
|
||||
break;
|
||||
case HeatingCircuit::Mode::ROOMINFLUENCE:
|
||||
return read_flash_string(F("roominfluence"));
|
||||
break;
|
||||
default:
|
||||
case HeatingCircuit::Mode::UNKNOWN:
|
||||
return read_flash_string(F("unknown"));
|
||||
@@ -1268,8 +1333,28 @@ void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
|
||||
// types 0x2AF ff
|
||||
void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||
changed_ |= telegram->read_value(hc->roominfluence, 0);
|
||||
changed_ |= telegram->read_value(hc->offsettemp, 2);
|
||||
changed_ |= telegram->read_value(hc->summertemp, 6);
|
||||
changed_ |= telegram->read_value(hc->summer_setmode, 7);
|
||||
if (hc->heatingtype < 3) {
|
||||
changed_ |= telegram->read_value(hc->designtemp, 4);
|
||||
} else {
|
||||
changed_ |= telegram->read_value(hc->designtemp, 5);
|
||||
}
|
||||
changed_ |= telegram->read_value(hc->minflowtemp, 8);
|
||||
}
|
||||
|
||||
// types 0x29B ff
|
||||
void Thermostat::process_RC300Curve(std::shared_ptr<const Telegram> telegram) {
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||
changed_ |= telegram->read_value(hc->heatingtype, 1); // 1=radiator, 2=convector, 3=floor
|
||||
changed_ |= telegram->read_value(hc->nofrosttemp, 6);
|
||||
if (hc->heatingtype < 3) {
|
||||
changed_ |= telegram->read_value(hc->maxflowtemp, 8);
|
||||
} else {
|
||||
changed_ |= telegram->read_value(hc->maxflowtemp, 7);
|
||||
}
|
||||
}
|
||||
|
||||
// types 0x31B (and 0x31C?)
|
||||
@@ -1306,6 +1391,7 @@ void Thermostat::process_RC300OutdoorTemp(std::shared_ptr<const Telegram> telegr
|
||||
// 0x240 RC300 parameter
|
||||
void Thermostat::process_RC300Settings(std::shared_ptr<const Telegram> telegram) {
|
||||
changed_ |= telegram->read_value(ibaBuildingType_, 9); // 1=light, 2=medium, 3=heavy
|
||||
changed_ |= telegram->read_value(ibaMinExtTemperature_, 10);
|
||||
}
|
||||
|
||||
// 0x267 RC300 floordrying
|
||||
@@ -1370,10 +1456,13 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
|
||||
changed_ |= telegram->read_value(hc->summertemp, 22); // is * 1
|
||||
changed_ |= telegram->read_value(hc->nofrosttemp, 23); // is * 1
|
||||
changed_ |= telegram->read_value(hc->flowtempoffset, 24); // is * 1, only in mixed circuits
|
||||
changed_ |= telegram->read_value(hc->minflowtemp, 16);
|
||||
if (hc->heatingtype == 3) {
|
||||
changed_ |= telegram->read_value(hc->designtemp, 36); // is * 1
|
||||
changed_ |= telegram->read_value(hc->designtemp, 36); // is * 1
|
||||
changed_ |= telegram->read_value(hc->maxflowtemp, 35); // is * 1
|
||||
} else {
|
||||
changed_ |= telegram->read_value(hc->designtemp, 17); // is * 1
|
||||
changed_ |= telegram->read_value(hc->designtemp, 17); // is * 1
|
||||
changed_ |= telegram->read_value(hc->maxflowtemp, 15); // is * 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1564,24 +1653,6 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set roominfluence
|
||||
bool Thermostat::set_roominfluence(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 roominfluence: Heating Circuit %d not found or activated"), hc_num);
|
||||
return false;
|
||||
}
|
||||
int t = 0;
|
||||
if (!Helpers::value2number(value, t)) {
|
||||
LOG_WARNING(F("Set roominfluence: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(F("Setting roominfluence to %d"), t);
|
||||
write_command(set_typeids[hc->hc_num() - 1], 4, t, set_typeids[hc->hc_num() - 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// sets the thermostat ww working mode, where mode is a string, ems and ems+
|
||||
bool Thermostat::set_wwmode(const char * value, const int8_t id) {
|
||||
uint8_t set = 0xFF;
|
||||
@@ -1627,6 +1698,19 @@ bool Thermostat::set_wwtemplow(const char * value, const int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set ww onetime RC300, ems+
|
||||
bool Thermostat::set_wwonetime(const char * value, const int8_t id) {
|
||||
bool b = false;
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
LOG_WARNING(F("Set warm water onetime: Invalid value"));
|
||||
return false;
|
||||
}
|
||||
char s[7];
|
||||
LOG_INFO(F("Setting warm water onetime to %s"), Helpers::render_boolean(s, b));
|
||||
write_command(0x02F5, 11, b ? 0xFF : 0x00, 0x031D);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// sets the thermostat ww circulation working mode, where mode is a string
|
||||
bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
|
||||
@@ -1957,6 +2041,15 @@ bool Thermostat::set_temperature(const float temperature, const std::string & mo
|
||||
if (mode_tostring(HeatingCircuit::Mode::DESIGN) == mode) {
|
||||
return set_temperature(temperature, HeatingCircuit::Mode::DESIGN, hc_num);
|
||||
}
|
||||
if (mode_tostring(HeatingCircuit::Mode::MINFLOW) == mode) {
|
||||
return set_temperature(temperature, HeatingCircuit::Mode::MINFLOW, hc_num);
|
||||
}
|
||||
if (mode_tostring(HeatingCircuit::Mode::MAXFLOW) == mode) {
|
||||
return set_temperature(temperature, HeatingCircuit::Mode::MAXFLOW, hc_num);
|
||||
}
|
||||
if (mode_tostring(HeatingCircuit::Mode::ROOMINFLUENCE) == mode) {
|
||||
return set_temperature(temperature, HeatingCircuit::Mode::ROOMINFLUENCE, hc_num);
|
||||
}
|
||||
|
||||
LOG_WARNING(F("Set temperature: Invalid mode"));
|
||||
return false;
|
||||
@@ -2005,6 +2098,49 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
case HeatingCircuit::Mode::ECO:
|
||||
offset = 0x04; // eco offset
|
||||
break;
|
||||
case HeatingCircuit::Mode::OFFSET:
|
||||
offset = 2;
|
||||
set_typeid = summer_typeids[hc->hc_num() - 1];
|
||||
validate_typeid = set_typeid;
|
||||
break;
|
||||
case HeatingCircuit::Mode::DESIGN:
|
||||
set_typeid = summer_typeids[hc->hc_num() - 1];
|
||||
validate_typeid = set_typeid;
|
||||
if (hc->heatingtype == 3) {
|
||||
offset = 5;
|
||||
} else {
|
||||
offset = 4;
|
||||
}
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::MINFLOW:
|
||||
set_typeid = summer_typeids[hc->hc_num() - 1];
|
||||
validate_typeid = set_typeid;
|
||||
offset = 8;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::MAXFLOW:
|
||||
set_typeid = curve_typeids[hc->hc_num() - 1];
|
||||
validate_typeid = set_typeid;
|
||||
if (hc->heatingtype == 3) {
|
||||
offset = 7;
|
||||
} else {
|
||||
offset = 8;
|
||||
}
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::NOFROST:
|
||||
set_typeid = curve_typeids[hc->hc_num() - 1];
|
||||
validate_typeid = set_typeid;
|
||||
offset = 6;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::ROOMINFLUENCE:
|
||||
set_typeid = summer_typeids[hc->hc_num() - 1];
|
||||
validate_typeid = set_typeid;
|
||||
offset = 0;
|
||||
factor = 1;
|
||||
break;
|
||||
default:
|
||||
case HeatingCircuit::Mode::AUTO:
|
||||
uint8_t mode_ = hc->get_mode(this->flags());
|
||||
@@ -2067,6 +2203,22 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
offset = EMS_OFFSET_RC35Set_temp_nofrost;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::ROOMINFLUENCE:
|
||||
offset = 4;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::MINFLOW:
|
||||
offset = 16;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::MAXFLOW:
|
||||
if (hc->heatingtype == 3) {
|
||||
offset = 35;
|
||||
} else {
|
||||
offset = 15;
|
||||
}
|
||||
factor = 1;
|
||||
break;
|
||||
default:
|
||||
case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code
|
||||
validate_typeid = monitor_typeids[hc->hc_num() - 1]; //get setpoint roomtemp back
|
||||
@@ -2226,6 +2378,18 @@ bool Thermostat::set_flowtempoffset(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::FLOWOFFSET);
|
||||
}
|
||||
|
||||
bool Thermostat::set_maxflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MAXFLOW);
|
||||
}
|
||||
|
||||
bool Thermostat::set_minflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -2250,7 +2414,15 @@ void Thermostat::add_commands() {
|
||||
register_mqtt_cmd(F("wwmode"), [&](const char * value, const int8_t id) { return set_wwmode(value, id); });
|
||||
register_mqtt_cmd(F("wwtemp"), [&](const char * value, const int8_t id) { return set_wwtemp(value, id); });
|
||||
register_mqtt_cmd(F("wwtemplow"), [&](const char * value, const int8_t id) { return set_wwtemplow(value, id); });
|
||||
register_mqtt_cmd(F("wwonetime"), [&](const char * value, const int8_t id) { return set_wwonetime(value, id); });
|
||||
register_mqtt_cmd(F("building"), [&](const char * value, const int8_t id) { return set_building(value, id); });
|
||||
register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { return set_nofrosttemp(value, id); });
|
||||
register_mqtt_cmd(F("designtemp"), [&](const char * value, const int8_t id) { return set_designtemp(value, id); });
|
||||
register_mqtt_cmd(F("offsettemp"), [&](const char * value, const int8_t id) { return set_offsettemp(value, id); });
|
||||
register_mqtt_cmd(F("minflowtemp"), [&](const char * value, const int8_t id) { return set_minflowtemp(value, id); });
|
||||
register_mqtt_cmd(F("maxflowtemp"), [&](const char * value, const int8_t id) { return set_maxflowtemp(value, id); });
|
||||
register_mqtt_cmd(F("minexttemp"), [&](const char * value, const int8_t id) { return set_minexttemp(value, id); });
|
||||
register_mqtt_cmd(F("roominfluence"), [&](const char * value, const int8_t id) { return set_roominfluence(value, id); });
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_RC20_2:
|
||||
register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { return set_nighttemp(value, id); });
|
||||
@@ -2280,6 +2452,8 @@ void Thermostat::add_commands() {
|
||||
register_mqtt_cmd(F("wwcircmode"), [&](const char * value, const int8_t id) { return set_wwcircmode(value, id); });
|
||||
register_mqtt_cmd(F("roominfluence"), [&](const char * value, const int8_t id) { return set_roominfluence(value, id); });
|
||||
register_mqtt_cmd(F("flowtempoffset"), [&](const char * value, const int8_t id) { return set_flowtempoffset(value, id); });
|
||||
register_mqtt_cmd(F("minflowtemp"), [&](const char * value, const int8_t id) { return set_minflowtemp(value, id); });
|
||||
register_mqtt_cmd(F("maxflowtemp"), [&](const char * value, const int8_t id) { return set_maxflowtemp(value, id); });
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_JUNKERS:
|
||||
register_mqtt_cmd(F("nofrosttemp"), [&](const char * value, const int8_t id) { return set_nofrosttemp(value, id); });
|
||||
@@ -2291,5 +2465,4 @@ void Thermostat::add_commands() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -58,13 +58,15 @@ class Thermostat : public EMSdevice {
|
||||
uint8_t heatingtype = EMS_VALUE_UINT_NOTSET; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
|
||||
uint8_t targetflowtemp = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t summertemp = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t nofrosttemp = EMS_VALUE_UINT_NOTSET;
|
||||
int8_t nofrosttemp = EMS_VALUE_INT_NOTSET; // signed -20°C to +10°C
|
||||
uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heating curve design temp at MinExtTemp
|
||||
int8_t offsettemp = EMS_VALUE_INT_NOTSET; // heating curve offest temp at roomtemp signed!
|
||||
uint8_t manualtemp = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t summer_setmode = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t roominfluence = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t flowtempoffset = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t minflowtemp = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t maxflowtemp = EMS_VALUE_UINT_NOTSET;
|
||||
|
||||
uint8_t hc_num() const {
|
||||
return hc_num_;
|
||||
@@ -86,7 +88,7 @@ class Thermostat : public EMSdevice {
|
||||
uint8_t get_mode(uint8_t flags) const;
|
||||
uint8_t get_mode_type(uint8_t flags) const;
|
||||
|
||||
enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN, SUMMER, FLOWOFFSET };
|
||||
enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN, SUMMER, FLOWOFFSET, MINFLOW, MAXFLOW, ROOMINFLUENCE };
|
||||
|
||||
// for sorting based on hc number
|
||||
friend inline bool operator<(const std::shared_ptr<HeatingCircuit> & lhs, const std::shared_ptr<HeatingCircuit> & rhs) {
|
||||
@@ -131,6 +133,7 @@ class Thermostat : public EMSdevice {
|
||||
std::vector<uint16_t> set_typeids;
|
||||
std::vector<uint16_t> timer_typeids;
|
||||
std::vector<uint16_t> summer_typeids;
|
||||
std::vector<uint16_t> curve_typeids;
|
||||
|
||||
std::string datetime_; // date and time stamp
|
||||
std::string errorCode_; // code from 0xA2 as string i.e. "A22(816)"
|
||||
@@ -245,7 +248,7 @@ class Thermostat : public EMSdevice {
|
||||
|
||||
void register_mqtt_ha_config();
|
||||
void register_mqtt_ha_config(uint8_t hc_num);
|
||||
void ha_config(bool force = false);
|
||||
bool ha_config(bool force = false);
|
||||
bool thermostat_ha_cmd(const char * message, uint8_t hc_num);
|
||||
|
||||
void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -273,6 +276,7 @@ class Thermostat : public EMSdevice {
|
||||
void process_RC300OutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC300Settings(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC300Floordry(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC300Curve(std::shared_ptr<const Telegram> telegram);
|
||||
void process_JunkersMonitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_JunkersSet(std::shared_ptr<const Telegram> telegram);
|
||||
void process_JunkersSet2(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -308,11 +312,14 @@ class Thermostat : public EMSdevice {
|
||||
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);
|
||||
bool set_minflowtemp(const char * value, const int8_t id);
|
||||
bool set_maxflowtemp(const char * value, const int8_t id);
|
||||
|
||||
// set functions - these don't use the id/hc, the parameters are ignored
|
||||
bool set_wwmode(const char * value, const int8_t id);
|
||||
bool set_wwtemp(const char * value, const int8_t id);
|
||||
bool set_wwtemplow(const char * value, const int8_t id);
|
||||
bool set_wwonetime(const char * value, const int8_t id);
|
||||
bool set_wwcircmode(const char * value, const int8_t id);
|
||||
bool set_datetime(const char * value, const int8_t id);
|
||||
bool set_minexttemp(const char * value, const int8_t id);
|
||||
|
||||
@@ -284,8 +284,7 @@ class EMSdevice {
|
||||
uint16_t telegram_type_id_; // it's type_id
|
||||
const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
|
||||
process_function_p process_function_;
|
||||
process_function_p process_function_;
|
||||
|
||||
TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function)
|
||||
: telegram_type_id_(telegram_type_id)
|
||||
|
||||
@@ -63,6 +63,7 @@ bool EMSESP::read_next_ = false;
|
||||
uint16_t EMSESP::publish_id_ = 0;
|
||||
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
|
||||
uint32_t EMSESP::last_fetch_ = 0;
|
||||
uint8_t EMSESP::publish_all_idx_ = 0;
|
||||
uint8_t EMSESP::unique_id_count_ = 0;
|
||||
|
||||
// for a specific EMS device go and request data values
|
||||
@@ -215,10 +216,9 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
shell.printfln(F(" #tx fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_fail_count());
|
||||
shell.printfln(F(" Rx line quality: %d%%"), rxservice_.quality());
|
||||
shell.printfln(F(" Tx line quality: %d%%"), txservice_.quality());
|
||||
shell.println();
|
||||
}
|
||||
|
||||
shell.println();
|
||||
|
||||
// Rx queue
|
||||
auto rx_telegrams = rxservice_.queue();
|
||||
if (rx_telegrams.empty()) {
|
||||
@@ -290,29 +290,74 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
|
||||
// MQTT publish everything, immediately
|
||||
void EMSESP::publish_all(bool force) {
|
||||
if (force) {
|
||||
publish_all_idx_ = 1;
|
||||
return;
|
||||
}
|
||||
if (Mqtt::connected()) {
|
||||
publish_device_values(EMSdevice::DeviceType::BOILER, force);
|
||||
publish_device_values(EMSdevice::DeviceType::THERMOSTAT, force);
|
||||
publish_device_values(EMSdevice::DeviceType::SOLAR, force);
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER, force);
|
||||
publish_device_values(EMSdevice::DeviceType::BOILER, false);
|
||||
publish_device_values(EMSdevice::DeviceType::THERMOSTAT, false);
|
||||
publish_device_values(EMSdevice::DeviceType::SOLAR, false);
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER, false);
|
||||
publish_other_values();
|
||||
publish_sensor_values(true);
|
||||
publish_sensor_values(true, false);
|
||||
system_.send_heartbeat();
|
||||
}
|
||||
}
|
||||
|
||||
// on command "publish HA" loop and wait between devices for publishing all sensors
|
||||
void EMSESP::publish_all_loop() {
|
||||
static uint32_t last = 0;
|
||||
if (!Mqtt::connected() || !publish_all_idx_) {
|
||||
return;
|
||||
}
|
||||
// every HA-sensor takes 20 ms, wait ~2 sec to finish (boiler have ~70 sensors)
|
||||
if ((uuid::get_uptime() - last < 2000)) {
|
||||
return;
|
||||
}
|
||||
last = uuid::get_uptime();
|
||||
switch (publish_all_idx_++) {
|
||||
case 1:
|
||||
publish_device_values(EMSdevice::DeviceType::BOILER, true);
|
||||
break;
|
||||
case 2:
|
||||
publish_device_values(EMSdevice::DeviceType::THERMOSTAT, true);
|
||||
break;
|
||||
case 3:
|
||||
publish_device_values(EMSdevice::DeviceType::SOLAR, true);
|
||||
break;
|
||||
case 4:
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER, true);
|
||||
break;
|
||||
case 5:
|
||||
publish_other_values();
|
||||
break;
|
||||
case 6:
|
||||
publish_sensor_values(true, true);
|
||||
break;
|
||||
case 7:
|
||||
system_.send_heartbeat();
|
||||
break;
|
||||
default:
|
||||
// all finished
|
||||
publish_all_idx_ = 0;
|
||||
last = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// create json doc for the devices values and add to MQTT publish queue
|
||||
// special case for Mixer units, since we want to bundle all devices together into one payload
|
||||
void EMSESP::publish_device_values(uint8_t device_type, bool force) {
|
||||
if (device_type == EMSdevice::DeviceType::MIXER && Mqtt::mqtt_format() != Mqtt::Format::SINGLE) {
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
// DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_LARGE);
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
emsdevice->publish_values(json, force);
|
||||
}
|
||||
}
|
||||
doc.shrinkToFit();
|
||||
// doc.shrinkToFit();
|
||||
Mqtt::publish("mixer_data", doc.as<JsonObject>());
|
||||
return;
|
||||
}
|
||||
@@ -335,9 +380,9 @@ void EMSESP::publish_other_values() {
|
||||
}
|
||||
}
|
||||
|
||||
void EMSESP::publish_sensor_values(const bool force) {
|
||||
if (dallassensor_.updated_values() || force) {
|
||||
dallassensor_.publish_values();
|
||||
void EMSESP::publish_sensor_values(const bool time, const bool force) {
|
||||
if (dallassensor_.updated_values() || time || force) {
|
||||
dallassensor_.publish_values(force);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +694,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
// shell.printf(F("[factory ID: %d] "), device_class.first);
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
|
||||
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
|
||||
shell.printf(F("(%d) %s: %s"), emsdevice->unique_id(), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
|
||||
if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->device_id() == actual_master_thermostat())) {
|
||||
shell.printf(F(" ** master device **"));
|
||||
}
|
||||
@@ -722,17 +767,20 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
std::string name = uuid::read_flash_string(device_p->name);
|
||||
emsdevices.push_back(EMSFactory::add(device_p->device_type, device_id, device_p->product_id, version, name, device_p->flags, brand));
|
||||
emsdevices.back()->unique_id(++unique_id_count_);
|
||||
auto name = uuid::read_flash_string(device_p->name);
|
||||
auto device_type = device_p->device_type;
|
||||
auto flags = device_p->flags;
|
||||
LOG_DEBUG(F("Adding new device %s (device ID 0x%02X, product ID %d, version %s)"), name.c_str(), device_id, product_id, version.c_str());
|
||||
emsdevices.push_back(EMSFactory::add(device_type, device_id, product_id, version, name, flags, brand));
|
||||
emsdevices.back()->unique_id(++unique_id_count_);
|
||||
|
||||
fetch_device_values(device_id); // go and fetch its data
|
||||
|
||||
// add info command, but not for all devices
|
||||
uint8_t device_type = device_p->device_type;
|
||||
if ((device_type == DeviceType::CONNECT) || (device_type == DeviceType::CONTROLLER) || (device_type == DeviceType::GATEWAY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Command::add_with_json(device_type, F_(info), [device_type](const char * value, const int8_t id, JsonObject & json) {
|
||||
return command_info(device_type, json);
|
||||
});
|
||||
@@ -934,11 +982,12 @@ void EMSESP::loop() {
|
||||
}
|
||||
|
||||
system_.loop(); // does LED and checks system health, and syslog service
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
shower_.loop(); // check for shower on/off
|
||||
dallassensor_.loop(); // this will also send out via MQTT
|
||||
mqtt_.loop(); // sends out anything in the queue via MQTT
|
||||
console_.loop(); // telnet/serial console
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
publish_all_loop();
|
||||
mqtt_.loop(); // sends out anything in the queue via MQTT
|
||||
console_.loop(); // telnet/serial console
|
||||
|
||||
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class EMSESP {
|
||||
|
||||
static void publish_device_values(uint8_t device_type, bool force = false);
|
||||
static void publish_other_values();
|
||||
static void publish_sensor_values(const bool force = false);
|
||||
static void publish_sensor_values(const bool time, const bool force = false);
|
||||
static void publish_all(bool force = false);
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
@@ -184,6 +184,7 @@ class EMSESP {
|
||||
static void process_UBADevices(std::shared_ptr<const Telegram> telegram);
|
||||
static void process_version(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_response(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_all_loop();
|
||||
|
||||
static bool command_info(uint8_t device_type, JsonObject & json);
|
||||
|
||||
@@ -206,6 +207,7 @@ class EMSESP {
|
||||
static bool read_next_;
|
||||
static uint16_t publish_id_;
|
||||
static bool tap_water_active_;
|
||||
static uint8_t publish_all_idx_;
|
||||
static uint8_t unique_id_count_;
|
||||
};
|
||||
|
||||
|
||||
@@ -66,10 +66,7 @@ MAKE_PSTR_WORD(publish)
|
||||
MAKE_PSTR_WORD(bar)
|
||||
MAKE_PSTR_WORD(min)
|
||||
MAKE_PSTR_WORD(uA)
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
MAKE_PSTR_WORD(test)
|
||||
#endif
|
||||
MAKE_PSTR_WORD(timeout)
|
||||
|
||||
// for commands
|
||||
MAKE_PSTR_WORD(call)
|
||||
@@ -81,6 +78,7 @@ MAKE_PSTR_WORD(command)
|
||||
MAKE_PSTR_WORD(commands)
|
||||
MAKE_PSTR_WORD(info)
|
||||
MAKE_PSTR_WORD(report)
|
||||
MAKE_PSTR_WORD(test)
|
||||
|
||||
// devices
|
||||
MAKE_PSTR_WORD(boiler)
|
||||
@@ -116,7 +114,7 @@ MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
|
||||
MAKE_PSTR(watchid_optional, "[ID]")
|
||||
MAKE_PSTR(watch_format_optional, "[off | on | raw]")
|
||||
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
||||
MAKE_PSTR(data_mandatory, "<\"XX XX ...\">")
|
||||
MAKE_PSTR(data_mandatory, "\"XX XX ...\"")
|
||||
MAKE_PSTR(percent, "%")
|
||||
MAKE_PSTR(degrees, "°C")
|
||||
MAKE_PSTR(asterisks, "********")
|
||||
@@ -246,6 +244,8 @@ MAKE_PSTR(floordrytemp, "Floordrying temperature")
|
||||
MAKE_PSTR(wwmode, "Warm water mode")
|
||||
MAKE_PSTR(wwtemp, "Warm water high temperature")
|
||||
MAKE_PSTR(wwtemplow, "Warm water low temperature")
|
||||
MAKE_PSTR(wwextra1, "Warm water circuit 1 extra")
|
||||
MAKE_PSTR(wwextra2, "Warm water circuit 2 extra")
|
||||
MAKE_PSTR(wwcircmode, "Warm water circulation mode")
|
||||
|
||||
// thermostat - per heating circuit
|
||||
@@ -267,6 +267,8 @@ MAKE_PSTR(summertemp, "Summer temperature")
|
||||
MAKE_PSTR(summermode, "Summer mode")
|
||||
MAKE_PSTR(roominfluence, "Room influence")
|
||||
MAKE_PSTR(flowtempoffset, "Flow temperature offset")
|
||||
MAKE_PSTR(minflowtemp, "Min. flow temperature")
|
||||
MAKE_PSTR(maxflowtemp, "Max. flow temperature")
|
||||
MAKE_PSTR(mode, "Mode")
|
||||
MAKE_PSTR(modetype, "Mode type")
|
||||
|
||||
|
||||
80
src/mqtt.cpp
80
src/mqtt.cpp
@@ -39,9 +39,9 @@ bool Mqtt::mqtt_enabled_;
|
||||
|
||||
std::vector<Mqtt::MQTTSubFunction> Mqtt::mqtt_subfunctions_;
|
||||
|
||||
uint16_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
|
||||
uint16_t Mqtt::mqtt_message_id_ = 0;
|
||||
uint16_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
// size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
|
||||
uint16_t Mqtt::mqtt_message_id_ = 0;
|
||||
std::list<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer
|
||||
|
||||
@@ -181,7 +181,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell.printfln(F("MQTT queue (%d messages):"), mqtt_messages_.size());
|
||||
shell.printfln(F("MQTT queue (%d/%d messages):"), mqtt_messages_.size(), MAX_MQTT_MESSAGES);
|
||||
|
||||
for (const auto & message : mqtt_messages_) {
|
||||
auto content = message.content_;
|
||||
@@ -211,14 +211,12 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
} // namespace emsesp
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
// simulate receiving a MQTT message, used only for testing
|
||||
void Mqtt::incoming(const char * topic, const char * payload) {
|
||||
on_message(topic, payload, strlen(payload));
|
||||
}
|
||||
#endif
|
||||
|
||||
// received an MQTT message that we subscribed too
|
||||
void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
|
||||
@@ -358,6 +356,11 @@ void Mqtt::start() {
|
||||
mqtt_enabled_ = mqttSettings.enabled;
|
||||
});
|
||||
|
||||
// if MQTT disabled, quit
|
||||
if (!mqtt_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
|
||||
mqttClient_->onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
|
||||
@@ -529,7 +532,7 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
|
||||
}
|
||||
|
||||
// if the queue is full, make room but removing the last one
|
||||
if (mqtt_messages_.size() >= maximum_mqtt_messages_) {
|
||||
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) {
|
||||
mqtt_messages_.pop_front();
|
||||
}
|
||||
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
|
||||
@@ -611,26 +614,6 @@ void Mqtt::process_queue() {
|
||||
return;
|
||||
}
|
||||
|
||||
// show queue - Debug only
|
||||
/*
|
||||
Serial.printf("MQTT queue:\n\r");
|
||||
for (const auto & message : mqtt_messages_) {
|
||||
auto content = message.content_;
|
||||
if (content->operation == Operation::PUBLISH) {
|
||||
// Publish messages
|
||||
Serial.printf(" [%02d] (Pub) topic=%s payload=%s (pid %d, retry #%d)\n\r",
|
||||
message.id_,
|
||||
content->topic.c_str(),
|
||||
content->payload.c_str(),
|
||||
message.packet_id_,
|
||||
message.retry_count_);
|
||||
} else {
|
||||
// Subscribe messages
|
||||
Serial.printf(" [%02d] (Sub) topic=%s\n\r", message.id_, content->topic.c_str());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// fetch first from queue and create the full topic name
|
||||
auto mqtt_message = mqtt_messages_.front();
|
||||
auto message = mqtt_message.content_;
|
||||
@@ -697,9 +680,8 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons
|
||||
return;
|
||||
}
|
||||
|
||||
return; // TODO
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_SMALL);
|
||||
// StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
|
||||
doc["name"] = name;
|
||||
doc["uniq_id"] = entity;
|
||||
@@ -727,11 +709,13 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons
|
||||
snprintf_P(ha_device, sizeof(ha_device), PSTR("ems-esp-%s"), EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
ids.add(ha_device);
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf_P(topic, sizeof(topic), PSTR("homeassistant/binary_sensor/ems-esp/%s/config"), entity);
|
||||
|
||||
// convert json to string and publish immediately with retain forced to true
|
||||
char payload_text[300];
|
||||
char payload_text[256];
|
||||
serializeJson(doc, payload_text); // convert json to string
|
||||
uint16_t packet_id = mqttClient_->publish(topic, 0, true, payload_text);
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
@@ -744,7 +728,8 @@ void Mqtt::register_mqtt_ha_binary_sensor(const __FlashStringHelper * name, cons
|
||||
}
|
||||
#endif
|
||||
|
||||
delay(MQTT_PUBLISH_WAIT);
|
||||
// delay(MQTT_PUBLISH_WAIT);
|
||||
delay(50);
|
||||
}
|
||||
|
||||
// HA config for a normal 'sensor' type
|
||||
@@ -765,14 +750,15 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
|
||||
if (prefix != nullptr) {
|
||||
snprintf_P(new_entity, sizeof(new_entity), PSTR("%s.%s"), prefix, entity);
|
||||
} else {
|
||||
strcpy(new_entity, entity);
|
||||
strncpy(new_entity, entity, sizeof(new_entity));
|
||||
}
|
||||
|
||||
std::string device_name = EMSdevice::device_type_2_device_name(device_type);
|
||||
char device_name[50];
|
||||
strncpy(device_name, EMSdevice::device_type_2_device_name(device_type).c_str(), sizeof(device_name));
|
||||
|
||||
// build unique identifier, replacing all . with _ as not to break HA
|
||||
std::string uniq(50, '\0');
|
||||
snprintf_P(&uniq[0], uniq.capacity() + 1, PSTR("%s_%s"), device_name.c_str(), new_entity);
|
||||
snprintf_P(&uniq[0], uniq.capacity() + 1, PSTR("%s_%s"), device_name, new_entity);
|
||||
std::replace(uniq.begin(), uniq.end(), '.', '_');
|
||||
|
||||
// topic
|
||||
@@ -782,9 +768,9 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
|
||||
// state topic
|
||||
char stat_t[MQTT_TOPIC_MAX_SIZE];
|
||||
if (suffix != nullptr) {
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data%s"), hostname_.c_str(), device_name.c_str(), uuid::read_flash_string(suffix).c_str());
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data%s"), hostname_.c_str(), device_name, uuid::read_flash_string(suffix).c_str());
|
||||
} else {
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data"), hostname_.c_str(), device_name.c_str());
|
||||
snprintf_P(stat_t, sizeof(stat_t), PSTR("%s/%s_data"), hostname_.c_str(), device_name);
|
||||
}
|
||||
|
||||
// value template
|
||||
@@ -793,20 +779,22 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
|
||||
|
||||
// ha device
|
||||
char ha_device[40];
|
||||
snprintf_P(ha_device, sizeof(ha_device), PSTR("ems-esp-%s"), device_name.c_str());
|
||||
snprintf_P(ha_device, sizeof(ha_device), PSTR("ems-esp-%s"), device_name);
|
||||
|
||||
// name
|
||||
char new_name[50];
|
||||
if (prefix != nullptr) {
|
||||
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s %s"), device_name.c_str(), prefix, uuid::read_flash_string(name).c_str());
|
||||
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s %s"), device_name, prefix, uuid::read_flash_string(name).c_str());
|
||||
} else {
|
||||
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s"), device_name.c_str(), uuid::read_flash_string(name).c_str());
|
||||
snprintf_P(new_name, sizeof(new_name), PSTR("%s %s"), device_name, uuid::read_flash_string(name).c_str());
|
||||
}
|
||||
new_name[0] = toupper(new_name[0]); // capitalize first letter
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_SMALL);
|
||||
// StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
|
||||
doc["name"] = new_name;
|
||||
doc["uniq_id"] = uniq;
|
||||
doc["uniq_id"] = uniq.c_str();
|
||||
if (uom != nullptr) {
|
||||
doc["unit_of_meas"] = uom;
|
||||
}
|
||||
@@ -819,9 +807,9 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(ha_device);
|
||||
|
||||
doc.shrinkToFit();
|
||||
// convert json to string and publish immediately with retain forced to true
|
||||
// std::string payload_text;
|
||||
char payload_text[300];
|
||||
char payload_text[256];
|
||||
serializeJson(doc, payload_text); // convert json to string
|
||||
|
||||
uint16_t packet_id = mqttClient_->publish(topic, 0, true, payload_text);
|
||||
@@ -836,6 +824,6 @@ void Mqtt::register_mqtt_ha_sensor(const char * prefix,
|
||||
}
|
||||
|
||||
// delay(MQTT_PUBLISH_WAIT); // don't flood asynctcp
|
||||
delay(50); // enough time to send the short message out
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
18
src/mqtt.h
18
src/mqtt.h
@@ -38,9 +38,10 @@
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
#define EMSESP_MAX_JSON_SIZE_SMALL 256 // for smaller json docs when using StaticJsonDocument
|
||||
#define EMSESP_MAX_JSON_SIZE_MEDIUM 768 // for smaller json docs from ems devices, when using StaticJsonDocument
|
||||
#define EMSESP_MAX_JSON_SIZE_LARGE 2048 // for large json docs from ems devices, like boiler or thermostat data. Using DynamicJsonDocument
|
||||
#define EMSESP_MAX_JSON_SIZE_SMALL 384 // for smaller json docs when using StaticJsonDocument
|
||||
#define EMSESP_MAX_JSON_SIZE_MEDIUM 768 // for medium json docs from ems devices, when using StaticJsonDocument
|
||||
#define EMSESP_MAX_JSON_SIZE_LARGE 1024 // for large json docs from ems devices, like boiler or thermostat data. Using StaticJsonDocument
|
||||
#define EMSESP_MAX_JSON_SIZE_DYN 2048 // for large json docs from web. Using DynamicJsonDocument
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -118,9 +119,7 @@ class Mqtt {
|
||||
mqttClient_->disconnect();
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
void incoming(const char * topic, const char * payload); // for testing only
|
||||
#endif
|
||||
|
||||
static bool connected() {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
@@ -171,11 +170,14 @@ class Mqtt {
|
||||
static std::list<QueuedMqttMessage> mqtt_messages_;
|
||||
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
static uint16_t mqtt_message_id_;
|
||||
|
||||
static size_t maximum_mqtt_messages_;
|
||||
static uint16_t mqtt_message_id_;
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 70; // size of queue
|
||||
#else
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 20; // size of queue
|
||||
#endif
|
||||
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 70; // size of queue
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, to account for large payloads
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ void Shower::start() {
|
||||
shower_alert_ = settings.shower_alert;
|
||||
});
|
||||
|
||||
send_mqtt_stat(false); // send first MQTT publish
|
||||
if (Mqtt::enabled()) {
|
||||
send_mqtt_stat(false); // send first MQTT publish
|
||||
}
|
||||
}
|
||||
|
||||
void Shower::loop() {
|
||||
|
||||
106
src/system.cpp
106
src/system.cpp
@@ -21,6 +21,10 @@
|
||||
|
||||
#include "version.h" // firmware version of EMS-ESP
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
#include "test/test.h"
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger System::logger_{F_(system), uuid::log::Facility::KERN};
|
||||
@@ -37,11 +41,22 @@ bool System::hide_led_ = false;
|
||||
uint8_t System::led_gpio_ = 0;
|
||||
uint16_t System::analog_ = 0;
|
||||
bool System::analog_enabled_ = false;
|
||||
bool System::syslog_enabled_ = false;
|
||||
std::string System::hostname_;
|
||||
int8_t System::syslog_level_ = -1;
|
||||
uint32_t System::syslog_mark_interval_ = 0;
|
||||
String System::syslog_host_;
|
||||
|
||||
|
||||
|
||||
// send on/off to a gpio pin
|
||||
// value: true = HIGH, false = LOW
|
||||
// http://ems-esp/api?device=system&cmd=pin&data=1&id=2
|
||||
bool System::command_pin(const char * value, const int8_t id) {
|
||||
if (id < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool v = false;
|
||||
if (Helpers::value2bool(value, v)) {
|
||||
pinMode(id, OUTPUT);
|
||||
@@ -53,12 +68,34 @@ bool System::command_pin(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// send raw
|
||||
// send raw to ems
|
||||
bool System::command_send(const char * value, const int8_t id) {
|
||||
EMSESP::send_raw_telegram(value); // ignore id
|
||||
return true;
|
||||
}
|
||||
|
||||
// fetch device values
|
||||
bool System::command_fetch(const char * value, const int8_t id) {
|
||||
LOG_INFO(F("Requesting data from EMS devices"));
|
||||
EMSESP::fetch_device_values();
|
||||
return true;
|
||||
}
|
||||
|
||||
// mqtt publish
|
||||
bool System::command_publish(const char * value, const int8_t id) {
|
||||
std::string ha(10, '\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;
|
||||
}
|
||||
}
|
||||
EMSESP::publish_all(); // ignore value and id
|
||||
LOG_INFO(F("Publishing all data to MQTT"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// restart EMS-ESP
|
||||
void System::restart() {
|
||||
LOG_NOTICE(F("Restarting system..."));
|
||||
@@ -112,12 +149,20 @@ uint8_t System::free_mem() {
|
||||
void System::syslog_init() {
|
||||
// fetch settings
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
syslog_enabled_ = settings.syslog_enabled;
|
||||
syslog_level_ = settings.syslog_level;
|
||||
syslog_mark_interval_ = settings.syslog_mark_interval;
|
||||
syslog_host_ = settings.syslog_host;
|
||||
});
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!syslog_enabled_) {
|
||||
syslog_.log_level((uuid::log::Level)-1);
|
||||
syslog_.mark_interval(0);
|
||||
syslog_.destination((IPAddress)((uint32_t)0));
|
||||
return;
|
||||
}
|
||||
|
||||
syslog_.start(); // syslog service re-start
|
||||
|
||||
// configure syslog
|
||||
@@ -151,11 +196,16 @@ void System::start() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(pin), System::command_pin);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(send), System::command_send);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(publish), System::command_publish);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, 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_(report), System::command_report);
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(test), System::command_test);
|
||||
#endif
|
||||
});
|
||||
|
||||
syslog_init(); // init SysLog
|
||||
|
||||
init();
|
||||
}
|
||||
@@ -169,6 +219,7 @@ void System::init() {
|
||||
Helpers::bool_format(settings.bool_format);
|
||||
analog_enabled_ = settings.analog_enabled;
|
||||
});
|
||||
syslog_init(); // init SysLog
|
||||
|
||||
EMSESP::esp8266React.getWiFiSettingsService()->read([&](WiFiSettings & settings) { hostname(settings.hostname.c_str()); });
|
||||
|
||||
@@ -207,8 +258,11 @@ void System::upload_status(bool in_progress) {
|
||||
// checks system health and handles LED flashing wizardry
|
||||
void System::loop() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
syslog_.loop();
|
||||
#endif
|
||||
|
||||
if (syslog_enabled_) {
|
||||
syslog_.loop();
|
||||
}
|
||||
|
||||
led_monitor(); // check status and report back using the LED
|
||||
system_check(); // check system health
|
||||
if (analog_enabled_) {
|
||||
@@ -230,6 +284,8 @@ void System::loop() {
|
||||
show_mem("core");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -481,13 +537,18 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
shell.println();
|
||||
shell.printfln(F("Syslog:"));
|
||||
shell.print(F_(1space));
|
||||
shell.printfln(F_(host_fmt), !settings.syslog_host.isEmpty() ? settings.syslog_host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
|
||||
shell.print(F_(1space));
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast<uuid::log::Level>(settings.syslog_level)));
|
||||
shell.print(F_(1space));
|
||||
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
|
||||
|
||||
if (!settings.syslog_enabled) {
|
||||
shell.printfln(F("Syslog: disabled"));
|
||||
} else {
|
||||
shell.printfln(F("Syslog:"));
|
||||
shell.print(F_(1space));
|
||||
shell.printfln(F_(host_fmt), !settings.syslog_host.isEmpty() ? settings.syslog_host.c_str() : uuid::read_flash_string(F_(unset)).c_str());
|
||||
shell.print(F_(1space));
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast<uuid::log::Level>(settings.syslog_level)));
|
||||
shell.print(F_(1space));
|
||||
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval);
|
||||
}
|
||||
});
|
||||
|
||||
#endif
|
||||
@@ -652,7 +713,7 @@ bool System::check_upgrade() {
|
||||
l_cfg.setAutoFormat(false);
|
||||
LittleFS.setConfig(l_cfg); // do not auto format if it can't find LittleFS
|
||||
if (LittleFS.begin()) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_FORCE_SERIAL)
|
||||
Serial.begin(115200);
|
||||
Serial.println(F("FS is Littlefs"));
|
||||
Serial.end();
|
||||
@@ -667,7 +728,7 @@ bool System::check_upgrade() {
|
||||
cfg.setAutoFormat(false); // prevent formatting when opening SPIFFS filesystem
|
||||
SPIFFS.setConfig(cfg);
|
||||
if (!SPIFFS.begin()) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_FORCE_SERIAL)
|
||||
Serial.begin(115200);
|
||||
Serial.println(F("No old SPIFFS found!"));
|
||||
Serial.end();
|
||||
@@ -766,9 +827,7 @@ bool System::check_upgrade() {
|
||||
file.close();
|
||||
|
||||
if (failed) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Serial.println(F("Failed to read system config. Quitting."));
|
||||
#endif
|
||||
SPIFFS.end();
|
||||
Serial.end();
|
||||
return false;
|
||||
@@ -791,10 +850,6 @@ bool System::check_upgrade() {
|
||||
Serial.printf(PSTR("Error. Failed to deserialize custom json, error %s\n"), error.c_str());
|
||||
failed = true;
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
serializeJson(doc, Serial);
|
||||
Serial.println();
|
||||
#endif
|
||||
custom_settings = doc["settings"];
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
@@ -803,6 +858,7 @@ bool System::check_upgrade() {
|
||||
settings.shower_timer = custom_settings["shower_timer"] | EMSESP_DEFAULT_SHOWER_TIMER;
|
||||
settings.master_thermostat = custom_settings["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT;
|
||||
settings.ems_bus_id = custom_settings["bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
settings.syslog_enabled = false;
|
||||
settings.syslog_host = EMSESP_DEFAULT_SYSLOG_HOST;
|
||||
settings.syslog_level = EMSESP_DEFAULT_SYSLOG_LEVEL;
|
||||
settings.syslog_mark_interval = EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
|
||||
@@ -821,9 +877,7 @@ bool System::check_upgrade() {
|
||||
SPIFFS.end();
|
||||
|
||||
if (failed) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Serial.println(F("Failed to read custom config. Quitting."));
|
||||
#endif
|
||||
Serial.end();
|
||||
return false;
|
||||
}
|
||||
@@ -918,6 +972,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json
|
||||
JsonObject node = json.createNestedObject("Settings");
|
||||
node["tx_mode"] = settings.tx_mode;
|
||||
node["ems_bus_id"] = settings.ems_bus_id;
|
||||
node["syslog_enabled"] = settings.syslog_enabled;
|
||||
node["syslog_level"] = settings.syslog_level;
|
||||
node["syslog_mark_interval"] = settings.syslog_mark_interval;
|
||||
node["syslog_host"] = settings.syslog_host;
|
||||
@@ -1030,4 +1085,13 @@ bool System::command_report(const char * value, const int8_t id, JsonObject & js
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
// 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);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
21
src/system.h
21
src/system.h
@@ -50,18 +50,24 @@ class System {
|
||||
|
||||
static bool command_pin(const char * value, const int8_t id);
|
||||
static bool command_send(const char * value, const int8_t id);
|
||||
static bool command_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_report(const char * value, const int8_t id, JsonObject & json);
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
|
||||
static uint8_t free_mem();
|
||||
static void upload_status(bool in_progress);
|
||||
static bool upload_status();
|
||||
static void show_mem(const char * note);
|
||||
static void set_led();
|
||||
static void init();
|
||||
static void syslog_init();
|
||||
|
||||
bool check_upgrade();
|
||||
void syslog_init();
|
||||
void send_heartbeat();
|
||||
|
||||
static std::string hostname() {
|
||||
@@ -108,12 +114,13 @@ class System {
|
||||
static std::string hostname_;
|
||||
|
||||
// settings
|
||||
static bool hide_led_;
|
||||
uint8_t syslog_level_;
|
||||
uint32_t syslog_mark_interval_;
|
||||
String syslog_host_;
|
||||
static uint8_t led_gpio_;
|
||||
static bool analog_enabled_;
|
||||
static bool hide_led_;
|
||||
static bool syslog_enabled_;
|
||||
static int8_t syslog_level_;
|
||||
static uint32_t syslog_mark_interval_;
|
||||
static String syslog_host_;
|
||||
static uint8_t led_gpio_;
|
||||
static bool analog_enabled_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -429,6 +429,11 @@ void TxService::add(const uint8_t operation,
|
||||
// format is EMS 1.0 (src, dest, type_id, offset, data)
|
||||
// length is the length of the whole telegram data, excluding the CRC
|
||||
void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t length, const bool front) {
|
||||
// check length
|
||||
if (length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build header. src, dest and offset have fixed positions
|
||||
uint8_t src = data[0];
|
||||
uint8_t dest = data[1];
|
||||
|
||||
@@ -384,7 +384,7 @@ class TxService : public EMSbus {
|
||||
|
||||
void send_telegram(const QueuedTxTelegram & tx_telegram);
|
||||
void send_telegram(const uint8_t * data, const uint8_t length);
|
||||
}; // namespace emsesp
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
|
||||
@@ -17,24 +17,145 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
|
||||
#include "test.h"
|
||||
|
||||
// create some fake test data
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// create some fake test data
|
||||
// used with the 'test' command, under su/admin
|
||||
void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
if (command == "default") {
|
||||
run_test(shell, "mixer"); // add the default test case here
|
||||
// no shell
|
||||
void Test::run_test(const char * command, int8_t id) {
|
||||
if ((command == nullptr) || (strlen(command) == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.empty()) {
|
||||
run_test(shell, "default");
|
||||
if (strcmp(command, "general") == 0) {
|
||||
EMSESP::logger().info(F("Testing general..."));
|
||||
|
||||
add_device(0x08, 123); // Nefit Trendline
|
||||
add_device(0x18, 157); // Bosch CR100
|
||||
|
||||
// add some data
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
|
||||
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
||||
|
||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
|
||||
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(command, "gateway") == 0) {
|
||||
EMSESP::logger().info(F("Testing gateway..."));
|
||||
|
||||
// add 0x48 KM200, via a version command
|
||||
rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
|
||||
|
||||
// Boiler(0x08) -> All(0x00), UBADevices(0x07), data: 09 01 00 00 00 00 00 00 01 00 00 00 00
|
||||
// check: make sure 0x48 is not detected again !
|
||||
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x09, 01, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, 00});
|
||||
|
||||
// add thermostat - Thermostat: RC300/RC310/Moduline 3000/CW400/Sense II (DeviceID:0x10, ProductID:158, Version:03.03) ** master device **
|
||||
add_device(0x10, 158); // Nefit Trendline
|
||||
|
||||
// simulate incoming telegram
|
||||
// Thermostat(0x10) -> 48(0x48), ?(0x26B), data: 6B 08 4F 00 00 00 02 00 00 00 02 00 03 00 03 00 03
|
||||
rx_telegram({0x10, 0x48, 0xFF, 00, 01, 0x6B, 00, 0x6B, 0x08, 0x4F, 00, 00, 00, 02, 00, 00, 00, 02, 00, 03, 00, 03, 00, 03});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(command, "boiler") == 0) {
|
||||
// EMSESP::logger().info(F("Testing boiler..."));
|
||||
add_device(0x08, 123); // Nefit Trendline
|
||||
|
||||
// UBAuptime
|
||||
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(command, "thermostat") == 0) {
|
||||
EMSESP::logger().info(F("Testing thermostat..."));
|
||||
add_device(0x10, 192); // FW120
|
||||
|
||||
// HC1
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
// HC2
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
// HC3
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(command, "solar") == 0) {
|
||||
EMSESP::logger().info(F("Testing solar..."));
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
|
||||
add_device(0x30, 163); // SM100
|
||||
|
||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
|
||||
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||
|
||||
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(command, "heatpump") == 0) {
|
||||
EMSESP::logger().info(F("Testing heatpump..."));
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
add_device(0x38, 200); // Enviline module
|
||||
add_device(0x10, 192); // FW120 thermostat
|
||||
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
|
||||
|
||||
uart_telegram("38 0B FF 00 03 7B 0C 34 00 74");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// used with the 'test' command, under su/admin
|
||||
void Test::run_test_shell(uuid::console::Shell & shell, const std::string & command) {
|
||||
// switch to su
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
|
||||
if ((command == "default") || (command == "general") || (command.empty())) {
|
||||
shell.printfln(F("Testing adding a general boiler & thermostat..."));
|
||||
run_test("general");
|
||||
shell.invoke_command("show devices");
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
}
|
||||
|
||||
if (command == "render") {
|
||||
shell.printfln(F("Testing render..."));
|
||||
|
||||
uint8_t test1 = 12;
|
||||
int8_t test2 = -12;
|
||||
uint16_t test3 = 456;
|
||||
@@ -130,47 +251,34 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
}
|
||||
|
||||
if (command == "devices") {
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); // this is important otherwise nothing will be picked up!
|
||||
shell.printfln(F("Testing devices..."));
|
||||
|
||||
//emsdevices.push_back(EMSFactory::add(EMSdevice::DeviceType::BOILER, EMSdevice::EMS_DEVICE_ID_BOILER, 0, "", "My Boiler", 0, 0));
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); // this is important otherwise nothing will be picked up!
|
||||
|
||||
// A fake response - UBADevices(0x07)
|
||||
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
}
|
||||
|
||||
if (command == "boiler") {
|
||||
// question: do we need to set the mask?
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
|
||||
// UBAuptime
|
||||
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call boiler info");
|
||||
}
|
||||
|
||||
// check for boiler and controller on same product_id
|
||||
if (command == "double") {
|
||||
// question: do we need to set the mask?
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x08, 206, version, EMSdevice::Brand::BUDERUS); // Nefit Excellent HR30
|
||||
EMSESP::add_device(0x09, 206, version, EMSdevice::Brand::BUDERUS); // Nefit Excellent HR30 Controller
|
||||
shell.printfln(F("Testing double..."));
|
||||
|
||||
add_device(0x08, 206); // Nefit Excellent HR30
|
||||
add_device(0x09, 206); // Nefit Excellent HR30 Controller
|
||||
|
||||
// UBAuptime
|
||||
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||
}
|
||||
|
||||
// unknown device -
|
||||
// unknown device
|
||||
if (command == "unknown") {
|
||||
// question: do we need to set the mask?
|
||||
std::string version("1.2.3");
|
||||
shell.printfln(F("Testing unknown..."));
|
||||
|
||||
// add boiler
|
||||
EMSESP::add_device(0x08, 84, version, EMSdevice::Brand::BUDERUS);
|
||||
add_device(0x08, 84);
|
||||
|
||||
// add Controller - BC10 GB142 - but using the same device_id to see what happens
|
||||
EMSESP::add_device(0x09, 84, version, EMSdevice::Brand::BUDERUS);
|
||||
// add Controller - BC10 GB142 - but using the same product_id to see what happens
|
||||
add_device(0x09, 84);
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
// note there is no brand (byte 9)
|
||||
@@ -181,33 +289,22 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
}
|
||||
|
||||
if (command == "unknown2") {
|
||||
shell.printfln(F("Testing unknown2..."));
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // product id is 90 which doesn't exist
|
||||
}
|
||||
|
||||
if (command == "gateway") {
|
||||
// add 0x48 KM200, via a version command
|
||||
rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
|
||||
|
||||
// Boiler(0x08) -> All(0x00), UBADevices(0x07), data: 09 01 00 00 00 00 00 00 01 00 00 00 00
|
||||
// check: make sure 0x48 is not detected again !
|
||||
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x09, 01, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, 00});
|
||||
|
||||
// add thermostat - Thermostat: RC300/RC310/Moduline 3000/CW400/Sense II (DeviceID:0x10, ProductID:158, Version:03.03) ** master device **
|
||||
std::string version("01.03");
|
||||
EMSESP::add_device(0x10, 158, version, EMSdevice::Brand::BUDERUS);
|
||||
|
||||
// simulate incoming telegram
|
||||
// Thermostat(0x10) -> 48(0x48), ?(0x26B), data: 6B 08 4F 00 00 00 02 00 00 00 02 00 03 00 03 00 03
|
||||
rx_telegram({0x10, 0x48, 0xFF, 00, 01, 0x6B, 00, 0x6B, 0x08, 0x4F, 00, 00, 00, 02, 00, 00, 00, 02, 00, 03, 00, 03, 00, 03});
|
||||
shell.printfln(F("Testing Gateway..."));
|
||||
run_test("gateway");
|
||||
}
|
||||
|
||||
if (command == "web") {
|
||||
shell.printfln(F("Testing Web..."));
|
||||
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
add_device(0x08, 123); // Nefit Trendline
|
||||
add_device(0x18, 157); // Bosch CR100
|
||||
|
||||
// add some data
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
@@ -228,57 +325,25 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
uart_telegram({0x98, 0x00, 0x06, 0x00, 0x00, 0x03, 0x04, 0x0C, 0x02, 0x33, 0x06, 00, 00, 00, 00, 00, 00});
|
||||
|
||||
shell.invoke_command("show");
|
||||
StaticJsonDocument<2000> doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
|
||||
StaticJsonDocument<500> doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
EMSESP::device_info_web(2, root); // show thermostat. use 1 for boiler
|
||||
serializeJsonPretty(doc, shell);
|
||||
shell.println();
|
||||
}
|
||||
|
||||
if (command == "general") {
|
||||
shell.printfln(F("Testing adding a boiler & thermostat..."));
|
||||
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
|
||||
// add some data
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
|
||||
uart_telegram({0x08, 0x98, 0x33, 0x00, 0x23, 0x24});
|
||||
|
||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
|
||||
// Thermostat RCPLUSStatusMessage_HC1(0x01A5)
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
if (command == "boiler") {
|
||||
shell.printfln(F("Testing boiler..."));
|
||||
run_test("boiler");
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
shell.invoke_command("call boiler info");
|
||||
}
|
||||
|
||||
if (command == "fr120") {
|
||||
shell.printfln(F("Testing adding a thermostat FR120..."));
|
||||
|
||||
// add_device(0x10, 165, version, EMSdevice::Brand::BUDERUS);
|
||||
// add_device(0x17, 125, version, EMSdevice::Brand::BUDERUS); // test unknown class test
|
||||
// add_device(0x17, 93, version, EMSdevice::Brand::BUDERUS);
|
||||
// add_device(0x17, 254, version, EMSdevice::Brand::BUDERUS); // test unknown product_id
|
||||
|
||||
// EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
|
||||
std::string version("1.2.3");
|
||||
|
||||
// add a boiler
|
||||
// EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
|
||||
// add a thermostat
|
||||
EMSESP::add_device(0x10, 191, version, EMSdevice::Brand::JUNKERS); // FR120
|
||||
add_device(0x10, 191); // FR120 thermostat
|
||||
|
||||
// HC1
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
|
||||
@@ -290,35 +355,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
if (command == "thermostat") {
|
||||
shell.printfln(F("Testing adding a thermostat FW120..."));
|
||||
|
||||
// add_device(0x10, 165, version, EMSdevice::Brand::BUDERUS);
|
||||
// add_device(0x17, 125, version, EMSdevice::Brand::BUDERUS); // test unknown class test
|
||||
// add_device(0x17, 93, version, EMSdevice::Brand::BUDERUS);
|
||||
// add_device(0x17, 254, version, EMSdevice::Brand::BUDERUS); // test unknown product_id
|
||||
|
||||
// EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
|
||||
std::string version("1.2.3");
|
||||
|
||||
// add a boiler
|
||||
// EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
|
||||
// add a thermostat
|
||||
EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
|
||||
|
||||
// HC1
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
// HC2
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
// HC3
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
|
||||
run_test("thermostat");
|
||||
shell.invoke_command("show");
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc1", "heat");
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc2", "28.8");
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat", "{\"cmd\":\"temp\",\"id\":2,\"data\":22}");
|
||||
@@ -327,13 +365,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
if (command == "tc100") {
|
||||
shell.printfln(F("Testing adding a TC100 thermostat to the EMS bus..."));
|
||||
|
||||
std::string version("02.21");
|
||||
|
||||
// add a boiler
|
||||
// EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
|
||||
// add a thermostat
|
||||
EMSESP::add_device(0x18, 202, version, EMSdevice::Brand::BOSCH); // Bosch TC100 - https://github.com/proddy/EMS-ESP/issues/474
|
||||
add_device(0x18, 202); // Bosch TC100 - https://github.com/proddy/EMS-ESP/issues/474
|
||||
|
||||
// 0x0A
|
||||
uart_telegram({0x98, 0x0B, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
@@ -342,51 +375,19 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
if (command == "solar") {
|
||||
shell.printfln(F("Testing Solar"));
|
||||
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x30, 163, version, EMSdevice::Brand::BUDERUS); // SM100
|
||||
|
||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
|
||||
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||
|
||||
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
|
||||
|
||||
run_test("solar");
|
||||
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on 1
|
||||
uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
|
||||
EMSESP::show_device_values(shell);
|
||||
}
|
||||
|
||||
if (command == "heatpump") {
|
||||
shell.printfln(F("Testing Heat Pump"));
|
||||
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
|
||||
std::string version("1.2.3");
|
||||
|
||||
// add heatpump
|
||||
EMSESP::add_device(0x38, 200, version, EMSdevice::Brand::BUDERUS); // Enviline module
|
||||
|
||||
// add a thermostat
|
||||
EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
|
||||
|
||||
uart_telegram("38 0B FF 00 03 7B 0C 34 00 74");
|
||||
run_test("heatpump");
|
||||
shell.invoke_command("call");
|
||||
shell.invoke_command("call heatpump info");
|
||||
EMSESP::show_device_values(shell);
|
||||
}
|
||||
|
||||
if (command == "solar200") {
|
||||
@@ -394,32 +395,26 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x30, 164, version, EMSdevice::Brand::BUDERUS); // SM200
|
||||
add_device(0x30, 164); // SM200
|
||||
|
||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80});
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
rx_telegram({0xB0, 0x0B, 0xFF, 0x00, 0x02, 0x62, 0x01, 0x44, 0x03, 0x30, 0x80, 00, 0x80, 00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x33});
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
rx_telegram({0xB0, 00, 0xFF, 0x18, 02, 0x62, 0x80, 00, 0xB8});
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
EMSESP::send_raw_telegram("B0 00 FF 18 02 62 80 00 B8");
|
||||
|
||||
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on 1
|
||||
uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation
|
||||
|
||||
EMSESP::show_device_values(shell);
|
||||
|
||||
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
|
||||
|
||||
EMSESP::show_device_values(shell);
|
||||
shell.invoke_command("show");
|
||||
}
|
||||
|
||||
if (command == "km") {
|
||||
@@ -429,13 +424,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x10, 158, version, EMSdevice::Brand::BUDERUS); // RC300
|
||||
EMSESP::add_device(0x48, 189, version, EMSdevice::Brand::BUDERUS); // KM200
|
||||
add_device(0x10, 158); // RC300
|
||||
add_device(0x48, 189); // KM200
|
||||
|
||||
// see https://github.com/proddy/EMS-ESP/issues/390
|
||||
|
||||
/*
|
||||
uart_telegram_withCRC("90 48 FF 04 01 A6 5C");
|
||||
uart_telegram_withCRC("90 48 FF 00 01 A6 4C");
|
||||
uart_telegram_withCRC("90 48 FF 08 01 A7 6D");
|
||||
@@ -473,7 +466,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
uart_telegram_withCRC("C8 90 FF 00 02 01 A6 D0");
|
||||
|
||||
// uart_telegram_withCRC("10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00");
|
||||
*/
|
||||
|
||||
uart_telegram_withCRC("C8 90 F7 02 01 FF 01 A6 BA");
|
||||
|
||||
@@ -494,8 +486,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_HT3); // switch to junkers
|
||||
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
add_device(0x18, 157); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
|
||||
// RCPLUSStatusMessage_HC1(0x01A5)
|
||||
// 98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 (no CRC)
|
||||
@@ -582,12 +573,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
uart_telegram({0x88, 00, 0x2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xD2, 00, 00, 0x80, 00, 00, 01, 0x9D, 0x80, 0x00, 0x02, 0x79, 00});
|
||||
}
|
||||
|
||||
if (command == "send") {
|
||||
shell.printfln(F("Sending to Tx..."));
|
||||
EMSESP::show_ems(shell);
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
}
|
||||
|
||||
if (command == "tx") {
|
||||
shell.printfln(F("Testing Tx..."));
|
||||
|
||||
@@ -626,8 +611,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
}
|
||||
|
||||
shell.loop_all();
|
||||
|
||||
EMSESP::txservice_.flush_tx_queue();
|
||||
}
|
||||
|
||||
@@ -665,8 +648,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
shell.printfln(F("Testing Commands..."));
|
||||
|
||||
// add a thermostat with 3 HCs
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x10, 192, version, EMSdevice::Brand::JUNKERS); // FW120
|
||||
add_device(0x10, 192); // FW120
|
||||
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x00, 0xCF, 0x21, 0x2E, 0x20, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03}); // HC1
|
||||
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x00, 0xCF, 0x22, 0x2F, 0x10, 0x00, 0x2E, 0x24,
|
||||
@@ -678,9 +661,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
shell.invoke_command("call");
|
||||
shell.invoke_command("call system info");
|
||||
|
||||
char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
strcpy(system_topic, "ems-esp/system");
|
||||
EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"info\"}"); // this should fail
|
||||
EMSESP::mqtt_.incoming("ems-esp/system", "{\"cmd\":\"info\"}"); // this should fail
|
||||
|
||||
shell.invoke_command("call thermostat wwmode"); // should do nothing
|
||||
shell.invoke_command("call thermostat mode auto 2"); // should error, no hc2
|
||||
@@ -689,8 +670,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
|
||||
if (command == "pin") {
|
||||
shell.printfln(F("Testing pin..."));
|
||||
|
||||
shell.invoke_command("su");
|
||||
shell.invoke_command("call system pin");
|
||||
shell.invoke_command("call system pin 1 true");
|
||||
}
|
||||
@@ -707,12 +686,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
});
|
||||
|
||||
// add a boiler
|
||||
// question: do we need to set the mask?
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
add_device(0x08, 123); // Nefit Trendline
|
||||
|
||||
// add a thermostat
|
||||
EMSESP::add_device(0x18, 157, version, EMSdevice::Brand::BOSCH); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
add_device(0x18, 157); // Bosch CR100 - https://github.com/proddy/EMS-ESP/issues/355
|
||||
|
||||
// RCPLUSStatusMessage_HC1(0x01A5) - HC1
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
@@ -787,23 +764,31 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
}
|
||||
|
||||
if (command == "rx2") {
|
||||
shell.printfln(F("Testing rx2..."));
|
||||
|
||||
uart_telegram({0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B,
|
||||
0x73, 0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF});
|
||||
}
|
||||
|
||||
// https://github.com/proddy/EMS-ESP/issues/380#issuecomment-633663007
|
||||
if (command == "rx3") {
|
||||
shell.printfln(F("Testing rx3..."));
|
||||
|
||||
uart_telegram({0x21, 0x0B, 0xFF, 0x00});
|
||||
}
|
||||
|
||||
// testing the UART tx command, without a queue
|
||||
if (command == "tx2") {
|
||||
shell.printfln(F("Testing tx2..."));
|
||||
|
||||
uint8_t t[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
|
||||
EMSuart::transmit(t, sizeof(t));
|
||||
}
|
||||
|
||||
// send read request with offset
|
||||
if (command == "offset") {
|
||||
shell.printfln(F("Testing offset..."));
|
||||
|
||||
// send_read_request(0x18, 0x08);
|
||||
EMSESP::txservice_.read_request(0x18, 0x08, 27); // no offset
|
||||
}
|
||||
@@ -820,14 +805,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
});
|
||||
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
std::string version("1.2.3");
|
||||
|
||||
// add controller
|
||||
EMSESP::add_device(0x09, 114, version, EMSdevice::Brand::BUDERUS);
|
||||
add_device(0x09, 114);
|
||||
|
||||
EMSESP::add_device(0x28, 160, version, EMSdevice::Brand::BUDERUS); // MM100, WWC
|
||||
EMSESP::add_device(0x29, 161, version, EMSdevice::Brand::BUDERUS); // MM200, WWC
|
||||
EMSESP::add_device(0x20, 160, version, EMSdevice::Brand::BOSCH); // MM100
|
||||
add_device(0x28, 160); // MM100, WWC
|
||||
add_device(0x29, 161); // MM200, WWC
|
||||
add_device(0x20, 160); // MM100
|
||||
|
||||
// WWC1 on 0x29
|
||||
uart_telegram({0xA9, 0x00, 0xFF, 0x00, 0x02, 0x32, 0x02, 0x6C, 0x00, 0x3C, 0x00, 0x3C, 0x3C, 0x46, 0x02, 0x03, 0x03, 0x00, 0x3C});
|
||||
@@ -838,16 +822,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
// check for error "No telegram type handler found for ID 0x255 (src 0x20)"
|
||||
uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A});
|
||||
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call");
|
||||
shell.invoke_command("call mixer info");
|
||||
shell.invoke_command("publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
shell.invoke_command("call mixer");
|
||||
// shell.invoke_command("show");
|
||||
// shell.invoke_command("call mixer info");
|
||||
// shell.invoke_command("call system publish");
|
||||
// shell.invoke_command("show mqtt");
|
||||
}
|
||||
|
||||
// finally dump to console
|
||||
EMSESP::loop();
|
||||
}
|
||||
|
||||
// simulates a telegram in the Rx queue, but without the CRC which is added automatically
|
||||
@@ -861,6 +840,7 @@ void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
}
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::rxservice_.add(data, len + 1);
|
||||
EMSESP::loop();
|
||||
}
|
||||
|
||||
// simulates a telegram straight from UART, but without the CRC which is added automatically
|
||||
@@ -874,7 +854,7 @@ void Test::uart_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
}
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::incoming_telegram(data, i + 1);
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::loop();
|
||||
}
|
||||
|
||||
// takes raw string, assuming it contains the CRC. This is what is output from 'watch raw'
|
||||
@@ -912,7 +892,7 @@ void Test::uart_telegram_withCRC(const char * rx_data) {
|
||||
}
|
||||
|
||||
EMSESP::incoming_telegram(data, count + 1);
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::loop();
|
||||
}
|
||||
|
||||
// takes raw string, adds CRC to end
|
||||
@@ -952,14 +932,14 @@ void Test::uart_telegram(const char * rx_data) {
|
||||
data[count + 1] = EMSESP::rxservice_.calculate_crc(data, count + 1); // add CRC
|
||||
|
||||
EMSESP::incoming_telegram(data, count + 2);
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::loop();
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
// Sends version telegram. Version is hardcoded to 1.0
|
||||
void Test::add_device(uint8_t device_id, uint8_t product_id) {
|
||||
// Send version: 09 0B 02 00 PP V1 V2
|
||||
uart_telegram({device_id, EMSESP_DEFAULT_EMS_BUS_ID, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0});
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
|
||||
#ifndef EMSESP_TEST_H
|
||||
#define EMSESP_TEST_H
|
||||
@@ -40,12 +40,14 @@ namespace emsesp {
|
||||
|
||||
class Test {
|
||||
public:
|
||||
static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing
|
||||
static void run_test_shell(uuid::console::Shell & shell, const std::string & command);
|
||||
static void run_test(const char * command, int8_t id = 0);
|
||||
static void dummy_mqtt_commands(const char * message);
|
||||
static void rx_telegram(const std::vector<uint8_t> & data);
|
||||
static void uart_telegram(const std::vector<uint8_t> & rx_data);
|
||||
static void uart_telegram(const char * rx_data);
|
||||
static void uart_telegram_withCRC(const char * rx_data);
|
||||
static void add_device(uint8_t device_id, uint8_t product_id);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "2.1.1b1"
|
||||
#define EMSESP_APP_VERSION "2.1.1b2"
|
||||
|
||||
Reference in New Issue
Block a user