This commit is contained in:
MichaelDvP
2023-10-18 07:11:11 +02:00
59 changed files with 5850 additions and 5192 deletions

View File

@@ -49,7 +49,7 @@
{208, DeviceType::BOILER, "Logamax Plus/GB192/Condens GC9000/Greenstar ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{219, DeviceType::BOILER, "Greenstar HIU", DeviceFlags::EMS_DEVICE_FLAG_HIU},
{219, DeviceType::BOILER, "Greenstar HIU/Logamax kompakt WS170", DeviceFlags::EMS_DEVICE_FLAG_HIU},
{234, DeviceType::BOILER, "Logamax Plus GB122/Condense 2300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Controllers - 0x09 / 0x10 / 0x50

View File

@@ -941,14 +941,15 @@ void EMSdevice::generate_values_web(JsonObject & output) {
}
}
// handle INTs
// add min and max values and steps, as integer values
else {
// add step if it's not 1
if (dv.numeric_operator > 0) {
obj["s"] = (float)1 / dv.numeric_operator;
} else if (dv.numeric_operator < 0) {
obj["s"] = (float)(-1) * dv.numeric_operator;
}
// add min and max values, if available
int16_t dv_set_min;
uint32_t dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {

View File

@@ -716,7 +716,6 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
uint8_t offset = telegram->offset;
// find name for src and dest by looking up known devices
std::string src_name("");
std::string dest_name("");
std::string type_name("");

View File

@@ -205,11 +205,11 @@ MAKE_NOTRANSLATION(L3, "L3")
MAKE_NOTRANSLATION(L4, "L4")
// templates - this are not translated and will be saved under options_single
MAKE_NOTRANSLATION(tpl_datetime, "< NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
MAKE_NOTRANSLATION(tpl_datetime, "NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1)")
MAKE_NOTRANSLATION(tpl_switchtime, "<nn> [ not_set | day hh:mm on|off ]")
MAKE_NOTRANSLATION(tpl_switchtime1, "<nn> [ not_set | day hh:mm Tn ]")
MAKE_NOTRANSLATION(tpl_holidays, "< dd.mm.yyyy-dd.mm.yyyy >")
MAKE_NOTRANSLATION(tpl_date, "< dd.mm.yyyy >")
MAKE_NOTRANSLATION(tpl_holidays, "dd.mm.yyyy-dd.mm.yyyy")
MAKE_NOTRANSLATION(tpl_date, "dd.mm.yyyy")
MAKE_NOTRANSLATION(tpl_input, "<inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
MAKE_NOTRANSLATION(tpl_input4, "<inv>[<comp><aux><cool><heat><dhw><pv>]")

View File

@@ -532,7 +532,7 @@ void Mqtt::ha_status() {
doc["obj_id"] = uniq;
doc["stat_t"] = mqtt_basename_ + "/status";
doc["name"] = "system status";
doc["name"] = "System status";
doc["pl_on"] = "online";
doc["pl_off"] = "offline";
doc["stat_cla"] = "measurement";
@@ -855,14 +855,11 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
case DeviceValueType::UINT:
case DeviceValueType::SHORT:
case DeviceValueType::USHORT:
// case DeviceValueType::ULONG:
if (discovery_type() == discoveryType::HOMEASSISTANT) {
// Home Assistant
// number - https://www.home-assistant.io/integrations/number.mqtt
snprintf(topic, sizeof(topic), "number/%s", config_topic);
} else {
// Domoticz
// Does not support number, use sensor
// Domoticz does not support number, use sensor
snprintf(topic, sizeof(topic), "sensor/%s", config_topic);
}
break;
@@ -1126,9 +1123,8 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
}
// add category "diagnostic" for system entities
if (device_type == EMSdevice::DeviceType::SYSTEM) {
doc["ent_cat"] = "diagnostic";
}
// config for writeable entities, like switches. diagnostic for read only sensors.
doc["ent_cat"] = (has_cmd) ? "config" : "diagnostic";
// add the dev json object to the end
doc["dev"] = dev_json;

View File

@@ -101,15 +101,16 @@ void Shower::loop() {
if (duration_ > SHOWER_MIN_DURATION) {
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
// char s[50];
// snprintf(s, 50, "%02u:%02u:%02u", (uint8_t)(duration_ / 3600000UL), (uint8_t)(duration_ / 60000UL), (uint8_t)((duration_ / 1000UL) % 60));
// duration in seconds
doc["duration"] = (duration_ / 1000UL); // seconds
time_t now = time(nullptr);
time_t now = time(nullptr);
// if NTP enabled, publish timestamp
if (now > 1576800000) { // year 2020
tm * tm_ = localtime(&now);
// doc["timestamp_s"] = now; // if needed, in seconds
tm * tm_ = localtime(&now);
char dt[25];
strftime(dt, sizeof(dt), "%FT%T%z", tm_);
doc["time"] = dt;
doc["timestamp"] = dt;
}
Mqtt::queue_publish("shower_data", doc.as<JsonObject>());
LOG_INFO("finished with duration %lu seconds", duration_ / 1000UL);
@@ -180,7 +181,7 @@ void Shower::set_shower_state(bool state, bool force) {
char stat_t[50];
//
// shower_active topic
// shower active
//
doc["name"] = "Shower Active";
@@ -217,7 +218,7 @@ void Shower::set_shower_state(bool state, bool force) {
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
//
// shower_duaration topic
// shower duaration
//
doc.clear();
@@ -244,6 +245,32 @@ void Shower::set_shower_state(bool state, bool force) {
snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str());
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
//
// shower timestamp
//
doc.clear();
snprintf(str, sizeof(str), "%s_shower_timestamp", Mqtt::basename().c_str());
doc["uniq_id"] = str;
doc["object_id"] = str;
snprintf(stat_t, sizeof(stat_t), "%s/shower_data", Mqtt::basename().c_str());
doc["stat_t"] = stat_t;
doc["name"] = "Shower Timestamp";
doc["val_tpl"] = "{{value_json.timestamp if value_json.timestamp is defined else 0}}";
doc["ent_cat"] = "diagnostic";
JsonObject dev3 = doc.createNestedObject("dev");
JsonArray ids3 = dev3.createNestedArray("ids");
ids3.add(Mqtt::basename());
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>(), "value_json.timestamp is defined"); // add "availability" section
snprintf(topic, sizeof(topic), "sensor/%s/shower_timestamp/config", Mqtt::basename().c_str());
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
}
}

View File

@@ -38,7 +38,9 @@ class Shower {
static constexpr uint32_t SHOWER_PAUSE_TIME = 15000; // in ms. 15 seconds, max time if water is switched off & on during a shower
static constexpr uint32_t SHOWER_MIN_DURATION = 120000; // in ms. 2 minutes, before recognizing its a shower
static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower
// static constexpr uint32_t SHOWER_MIN_DURATION = 5000; // for testing in ms. 5 seconds
static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower
void shower_alert_start();
void shower_alert_stop();

View File

@@ -113,7 +113,8 @@ class Telegram {
return (val != value);
}
// read a value from a telegram. We always store the value, regardless if its garbage
// read a value from a telegram if its not out of bounds.
// Then we update the value, regardless if its garbage
template <typename Value>
// assuming negative numbers are stored as 2's-complement
// https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c
@@ -124,6 +125,20 @@ class Telegram {
uint8_t num_bytes = (!s) ? sizeof(Value) : s;
// check for out of bounds, if so don't modify the value
auto msg_size = (index - this->offset + num_bytes - 1);
#ifdef EMSESP_DEBUG
// Serial.print(" index: ");
// Serial.print(index);
// Serial.print(" offset: ");
// Serial.print(offset);
// Serial.print(" index: ");
// Serial.print(" message_length: ");
// Serial.print(this->message_length);
// Serial.print(" msg_size: ");
// Serial.print(msg_size);
// Serial.println();
#endif
if ((index < this->offset) || (msg_size >= this->message_length) || (msg_size > EMS_MAX_TELEGRAM_MESSAGE_LENGTH)) {
return false;
}

View File

@@ -576,7 +576,7 @@ TemperatureSensor::Sensor::Sensor(const uint8_t addr[])
(unsigned int)(internal_id_ >> 48) & 0xFF,
(unsigned int)(internal_id_ >> 32) & 0xFFFF,
(unsigned int)(internal_id_ >> 16) & 0xFFFF,
(unsigned int)(internal_id_)&0xFFFF);
(unsigned int)(internal_id_) & 0xFFFF);
id_ = std::string(id_s);
name_ = std::string{}; // name (alias) is empty
offset_ = 0; // 0 degrees offset

View File

@@ -74,6 +74,18 @@ bool Test::run_test(const char * command, int8_t id) {
//
#ifdef EMSESP_STANDALONE
if (strcmp(command, "heat_exchange") == 0) {
EMSESP::logger().info("Testing heating exchange...");
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
// [emsesp] boiler(0x08) -W-> Me(0x0B), UBAMonitorFastPlus(0xE4), data: 00 01 35 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00 80 00 (offset 6)
uart_telegram({0x08, 0x00, 0xE4, 0x00, //
00, 01, 0x35, 00, 00, 00, 00, 00, 00, 00, 00, 0x80, 00, 00, 00, 00, 00, 00, 00, 0x80, 00});
return true;
}
if (strcmp(command, "2thermostats") == 0) {
EMSESP::logger().info("Testing with multiple thermostats...");
@@ -493,16 +505,24 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing RC310...");
run_test("310");
shell.invoke_command("show devices");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");
ok = true;
}
if (command == "heat_exchange") {
shell.printfln("Testing heat exchange...");
run_test("heat_exchange");
shell.invoke_command("show devices");
shell.invoke_command("show values");
ok = true;
}
if (command == "2thermostats") {
shell.printfln("Testing multiple thermostats...");
run_test("2thermostats");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("show devices");
ok = true;
}
@@ -571,7 +591,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
run_test("boiler");
shell.invoke_command("show devices");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call boiler info");
shell.invoke_command("call system publish");
@@ -610,7 +630,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// HC1
uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x01, 0x02, 0x00, 0xCF, 0x00, 0xE6});
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("show devices");
ok = true;
}
@@ -653,7 +673,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
uart_telegram(
{0x08, 0x0B, 0xC2, 0, 0x08, 0xAC, 00, 0x10, 0x31, 0x48, 0x30, 0x31, 0x15, 0x80, 0x95, 0x0B, 0x0E, 0x10, 0x38, 00, 0x7F, 0xFF, 0xFF, 0xFF});
// shell.invoke_command("show");
// shell.invoke_command("show values");
ok = true;
}
@@ -686,12 +706,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// Mqtt::nested_format(0);
emsesp::EMSESP::temperaturesensor_.test();
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
// rename
EMSESP::temperaturesensor_.update("01-0203-0405-0607", "testtemperature", 2);
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
ok = true;
}
@@ -704,14 +724,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// Mqtt::nested_format(0);
emsesp::EMSESP::analogsensor_.test();
shell.invoke_command("show");
shell.invoke_command("show values");
// shell.invoke_command("call system publish");
// shell.invoke_command("show mqtt");
// rename
// bool update(uint8_t id, const std::string & name, int16_t offset, float factor, uint8_t uom, uint8_t type);
EMSESP::analogsensor_.update(36, "analogtest", 2, 0.7, 17, 1);
shell.invoke_command("show");
shell.invoke_command("show values");
// shell.invoke_command("call system publish");
ok = true;
}
@@ -1174,7 +1194,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing adding a thermostat FW120...");
run_test("thermostat");
shell.invoke_command("show");
shell.invoke_command("show values");
shell.invoke_command("call system publish");
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc1", "heat");
@@ -1243,7 +1263,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
shell.invoke_command("show");
shell.invoke_command("show values");
ok = true;
}
@@ -1676,7 +1696,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// 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("show values");
shell.invoke_command("call mixer info");
shell.invoke_command("call system publish");
shell.invoke_command("show mqtt");

View File

@@ -56,6 +56,8 @@ namespace emsesp {
// #define EMSESP_DEBUG_DEFAULT "memory"
// #define EMSESP_DEBUG_DEFAULT "coldshot"
// #define EMSESP_DEBUG_DEFAULT "custom_entities"
// #define EMSESP_DEBUG_DEFAULT "heat_exchange"
class Test {
public:

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.6.3-dev.2i"
#define EMSESP_APP_VERSION "3.6.3-dev.3a"