Merge branch 'dev' into dev2

This commit is contained in:
MichaelDvP
2024-01-21 09:35:16 +01:00
76 changed files with 963 additions and 958 deletions

View File

@@ -679,7 +679,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
// if we're filtering on an attribute, go find it
if (attribute_s) {
if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;

View File

@@ -128,14 +128,16 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
string_vector{"test"},
string_vector{F_(name_optional), F_(data_optional)},
string_vector{F_(name_optional), F_(data_optional), F_(id_optional)},
[=](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.empty()) {
Test::run_test(shell, "default");
} else if (arguments.size() == 1) {
Test::run_test(shell, arguments.front());
} else {
} else if (arguments.size() == 2) {
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str());
} else {
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str(), arguments[2].c_str());
}
});
commands->add_command(ShellContext::MAIN, CommandFlags::USER, string_vector{"t"}, [=](Shell & shell, const std::vector<std::string> & arguments) {
@@ -531,15 +533,8 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
if (return_code == CommandRet::OK && json.size()) {
if (json.containsKey("api_data")) {
JsonVariant data = json["api_data"];
if (data.is<int>()) {
shell.printfln("%d", data.as<int>());
} else if (data.is<float>()) {
char s[10];
shell.println(Helpers::render_value(s, data.as<float>(), 1));
} else {
shell.println(data.as<const char *>());
}
String data = json["api_data"].as<String>();
shell.println(data.c_str());
return;
}
serializeJsonPretty(doc, shell);
@@ -568,9 +563,9 @@ static void setup_commands(std::shared_ptr<Commands> & commands) {
}
return devices_list;
} else if (current_arguments.size() == 1) {
std::vector<std::string> command_list;
uint8_t device_type = EMSdevice::device_name_2_device_type(current_arguments[0].c_str());
uint8_t device_type = EMSdevice::device_name_2_device_type(current_arguments[0].c_str());
if (Command::device_has_commands(device_type)) {
std::vector<std::string> command_list;
for (const auto & cf : Command::commands()) {
if (cf.device_type_ == device_type) {
command_list.emplace_back(cf.cmd_);

View File

@@ -482,6 +482,15 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(hpMaxPower),
DeviceValueUOM::PERCENT,
MAKE_CF_CB(set_hpMaxPower));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpSetDiffPress_,
DeviceValueType::UINT,
DeviceValueNumOp::DV_NUMOP_MUL50,
FL_(hpSetDiffPress),
DeviceValueUOM::MBAR,
MAKE_CF_CB(set_hpDiffPress),
150,
750);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpCompOn_, DeviceValueType::BOOL, FL_(hpCompOn), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpActivity_, DeviceValueType::ENUM, FL_(enum_hpactivity), FL_(hpActivity), DeviceValueUOM::NONE);
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpHeatingOn_, DeviceValueType::BOOL, FL_(hpHeatingOn), DeviceValueUOM::NONE);
@@ -1935,6 +1944,10 @@ void Boiler::process_HpMeters(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, meterHeat_, 24);
}
void Boiler::process_HpPressure(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpSetDiffPress_, 9);
}
// HIU unit
// boiler(0x08) -B-> All(0x00), ?(0x0779), data: 06 05 01 01 AD 02 EF FF FF 00 00 7F FF
@@ -2939,6 +2952,15 @@ bool Boiler::set_hpMaxPower(const char * value, const int8_t id) {
return false;
}
bool Boiler::set_hpDiffPress(const char * value, const int8_t id) {
int v;
if (Helpers::value2number(value, v)) {
write_command(0x2CC, 9, (uint8_t)(v / 50), 0x2CC);
return true;
}
return false;
}
bool Boiler::set_vp_cooling(const char * value, const int8_t id) {
bool v;
if (Helpers::value2bool(value, v)) {

View File

@@ -223,6 +223,7 @@ class Boiler : public EMSdevice {
uint32_t meterHeat_;
uint8_t hpEA0_;
uint8_t hpPumpMode_;
uint8_t hpSetDiffPress_;
// Pool unit
int8_t poolSetTemp_;
@@ -334,6 +335,7 @@ class Boiler : public EMSdevice {
void process_HpPool(std::shared_ptr<const Telegram> telegram);
void process_HpInput(std::shared_ptr<const Telegram> telegram);
void process_HpInConfig(std::shared_ptr<const Telegram> telegram);
void process_HpPressure(std::shared_ptr<const Telegram> telegram);
void process_HpCooling(std::shared_ptr<const Telegram> telegram);
void process_HpHeaterConfig(std::shared_ptr<const Telegram> telegram);
void process_HybridHp(std::shared_ptr<const Telegram> telegram);
@@ -437,6 +439,7 @@ class Boiler : public EMSdevice {
bool set_hpCircPumpWw(const char * value, const int8_t id);
bool set_hpPumpMode(const char * value, const int8_t id);
bool set_hpMaxPower(const char * value, const int8_t id);
bool set_hpDiffPress(const char * value, const int8_t id);
bool set_auxLimit(const char * value, const int8_t id);
inline bool set_auxMaxLimit(const char * value, const int8_t id) {

View File

@@ -30,7 +30,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage_HC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSSetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
register_telegram_type(device_id - 0x20 + 0x02CD, "MMPLUSSetMessage_HC", false, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
register_telegram_type((device_id - 0x20) * 2 + 0x02CC, "MMPLUSSetMessage_HC", false, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flowTempHc), DeviceValueUOM::DEGREES);
@@ -151,7 +151,7 @@ void Mixer::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
}
// Mixer Setting 0x2CD
// Mixer Setting 0x2CC
void Mixer::process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, activated_, 0); // on = 0xFF
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
@@ -241,7 +241,7 @@ bool Mixer::set_activated(const char * value, const int8_t id) {
}
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 0, b ? 0xFF : 0, 0x2CD + hc);
write_command(0x2CC + hc * 2, 0, b ? 0xFF : 0, 0x2CC + hc * 2);
return true;
}
return false;
@@ -260,7 +260,7 @@ bool Mixer::set_setValveTime(const char * value, const int8_t id) {
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
v = (v + 5) / 10;
uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 1, v, 0x2CD + hc);
write_command(0x2CC + hc * 2, 1, v, 0x2CC + hc * 2);
return true;
}
return false;
@@ -273,7 +273,7 @@ bool Mixer::set_flowTempOffset(const char * value, const int8_t id) {
}
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 2, v, 0x2CD + hc);
write_command(0x2CC + hc * 2, 2, v, 0x2CC + hc * 2);
return true;
}
return false;

View File

@@ -583,6 +583,7 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
}
}
});
if (ignore) {
return;
}
@@ -943,11 +944,10 @@ void EMSdevice::generate_values_web(JsonObject output) {
auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string
// add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique
if (dv.has_tag()) {
obj["id"] = mask + tag_to_string(dv.tag) + " " + fullname;
} else {
obj["id"] = mask + fullname;
}
obj["id"] = dv.has_tag() ? mask + tag_to_string(dv.tag) + " " + fullname : mask + fullname; // suffix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
// obj["id"] = dv.has_tag() ? mask + fullname + " " + tag_to_string(dv.tag) : mask + fullname; // suffix tag
// add commands and options
if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
@@ -1001,7 +1001,7 @@ void EMSdevice::generate_values_web(JsonObject output) {
// as generate_values_web() but stripped down to only show all entities and their state
// this is used only for WebCustomizationService::device_entities()
void EMSdevice::generate_values_web_customization(JsonArray & output) {
void EMSdevice::generate_values_web_customization(JsonArray output) {
for (auto & dv : devicevalues_) {
// also show commands and entities that have an empty full name
JsonObject obj = output.add<JsonObject>();
@@ -1058,13 +1058,10 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
auto fullname = Helpers::translated_word(dv.fullname);
if (dv.type != DeviceValueType::CMD) {
if (fullname) {
if (dv.has_tag()) {
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname);
obj["n"] = name;
} else {
obj["n"] = fullname;
}
obj["n"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname : fullname; // prefix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
// obj["n"] = (dv.has_tag()) ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
}
// add the custom name, is optional
@@ -1409,13 +1406,10 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
auto fullname = dv.get_fullname();
if (!fullname.empty()) {
if (dv.has_tag()) {
char name[50];
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname.c_str());
json["fullname"] = name;
} else {
json["fullname"] = fullname;
}
json["fullname"] = dv.has_tag() ? fullname + " " + tag_to_string(dv.tag) : fullname; // suffix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
json["fullname"] = dv.has_tag() ? std::string(tag_to_string(dv.tag)) + " " + fullname.c_str() : fullname; // prefix tag
}
if (dv.tag != DeviceValueTAG::TAG_NONE) {
@@ -1549,7 +1543,7 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
EMSESP::logger().debug("Attribute '%s'", attribute_s);
#endif
if (json.containsKey(attribute_s)) {
JsonVariant data = json[attribute_s];
String data = json[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;
@@ -1610,16 +1604,20 @@ bool EMSdevice::generate_values(JsonObject output, const uint8_t tag_filter, con
char name[80];
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
char short_name[20];
if (output_target == OUTPUT_TARGET::CONSOLE) {
snprintf(short_name, sizeof(short_name), " (%s)", dv.short_name);
} else {
strcpy(short_name, "");
}
// char short_name[20];
// if (output_target == OUTPUT_TARGET::CONSOLE) {
// snprintf(short_name, sizeof(short_name), "(%s)", dv.short_name);
// } else {
// strcpy(short_name, "");
// }
// add tag
if (have_tag) {
snprintf(name, sizeof(name), "%s %s%s", tag_to_string(dv.tag), fullname.c_str(), short_name); // prefix the tag
snprintf(name, sizeof(name), "%s %s (%s)", tag_to_string(dv.tag), fullname.c_str(), dv.short_name); // prefix tag
// TODO check TAG https://github.com/emsesp/EMS-ESP32/issues/1338
// snprintf(name, sizeof(name), "%s %s (%s)", fullname.c_str(), tag_to_string(dv.tag), dv.short_name); // sufix tag
} else {
snprintf(name, sizeof(name), "%s%s", fullname.c_str(), short_name);
snprintf(name, sizeof(name), "%s (%s)", fullname.c_str(), dv.short_name);
}
} else {
strlcpy(name, (dv.short_name), sizeof(name)); // use short name

View File

@@ -222,7 +222,7 @@ class EMSdevice {
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
bool generate_values(JsonObject output, const uint8_t tag_filter, const bool nested, const uint8_t output_target);
void generate_values_web(JsonObject output);
void generate_values_web_customization(JsonArray & output);
void generate_values_web_customization(JsonArray output);
void add_device_value(uint8_t tag,
void * value_p,

View File

@@ -110,7 +110,7 @@ const char * DeviceValue::DeviceValueUOM_s[] = {
F_(uom_blank), // 0
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], F_(uom_ua),
F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm),
F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_blank)
F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_mbar), F_(uom_blank)
};

View File

@@ -71,7 +71,8 @@ class DeviceValue {
KMIN, // 21 - K*min
K, // 22 - K
VOLTS, // 23 - V
CONNECTIVITY // 24 - used in HA
MBAR, // 24 - mbar
CONNECTIVITY // 25 - used in HA
};
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
@@ -143,7 +144,8 @@ class DeviceValue {
DV_NUMOP_DIV100 = 100,
DV_NUMOP_MUL5 = -5,
DV_NUMOP_MUL10 = -10,
DV_NUMOP_MUL15 = -15
DV_NUMOP_MUL15 = -15,
DV_NUMOP_MUL50 = -50
};
uint8_t device_type; // EMSdevice::DeviceType

View File

@@ -392,7 +392,6 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
// extract the shortname from the key, which is in brackets
std::size_t first_bracket = key.find_last_of('(');
std::size_t last_bracket = key.find_last_of(')');
std::string shortname = key.substr(first_bracket + 1, last_bracket - first_bracket - 1);
std::string uom = emsdevice->get_value_uom(key.substr(first_bracket + 1, last_bracket - first_bracket - 1));
shell.printfln(" %s: %s%s %s%s", key.c_str(), COLOR_BRIGHT_GREEN, p.value().as<std::string>().c_str(), uom.c_str(), COLOR_RESET);
@@ -705,7 +704,7 @@ bool EMSESP::get_device_value_info(JsonObject root, const char * cmd, const int8
// search for recognized device_ids : Me, All, otherwise print hex value
std::string EMSESP::device_tostring(const uint8_t device_id) {
if ((device_id & 0x7F) == rxservice_.ems_bus_id()) {
if ((device_id & 0x7F) == EMSbus::ems_bus_id()) {
return "Me";
} else if (device_id == 0x00) {
return "All";
@@ -734,9 +733,13 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
} else if (emsdevice->is_device_id(dest)) {
dest_name = emsdevice->device_type_name();
}
// get the type name, any match will do
// get the type name
if (type_name.empty()) {
type_name = emsdevice->telegram_type_name(telegram);
if ((telegram->operation == Telegram::Operation::RX_READ && emsdevice->is_device_id(dest))
|| (telegram->operation != Telegram::Operation::RX_READ && dest == 0 && emsdevice->is_device_id(src))
|| (telegram->operation != Telegram::Operation::RX_READ && src == EMSbus::ems_bus_id() && emsdevice->is_device_id(dest))) {
type_name = emsdevice->telegram_type_name(telegram);
}
}
}
}
@@ -867,7 +870,7 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// returns false if there are none found
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// if watching or reading...
if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == txservice_.ems_bus_id())) {
if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == EMSbus::ems_bus_id())) {
if (telegram->type_id == response_id_) {
if (!trace_raw_) {
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
@@ -896,7 +899,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
}
// only process broadcast telegrams or ones sent to us on request
// if ((telegram->dest != 0x00) && (telegram->dest != rxservice_.ems_bus_id())) {
// if ((telegram->dest != 0x00) && (telegram->dest != EMSbus::ems_bus_id())) {
if (telegram->operation == Telegram::Operation::RX_READ) {
// LOG_DEBUG("read telegram received, not processing");
return false;
@@ -924,13 +927,13 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
bool found = false;
bool knowndevice = false;
for (const auto & emsdevice : emsdevices) {
if (emsdevice->is_device_id(telegram->src)) {
if (emsdevice->is_device_id(telegram->src) && (telegram->dest == 0 || telegram->dest == EMSbus::ems_bus_id())) {
knowndevice = true;
found = emsdevice->handle_telegram(telegram);
// if we correctly processed the telegram then follow up with sending it via MQTT (if enabled)
if (found && Mqtt::connected()) {
if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update())
|| (telegram->type_id == publish_id_ && telegram->dest == txservice_.ems_bus_id())) {
|| (telegram->type_id == publish_id_ && telegram->dest == EMSbus::ems_bus_id())) {
if (telegram->type_id == publish_id_) {
publish_id_ = 0;
}
@@ -947,7 +950,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
emsdevice->add_handlers_ignored(telegram->type_id);
}
break;
} else if (emsdevice->is_device_id(telegram->dest)) {
} else if (emsdevice->is_device_id(telegram->dest) && telegram->src != EMSbus::ems_bus_id()) {
emsdevice->handle_telegram(telegram);
}
}
@@ -1021,7 +1024,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
// if its not in our database, we don't add it
bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand) {
// don't add ourselves!
if (device_id == rxservice_.ems_bus_id()) {
if (device_id == EMSbus::ems_bus_id()) {
return false;
}
@@ -1062,7 +1065,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
device_p = &device;
break;
}
if ((device_id >= EMSdevice::EMS_DEVICE_ID_HS1 && device_id <= EMSdevice::EMS_DEVICE_ID_HS16)) {
if (device_id >= EMSdevice::EMS_DEVICE_ID_HS1 && device_id <= EMSdevice::EMS_DEVICE_ID_HS16) {
device_p = &device;
device_p->device_type = DeviceType::HEATSOURCE;
break;
@@ -1265,7 +1268,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
#endif
// check first for echo
uint8_t first_value = data[0];
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) {
if (((first_value & 0x7F) == EMSbus::ems_bus_id()) && (length > 1)) {
// if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length);
#ifdef EMSESP_UART_DEBUG
@@ -1336,7 +1339,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
wait_km_ = true;
connect_time = uuid::get_uptime_sec();
}
if (poll_id == txservice_.ems_bus_id()) {
if (poll_id == EMSbus::ems_bus_id()) {
EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active
}
if (wait_km_) {

View File

@@ -249,6 +249,7 @@ MAKE_WORD_CUSTOM(uom_l, "l")
MAKE_WORD_CUSTOM(uom_kmin, "K*min")
MAKE_WORD_CUSTOM(uom_k, "K")
MAKE_WORD_CUSTOM(uom_volts, "V")
MAKE_WORD_CUSTOM(uom_mbar, "mbar")
// MQTT topics and prefixes
MAKE_WORD_CUSTOM(heating_active, "heating_active")

View File

@@ -286,7 +286,7 @@ MAKE_WORD_TRANSLATION(partymode, "party", "Party", "party", "", "impreza", "", "
MAKE_WORD_TRANSLATION(fireplace, "fireplace", "Kamin", "haard", "", "kominek", "", "", "şömine", "camino", "krb") // TODO translate
// MQTT Discovery - this is special device entity for 'climate'
MAKE_TRANSLATION(haclimate, "haclimate", "Discovery current room temperature", "Discovery Temperatur", "Discovery huidige kamertemperatuur", "", "termostat w HA", "HA Avlest temp", "", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "Zistiť aktuálnu teplotu v miestnosti") // TODO translate
MAKE_TRANSLATION(haclimate, "haclimate", "mqtt discovery current room temperature", "Discovery Temperatur", "Discovery huidige kamertemperatuur", "", "termostat w HA", "HA Avlest temp", "", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "Zistiť aktuálnu teplotu v miestnosti") // TODO translate
// Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it, sk
// Boiler
@@ -463,6 +463,7 @@ MAKE_TRANSLATION(hpPumpMode, "hppumpmode", "primary heatpump mode", "Modus Haupt
MAKE_TRANSLATION(instantstart, "instantstart", "instant start", "Sofortstart", "", "", "natychmiastowy start", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(heatondelay, "heatondelay", "heat-on delay", "Einschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(heatoffdelay, "heatoffdelay", "heat-off delay", "Ausschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(hpSetDiffPress, "hpsetdiffpress", "set differental pressure", "Pumpensolldruck", "", "", "", "", "", "", "", "") // TODO translate
// hybrid heatpump
MAKE_TRANSLATION(hybridStrategy, "hybridstrategy", "hybrid control strategy", "Hybrid Strategie", "Hybride strategie", "Hybrid kontrollstrategi", "strategia sterowania hybrydowego", "hybrid kontrollstrategi", "stratégie contrôle hybride", "hibrit kontrol stratejisi", "strategia comtrollo ibrido", "hybridná stratégia riadenia")

View File

@@ -683,12 +683,12 @@ bool Mqtt::queue_publish(const char * topic, const std::string & payload) {
return queue_publish_message((topic), payload, mqtt_retain_);
}
bool Mqtt::queue_publish(const char * topic, const JsonObjectConst & payload) {
bool Mqtt::queue_publish(const char * topic, const JsonObjectConst payload) {
return queue_publish_retain(topic, payload, mqtt_retain_);
}
// publish json doc, only if its not empty
bool Mqtt::queue_publish(const std::string & topic, const JsonObjectConst & payload) {
bool Mqtt::queue_publish(const std::string & topic, const JsonObjectConst payload) {
return queue_publish_retain(topic, payload, mqtt_retain_);
}
@@ -698,11 +698,11 @@ bool Mqtt::queue_publish_retain(const char * topic, const std::string & payload,
}
// publish json doc, only if its not empty, using the retain flag
bool Mqtt::queue_publish_retain(const std::string & topic, const JsonObjectConst & payload, const bool retain) {
bool Mqtt::queue_publish_retain(const std::string & topic, const JsonObjectConst payload, const bool retain) {
return queue_publish_retain(topic.c_str(), payload, retain);
}
bool Mqtt::queue_publish_retain(const char * topic, const JsonObjectConst & payload, const bool retain) {
bool Mqtt::queue_publish_retain(const char * topic, const JsonObjectConst payload, const bool retain) {
if (payload.size()) {
std::string payload_text;
payload_text.reserve(measureJson(payload) + 1);
@@ -722,7 +722,7 @@ bool Mqtt::queue_remove_topic(const char * topic) {
}
// queue a Home Assistant config topic and payload, with retain flag off.
bool Mqtt::queue_ha(const char * topic, const JsonObjectConst & payload) {
bool Mqtt::queue_ha(const char * topic, const JsonObjectConst payload) {
if (!enabled()) {
return false;
}
@@ -817,7 +817,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSd
const int16_t dv_set_min,
const uint32_t dv_set_max,
const int8_t num_op,
const JsonObjectConst & dev_json) {
const JsonObjectConst dev_json) {
// ignore if name (fullname) is empty
if (!fullname || !en_name) {
return false;

View File

@@ -68,31 +68,31 @@ class Mqtt {
static bool queue_publish(const std::string & topic, const std::string & payload);
static bool queue_publish(const char * topic, const char * payload);
static bool queue_publish(const std::string & topic, const JsonObjectConst & payload);
static bool queue_publish(const char * topic, const JsonObjectConst & payload);
static bool queue_publish(const std::string & topic, const JsonObjectConst payload);
static bool queue_publish(const char * topic, const JsonObjectConst payload);
static bool queue_publish(const char * topic, const std::string & payload);
static bool queue_publish_retain(const std::string & topic, const JsonObjectConst & payload, const bool retain);
static bool queue_publish_retain(const std::string & topic, const JsonObjectConst payload, const bool retain);
static bool queue_publish_retain(const char * topic, const std::string & payload, const bool retain);
static bool queue_publish_retain(const char * topic, const JsonObjectConst & payload, const bool retain);
static bool queue_ha(const char * topic, const JsonObjectConst & payload);
static bool queue_publish_retain(const char * topic, const JsonObjectConst payload, const bool retain);
static bool queue_ha(const char * topic, const JsonObjectConst payload);
static bool queue_remove_topic(const char * topic);
static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
static bool publish_ha_sensor_config(uint8_t type,
uint8_t tag,
const char * const fullname,
const char * const en_name,
const uint8_t device_type,
const char * const entity,
const uint8_t uom,
const bool remove,
const bool has_cmd,
const char * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const uint32_t dv_set_max,
const int8_t num_op,
const JsonObjectConst & dev_json);
static bool publish_ha_sensor_config(uint8_t type,
uint8_t tag,
const char * const fullname,
const char * const en_name,
const uint8_t device_type,
const char * const entity,
const uint8_t uom,
const bool remove,
const bool has_cmd,
const char * const ** options,
uint8_t options_size,
const int16_t dv_set_min,
const uint32_t dv_set_max,
const int8_t num_op,
const JsonObjectConst dev_json);
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
@@ -168,6 +168,10 @@ class Mqtt {
return entity_format_;
}
static void entity_format(uint8_t n) {
entity_format_ = n;
}
static uint8_t discovery_type() {
return discovery_type_;
}

View File

@@ -632,7 +632,7 @@ void System::send_info_mqtt() {
}
// create the json for heartbeat
bool System::heartbeat_json(JsonObject output) {
void System::heartbeat_json(JsonObject output) {
uint8_t bus_status = EMSESP::bus_status();
if (bus_status == EMSESP::BUS_STATUS_TX_ERRORS) {
output["bus_status"] = "txerror";
@@ -684,8 +684,6 @@ bool System::heartbeat_json(JsonObject output) {
output["wifistrength"] = wifi_quality(rssi);
}
#endif
return true;
}
// send periodic MQTT message with system information
@@ -700,9 +698,8 @@ void System::send_heartbeat() {
JsonDocument doc;
JsonObject json = doc.to<JsonObject>();
if (heartbeat_json(json)) {
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
}
heartbeat_json(json);
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
}
// initializes network
@@ -1471,7 +1468,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
#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) {
return Test::run_test(value, id);
return Test::test(value, id);
}
#endif

View File

@@ -79,7 +79,7 @@ class System {
void syslog_init();
bool check_upgrade(bool factory_settings);
bool check_restore();
bool heartbeat_json(JsonObject output);
void heartbeat_json(JsonObject output);
void send_heartbeat();
void send_info_mqtt();

View File

@@ -414,8 +414,8 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
if (Helpers::toLower(command_s) == Helpers::toLower(sensor.name().c_str()) || Helpers::toLower(command_s) == Helpers::toLower(sensor.id().c_str())) {
output["id"] = sensor.id();
output["name"] = sensor.name();
char val[10];
if (Helpers::hasValue(sensor.temperature_c)) {
char val[10];
output["value"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
}
@@ -426,7 +426,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
// if we're filtering on an attribute, go find it
if (attribute_s) {
if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;

View File

@@ -1,7 +1,6 @@
/*
* EMS-ESP - https://github.com/emsesp/EMS-ESP
* Copyright 2020-2023 Paul Derbyshire
* Copyright 2020-2023 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,12 +24,19 @@ namespace emsesp {
// no shell, called via the API or 'call system test' command
// or http://ems-esp/api?device=system&cmd=test&data=boiler
bool Test::run_test(const char * command, int8_t id) {
if ((command == nullptr) || (strlen(command) == 0)) {
bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
if (cmd.empty()) {
return false;
}
if (strcmp(command, "memory") == 0) {
if (cmd == "add") {
Mqtt::entity_format(Mqtt::entityFormat::SINGLE_LONG); // SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT
System::test_set_all_active(true); // include all entities and give them fake values
add_device(id1, id2);
return true;
}
if (cmd == "memory") {
EMSESP::logger().notice("Testing memory by adding lots of devices and entities...");
System::test_set_all_active(true); // include all entities and give them fake values
@@ -43,7 +49,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "general") == 0) {
if (cmd == "general") {
EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat");
// System::test_set_all_active(true); // uncomment if we want to show all entities and give them fake values
@@ -77,7 +83,7 @@ bool Test::run_test(const char * command, int8_t id) {
//
#ifdef EMSESP_STANDALONE
if (strcmp(command, "heat_exchange") == 0) {
if (cmd == "heat_exchange") {
EMSESP::logger().info("Testing heating exchange...");
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
@@ -89,7 +95,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "2thermostats") == 0) {
if (cmd == "2thermostats") {
EMSESP::logger().info("Testing with multiple thermostats...");
add_device(0x08, 123); // GB072
@@ -121,7 +127,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "310") == 0) {
if (cmd == "310") {
EMSESP::logger().info("Adding a GB072/RC310 combo...");
add_device(0x08, 123); // GB072
@@ -148,7 +154,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "gateway") == 0) {
if (cmd == "gateway") {
EMSESP::logger().info("Adding a Gateway...");
// add 0x48 KM200, via a version command
@@ -168,7 +174,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "mixer") == 0) {
if (cmd == "mixer") {
EMSESP::logger().info("Adding a mixer...");
// add controller
@@ -190,7 +196,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "boiler") == 0) {
if (cmd == "boiler") {
EMSESP::logger().info("Adding boiler...");
add_device(0x08, 123); // Nefit Trendline
@@ -207,7 +213,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "thermostat") == 0) {
if (cmd == "thermostat") {
EMSESP::logger().info("Adding thermostat...");
add_device(0x10, 192); // FW120
@@ -220,7 +226,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "solar") == 0) {
if (cmd == "solar") {
EMSESP::logger().info("Adding solar...");
add_device(0x30, 163); // SM100
@@ -239,7 +245,7 @@ bool Test::run_test(const char * command, int8_t id) {
return true;
}
if (strcmp(command, "heatpump") == 0) {
if (cmd == "heatpump") {
EMSESP::logger().info("Adding heatpump...");
add_device(0x38, 200); // Enviline module
@@ -258,11 +264,13 @@ bool Test::run_test(const char * command, int8_t id) {
}
// These next tests are run from the Consol via the test command, so inherit the Shell
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & data) {
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & id1_s, const std::string & id2_s) {
shell.add_flags(CommandFlags::ADMIN); // switch to su
// init stuff
Mqtt::ha_enabled(true);
Mqtt::entity_format(Mqtt::entityFormat::SINGLE_SHORT); // SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
// EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw mode
@@ -275,11 +283,32 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
command = cmd;
}
// extract params
int8_t id1 = -1;
int8_t id2 = -1;
if (!id1_s.empty()) {
if (id1_s[0] == '0' && id1_s[1] == 'x') {
id1 = Helpers::hextoint(id1_s.c_str());
} else {
id1 = Helpers::atoint(id1_s.c_str());
}
}
if (!id2_s.empty()) {
id2 = Helpers::atoint(id2_s.c_str());
}
bool ok = false;
if (command == "add") {
shell.printfln("Testing Adding a device (product_id %d), with all values...", id2);
test("add", id1, id2); // e.g. 8 172
shell.invoke_command("show values");
ok = true;
}
if (command == "general") {
shell.printfln("Testing adding a boiler, thermostat and sensors...");
run_test("general");
test("general");
// add sensors
emsesp::EMSESP::analogsensor_.test();
@@ -296,14 +325,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// https://github.com/emsesp/EMS-ESP32/issues/869
if (command == "memory") {
shell.printfln("Testing memory by adding lots of devices and entities...");
run_test("memory");
test("memory");
shell.invoke_command("show values");
ok = true;
}
if (command == "custom_entities") {
shell.printfln("custom entities...");
run_test("general");
test("general");
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
@@ -318,7 +347,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "coldshot") {
shell.printfln("Testing coldshot...");
run_test("general");
test("general");
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
@@ -370,7 +399,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "modes") {
shell.printfln("Testing thermostat modes...");
run_test("general");
test("general");
shell.invoke_command("call thermostat mode auto");
shell.invoke_command("call thermostat mode Manuell"); // DE
shell.invoke_command("call thermostat mode 1");
@@ -506,13 +535,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "gateway") {
shell.printfln("Testing Gateway...");
run_test("gateway");
test("gateway");
ok = true;
}
if (command == "310") {
shell.printfln("Testing RC310...");
run_test("310");
test("310");
shell.invoke_command("show devices");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
@@ -522,7 +551,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "heat_exchange") {
shell.printfln("Testing heat exchange...");
run_test("heat_exchange");
test("heat_exchange");
shell.invoke_command("show devices");
shell.invoke_command("show values");
ok = true;
@@ -530,7 +559,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "2thermostats") {
shell.printfln("Testing multiple thermostats...");
run_test("2thermostats");
test("2thermostats");
shell.invoke_command("show values");
shell.invoke_command("show devices");
ok = true;
@@ -542,8 +571,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::enabled(false); // turn off mqtt
Mqtt::ha_enabled(false); // turn off ha
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
JsonDocument doc; // some absurd high number
for (const auto & emsdevice : EMSESP::emsdevices) {
@@ -596,7 +625,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true);
Mqtt::nested_format(1);
run_test("boiler");
test("boiler");
shell.invoke_command("show devices");
shell.invoke_command("show values");
shell.invoke_command("call boiler info");
@@ -622,7 +651,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "shower_alert") {
shell.printfln("Testing Shower Alert...");
run_test("boiler");
test("boiler");
// device type, command, data
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
@@ -650,10 +679,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1); // is nested
// Mqtt::nested_format(2); // not nested
run_test("boiler");
run_test("thermostat");
run_test("solar");
run_test("mixer");
test("boiler");
test("thermostat");
test("solar");
test("mixer");
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");
@@ -672,8 +701,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1);
// Mqtt::send_response(false);
run_test("boiler");
// run_test("thermostat");
test("boiler");
// test("thermostat");
// 0xC2
// [emsesp] Boiler(0x08) -> Me(0x0B), UBAErrorMessage3(0xC2), data: 08 AC 00 10 31 48 30 31 15 80 95 0B 0E 10 38 00 7F FF FF FF 08 AC 00 10 09 41 30
@@ -692,8 +721,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1);
// Mqtt::send_response(false);
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
shell.invoke_command("call boiler wwseltemp");
shell.invoke_command("call system publish");
@@ -745,15 +774,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
}
if (command == "healthcheck") {
uint8_t n = 0;
if (!data.empty()) {
n = Helpers::atoint(data.c_str());
}
// n=1 = EMSESP::system_.HEALTHCHECK_NO_BUS
// n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK
shell.printfln("Testing healthcheck with %d", n);
EMSESP::system_.healthcheck(n);
if (id1 == -1) {
id1 = 0;
}
shell.printfln("Testing healthcheck with %d", id1);
EMSESP::system_.healthcheck(id1);
ok = true;
}
@@ -763,7 +790,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true);
// Mqtt::send_response(false);
run_test("thermostat");
test("thermostat");
// shell.invoke_command("call thermostat seltemp");
// shell.invoke_command("call system publish");
@@ -794,7 +821,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true);
// Mqtt::send_response(false);
run_test("boiler");
test("boiler");
shell.invoke_command("call boiler wwseltemp");
shell.invoke_command("call system publish");
@@ -819,7 +846,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::ha_enabled(true);
// Mqtt::send_response(false);
run_test("boiler");
test("boiler");
shell.invoke_command("call boiler wwseltemp");
shell.invoke_command("call system publish");
@@ -842,8 +869,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// EMSESP::bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR
EMSESP::system_.bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
AsyncWebServerRequest request;
JsonDocument doc;
@@ -875,8 +902,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
EMSESP::system_.bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR
// EMSESP::bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
ok = true;
@@ -887,15 +914,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing API wwmode");
Mqtt::ha_enabled(false);
Mqtt::nested_format(1);
run_test("310");
test("310");
AsyncWebServerRequest request;
request.method(HTTP_POST);
JsonDocument doc;
JsonVariant json;
char data[] = "{\"value\":\"off\"}";
deserializeJson(doc, data);
char odata[] = "{\"value\":\"off\"}";
deserializeJson(doc, odata);
json = doc.as<JsonVariant>();
request.url("/api/thermostat/wwmode");
EMSESP::webAPIService.webAPIService_post(&request, json);
@@ -911,8 +938,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1);
// Mqtt::send_response(true);
run_test("boiler");
run_test("thermostat");
test("boiler");
test("thermostat");
AsyncWebServerRequest requestX;
JsonDocument docX;
@@ -1181,10 +1208,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing nested MQTT");
Mqtt::ha_enabled(false); // turn off HA Discovery to stop the chatter
run_test("boiler");
run_test("thermostat");
run_test("solar");
run_test("mixer");
test("boiler");
test("thermostat");
test("solar");
test("mixer");
// first with nested
Mqtt::nested_format(1);
@@ -1201,7 +1228,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "thermostat") {
shell.printfln("Testing adding a thermostat FW120...");
run_test("thermostat");
test("thermostat");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
@@ -1229,7 +1256,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "solar") {
shell.printfln("Testing Solar");
run_test("solar");
test("solar");
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on (1)sh
EMSESP::show_device_values(shell);
@@ -1243,7 +1270,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "heatpump") {
shell.printfln("Testing Heat Pump");
run_test("heatpump");
test("heatpump");
shell.invoke_command("call");
shell.invoke_command("call heatpump info");
ok = true;
@@ -1698,7 +1725,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "mixer") {
shell.printfln("Testing Mixer...");
run_test("mixer");
test("mixer");
// check for error "No telegram type handler found for ID 0x255 (src 0x20)"
uart_telegram({0xA0, 0x00, 0xFF, 0x00, 0x01, 0x55, 0x00, 0x1A});

View File

@@ -22,6 +22,7 @@
#define EMSESP_TEST_H
#include "emsesp.h"
#include <ESPAsyncWebServer.h>
namespace emsesp {
@@ -60,8 +61,8 @@ namespace emsesp {
class Test {
public:
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & data = "");
static bool run_test(const char * command, int8_t id = 0);
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & id1 = "", const std::string & id2 = "");
static bool test(const std::string & command, int8_t id1 = -1, int8_t id2 = -1);
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);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.5-test.8"
#define EMSESP_APP_VERSION "3.6.5-test.10"

View File

@@ -53,7 +53,7 @@ void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
// For HTTP POSTS with an optional JSON body
// HTTP_POST | HTTP_PUT | HTTP_PATCH
// POST /{device}[/{hc|id}][/{name}]
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant json) {
// if no body then treat it as a secure GET
if (!json.is<JsonObject>()) {
webAPIService_get(request);
@@ -134,8 +134,8 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
// if we're returning single values, just sent as plain text
// https://github.com/emsesp/EMS-ESP32/issues/462#issuecomment-1093877210
if (output.containsKey("api_data")) {
JsonVariant data = output["api_data"];
request->send(200, "text/plain; charset=utf-8", data.as<String>());
String data = output["api_data"].as<String>();
request->send(200, "text/plain; charset=utf-8", data);
api_count_++;
delete response;
return;

View File

@@ -31,8 +31,8 @@ class WebAPIService {
public:
WebAPIService(AsyncWebServer * server, SecurityManager * securityManager);
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json); // for POSTs
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant json); // for POSTs
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
static uint32_t api_count() {
return api_count_;

View File

@@ -48,6 +48,7 @@ void WebCustomEntity::read(WebCustomEntity & webEntity, JsonObject root) {
for (const CustomEntityItem & entityItem : webEntity.customEntityItems) {
JsonObject ei = entity.add<JsonObject>();
ei["id"] = counter++; // id is only used to render the table and must be unique
ei["ram"] = entityItem.ram;
ei["device_id"] = entityItem.device_id;
ei["type_id"] = entityItem.type_id;
ei["offset"] = entityItem.offset;
@@ -73,7 +74,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
JsonDocument doc;
deserializeJson(doc, json);
root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA);
Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake custom entity file: ");
serializeJson(root, Serial);
Serial.println(COLOR_RESET);
@@ -88,6 +89,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
if (root["entities"].is<JsonArray>()) {
for (const JsonObject ei : root["entities"].as<JsonArray>()) {
auto entityItem = CustomEntityItem();
entityItem.ram = ei["ram"] | 0;
entityItem.device_id = ei["device_id"]; // send as numeric, will be converted to string in web
entityItem.type_id = ei["type_id"];
entityItem.offset = ei["offset"];
@@ -96,6 +98,14 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
entityItem.uom = ei["uom"];
entityItem.value_type = ei["value_type"];
entityItem.writeable = ei["writeable"];
entityItem.data = ei["value"].as<std::string>();
if (entityItem.ram == 1) {
entityItem.device_id = 0;
entityItem.type_id = 0;
entityItem.uom = 0;
entityItem.value_type = DeviceValueType::STRING;
entityItem.writeable = true;
}
if (entityItem.value_type == DeviceValueType::BOOL) {
entityItem.value = EMS_VALUE_DEFAULT_BOOL;
@@ -107,7 +117,7 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
entityItem.value = EMS_VALUE_DEFAULT_SHORT;
} else if (entityItem.value_type == DeviceValueType::USHORT) {
entityItem.value = EMS_VALUE_DEFAULT_USHORT;
} else { // if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) {
} else if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) {
entityItem.value = EMS_VALUE_DEFAULT_ULONG;
}
if (entityItem.factor == 0) {
@@ -134,7 +144,9 @@ bool WebCustomEntityService::command_setvalue(const char * value, const std::str
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
for (CustomEntityItem & entityItem : *customEntityItems) {
if (Helpers::toLower(entityItem.name) == Helpers::toLower(name)) {
if (entityItem.value_type == DeviceValueType::STRING) {
if (entityItem.ram == 1) {
entityItem.data = value;
} else if (entityItem.value_type == DeviceValueType::STRING) {
char telegram[84];
strlcpy(telegram, value, sizeof(telegram));
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
@@ -274,7 +286,7 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
for (const CustomEntityItem & entity : *customEntityItems) {
render_value(output, entity);
}
return (output.size() != 0);
return true;
}
char command_s[30];
@@ -297,18 +309,20 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
output["readable"] = true;
output["writeable"] = entity.writeable;
output["visible"] = true;
output["device_id"] = Helpers::hextoa(entity.device_id);
output["type_id"] = Helpers::hextoa(entity.type_id);
output["offset"] = entity.offset;
if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) {
output["factor"] = entity.factor;
} else if (entity.value_type == DeviceValueType::STRING) {
output["bytes"] = (uint8_t)entity.factor;
if (entity.ram == 0) {
output["device_id"] = Helpers::hextoa(entity.device_id);
output["type_id"] = Helpers::hextoa(entity.type_id);
output["offset"] = entity.offset;
if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) {
output["factor"] = entity.factor;
} else if (entity.value_type == DeviceValueType::STRING) {
output["bytes"] = (uint8_t)entity.factor;
}
}
render_value(output, entity, true);
if (attribute_s) {
if (output.containsKey(attribute_s)) {
JsonVariant data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
return true;
@@ -547,10 +561,21 @@ void WebCustomEntityService::fetch() {
EMSESP::webCustomEntityService.read([&](WebCustomEntity & webEntity) { customEntityItems = &webEntity.customEntityItems; });
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
for (auto & entity : *customEntityItems) {
EMSESP::send_read_request(entity.type_id,
entity.device_id,
entity.offset,
entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]);
if (entity.device_id > 0 && entity.type_id > 0) { // ths excludes also RAM type
bool needFetch = true;
for (const auto & emsdevice : EMSESP::emsdevices) {
if (entity.value_type != DeviceValueType::STRING && emsdevice->is_device_id(entity.device_id) && emsdevice->is_fetch(entity.type_id)) {
needFetch = false;
break;
}
}
if (needFetch) {
EMSESP::send_read_request(entity.type_id,
entity.device_id,
entity.offset,
entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]);
}
}
}
// EMSESP::logger().debug("fetch custom entities");
}
@@ -563,8 +588,8 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
for (auto & entity : *customEntityItems) {
if (entity.value_type == DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
&& telegram->offset == entity.offset) {
auto data = Helpers::data_to_hex(telegram->message_data, telegram->message_length);
&& telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + (uint8_t)entity.factor)) {
auto data = Helpers::data_to_hex(telegram->message_data, (uint8_t)entity.factor);
if (entity.data != data) {
entity.data = data;
if (Mqtt::publish_single()) {
@@ -573,9 +598,8 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
has_change = true;
}
}
}
if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
&& telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) {
} else if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
&& telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) {
uint32_t value = 0;
for (uint8_t i = 0; i < len[entity.value_type]; i++) {
value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset];

View File

@@ -21,7 +21,7 @@
#define WebCustomEntityService_h
#define EMSESP_CUSTOMENTITY_FILE "/config/emsespEntity.json"
#define EMSESP_CUSTOMENTITY_SERVICE_PATH "/rest/customentities" // GET and POST
#define EMSESP_CUSTOMENTITY_SERVICE_PATH "/rest/customEntities" // GET and POST
namespace emsesp {
@@ -38,6 +38,7 @@ class CustomEntityItem {
bool writeable;
uint32_t value;
std::string data;
uint8_t ram;
};
class WebCustomEntity {

View File

@@ -25,14 +25,7 @@ using namespace std::placeholders; // for `_1` etc
bool WebCustomization::_start = true;
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(WebCustomization::read,
WebCustomization::update,
this,
server,
EMSESP_CUSTOMIZATION_SERVICE_PATH,
securityManager,
AuthenticationPredicates::IS_AUTHENTICATED)
, _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
: _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
, _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH,
securityManager->wrapCallback(std::bind(&WebCustomizationService::customization_entities, this, _1, _2),
AuthenticationPredicates::IS_AUTHENTICATED)) {
@@ -85,7 +78,7 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
entityJson["product_id"] = entityCustomization.product_id;
entityJson["device_id"] = entityCustomization.device_id;
// entries are in the form <XX><shortname>[|optional customname] e.g "08heatingactive|heating is on"
// entries are in the form <XX><shortname>[optional customname] e.g "08heatingactive|heating is on"
JsonArray masked_entityJson = entityJson["entity_ids"].to<JsonArray>();
for (std::string entity_id : entityCustomization.entity_ids) {
masked_entityJson.add(entity_id);
@@ -103,7 +96,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
JsonDocument doc;
deserializeJson(doc, json);
root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA);
Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake customization file: ");
serializeJson(root, Serial);
Serial.println(COLOR_RESET);
@@ -239,7 +232,7 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
// takes a list of updated entities with new masks from the web UI
// saves it in the customization service
// and updates the entity list real-time
void WebCustomizationService::customization_entities(AsyncWebServerRequest * request, JsonVariant & json) {
void WebCustomizationService::customization_entities(AsyncWebServerRequest * request, JsonVariant json) {
bool need_reboot = false;
if (json.is<JsonObject>()) {
// find the device using the unique_id

View File

@@ -23,7 +23,6 @@
// GET
#define DEVICES_SERVICE_PATH "/rest/devices"
#define EMSESP_CUSTOMIZATION_SERVICE_PATH "/rest/customization"
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
// POST
@@ -89,7 +88,6 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
private:
#endif
HttpEndpoint<WebCustomization> _httpEndpoint;
FSPersistence<WebCustomization> _fsPersistence;
// GET
@@ -97,7 +95,7 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
void device_entities(AsyncWebServerRequest * request);
// POST
void customization_entities(AsyncWebServerRequest * request, JsonVariant & json);
void customization_entities(AsyncWebServerRequest * request, JsonVariant json);
void reset_customization(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _masked_entities_handler;

View File

@@ -227,7 +227,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request) {
}
// assumes the service has been checked for admin authentication
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant & json) {
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) {
uint8_t unique_id = json["id"]; // unique ID
const char * cmd = json["c"]; // the command
@@ -323,7 +323,7 @@ void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVar
// takes a temperaturesensor name and optional offset from the WebUI and update the customization settings
// via the temperaturesensor service
void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json) {
void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json) {
bool ok = false;
if (json.is<JsonObject>()) {
JsonObject sensor = json;
@@ -346,7 +346,7 @@ void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, J
}
// update the analog record, or create a new one
void WebDataService::write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json) {
void WebDataService::write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json) {
bool ok = false;
if (json.is<JsonObject>()) {
JsonObject analog = json;

View File

@@ -47,9 +47,9 @@ class WebDataService {
void device_data(AsyncWebServerRequest * request);
// POST
void write_device_value(AsyncWebServerRequest * request, JsonVariant & json);
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json);
void write_device_value(AsyncWebServerRequest * request, JsonVariant json);
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json);
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json);
void scan_devices(AsyncWebServerRequest * request); // command
AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler;

View File

@@ -211,7 +211,7 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) {
}
// sets the values like level after a POST
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) {
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant json) {
if (!json.is<JsonObject>()) {
return;
}

View File

@@ -64,7 +64,7 @@ class WebLogService : public uuid::log::Handler {
char * messagetime(char * out, const uint64_t t, const size_t bufsize);
void setValues(AsyncWebServerRequest * request, JsonVariant & json);
void setValues(AsyncWebServerRequest * request, JsonVariant json);
AsyncCallbackJsonWebHandler setValues_; // for POSTs

View File

@@ -62,7 +62,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
JsonDocument doc;
deserializeJson(doc, json);
root = doc.as<JsonObject>();
Serial.println(COLOR_BRIGHT_MAGENTA);
Serial.print(COLOR_BRIGHT_MAGENTA);
Serial.print(" Using fake scheduler file: ");
serializeJson(root, Serial);
Serial.println(COLOR_RESET);
@@ -177,7 +177,6 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
attribute_s = breakp + 1;
}
JsonVariant data;
for (const ScheduleItem & scheduleItem : *scheduleItems) {
if (Helpers::toLower(scheduleItem.name) == Helpers::toLower(command_s)) {
output["name"] = scheduleItem.name;
@@ -199,7 +198,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
}
if (attribute_s && output.containsKey(attribute_s)) {
data = output[attribute_s];
String data = output[attribute_s].as<String>();
output.clear();
output["api_data"] = data;
}