This commit is contained in:
proddy
2024-11-29 10:30:05 +01:00
161 changed files with 15515 additions and 12248 deletions

View File

@@ -138,6 +138,20 @@ void AnalogSensor::reload(bool get_nvs) {
continue; // skip this loop pass
}
if ((sensor.gpio() == 25 || sensor.gpio() == 26)
&& (sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::RATE
|| sensor.type() == AnalogType::TIMER)) {
// pullup is mapped to DAC, so set to 3.3V
#if CONFIG_IDF_TARGET_ESP32
if (sensor.gpio() == 25 || sensor.gpio() == 26) {
dacWrite(sensor.gpio(), 255);
}
#elif CONFIG_IDF_TARGET_ESP32S2
if (sensor.gpio() == 17 || sensor.gpio() == 18) {
dacWrite(sensor.gpio(), 255);
}
#endif
}
if (sensor.type() == AnalogType::ADC) {
LOG_DEBUG("ADC Sensor on GPIO %02d", sensor.gpio());
// analogSetPinAttenuation does not work with analogReadMilliVolts
@@ -146,15 +160,6 @@ void AnalogSensor::reload(bool get_nvs) {
} else if (sensor.type() == AnalogType::COUNTER) {
LOG_DEBUG("I/O Counter on GPIO %02d", sensor.gpio());
pinMode(sensor.gpio(), INPUT_PULLUP);
#if CONFIG_IDF_TARGET_ESP32
if (sensor.gpio() == 25 || sensor.gpio() == 26) {
dacWrite(sensor.gpio(), 255);
}
#elif CONFIG_IDF_TARGET_ESP32S2
if (sensor.gpio() == 23 || sensor.gpio() == 24) {
dacWrite(sensor.gpio(), 255);
}
#endif
sensor.polltime_ = 0;
sensor.poll_ = digitalRead(sensor.gpio());
if (double_t val = EMSESP::nvs_.getDouble(sensor.name().c_str(), 0)) {
@@ -192,7 +197,7 @@ void AnalogSensor::reload(bool get_nvs) {
sensor.set_value(sensor.offset());
} else
#elif CONFIG_IDF_TARGET_ESP32S2
if (sensor.gpio() == 23 || sensor.gpio() == 24) {
if (sensor.gpio() == 17 || sensor.gpio() == 18) {
if (sensor.offset() > 255) {
sensor.set_offset(255);
} else if (sensor.offset() < 0) {
@@ -533,7 +538,7 @@ void AnalogSensor::publish_values(const bool force) {
char val_obj[50];
char val_cond[95];
if (Mqtt::is_nested()) {
snprintf(val_obj, sizeof(val_obj), "value_json['%02d'].value", sensor.gpio());
snprintf(val_obj, sizeof(val_obj), "value_json['%02d']['value']", sensor.gpio());
snprintf(val_cond, sizeof(val_cond), "value_json['%02d'] is defined and %s is defined", sensor.gpio(), val_obj);
} else {
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
@@ -745,7 +750,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
dacWrite(sensor.gpio(), sensor.offset());
} else
#elif CONFIG_IDF_TARGET_ESP32S2
if ((sensor.gpio() == 23 || sensor.gpio() == 24) && v <= 255) {
if ((sensor.gpio() == 17 || sensor.gpio() == 18) && v <= 255) {
sensor.set_offset(v);
sensor.set_value(v);
pinMode(sensor.gpio(), OUTPUT);

View File

@@ -379,17 +379,6 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
return CommandRet::NOT_ALLOWED; // command not allowed
}
// build up the log string for reporting back
// We send the log message as Warning so it appears in the log (debug is only enabled when compiling with DEBUG)
std::string ro = EMSESP::system_.readonly_mode() ? "[readonly] " : "";
auto description = Helpers::translated_word(cf->description_);
char info_s[100];
if (strlen(description)) {
snprintf(info_s, sizeof(info_s), "%s/%s (%s)", dname, cmd, description);
} else {
snprintf(info_s, sizeof(info_s), "%s/%s", dname, cmd);
}
// call the function based on command function type
// commands return true or false only (bool)
uint8_t return_code = CommandRet::OK;
@@ -406,7 +395,7 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
}
}
// report back. If not OK show output from error, otherwise return the HTTP code
// report back. If not OK show output from error, otherwise return the error code
if (return_code != CommandRet::OK) {
char error[100];
if (single_command) {
@@ -418,6 +407,16 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
output["message"] = error;
LOG_WARNING(error);
} else {
// build up the log string for reporting back
// We send the log message as Warning so it appears in the log (debug is only enabled when compiling with DEBUG)
std::string ro = EMSESP::system_.readonly_mode() ? "[readonly] " : "";
auto description = Helpers::translated_word(cf->description_);
char info_s[100];
if (strlen(description)) {
snprintf(info_s, sizeof(info_s), "%s/%s (%s)", dname, cmd, description);
} else {
snprintf(info_s, sizeof(info_s), "%s/%s", dname, cmd);
}
if (single_command) {
// log as DEBUG (TRACE) regardless if compiled with EMSESP_DEBUG
logger_.debug("%sCalled command %s", ro.c_str(), info_s);

View File

@@ -52,13 +52,19 @@ using string_vector = std::vector<const char *>;
#define F_(string_name) (__pstr__##string_name)
#define FL_(list_name) (__pstr__L_##list_name)
#if defined(EMSESP_TEST) || defined(EMSESP_EN_ONLY)
// In testing just take one language (en) to save on Flash space
// The language settings below must match system.cpp
#if defined(EMSESP_TEST)
// in Test mode use two languages (en & de) to save flash memory needed for the tests
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
#elif defined(EMSESP_EN_ONLY)
// EN only
#define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr};
#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
#elif defined(EMSESP_DE_ONLY)
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {de, nullptr};
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, de, nullptr};
// EN + DE
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
#else
#define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};

View File

@@ -137,7 +137,7 @@
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC and 0x11 for the MP100
{ 69, DeviceType::MIXER, "MM10", DeviceFlags::EMS_DEVICE_FLAG_MM10},
{100, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{102, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{102, DeviceType::MIXER, "IPM2", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{159, DeviceType::MIXER, "MM50", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{160, DeviceType::MIXER, "MM100", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::MIXER, "MM200", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
@@ -180,10 +180,23 @@
{ 74, DeviceType::ALERT, "EM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Gateways - 0x48
{17, DeviceType::GATEWAY, "MX400", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48 and 0x4B
{189, DeviceType::GATEWAY, "KM200, MB LAN 2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{252, DeviceType::GATEWAY, "K30RF, MX300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Generic - 0x40 or other with no product-id and no version
{0, DeviceType::GENERIC, "unknown", DeviceFlags::EMS_DEVICE_FLAG_NONE}
#if defined(EMSESP_STANDALONE)
,
{100, DeviceType::WATER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{102, DeviceType::WATER, "IPM2", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{160, DeviceType::WATER, "MM100", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::WATER, "MM200", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{163, DeviceType::WATER, "SM100, MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::WATER, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{248, DeviceType::MIXER, "HM210", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{157, DeviceType::THERMOSTAT, "RC120", DeviceFlags::EMS_DEVICE_FLAG_CR120}
#endif
// clang-format on

View File

@@ -520,7 +520,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &totalCompStarts_, DeviceValueType::UINT24, FL_(totalCompStarts), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingStarts_, DeviceValueType::UINT24, FL_(heatingStarts), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &coolingStarts_, DeviceValueType::UINT24, FL_(coolingStarts), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DHW1, &wwStarts2_, DeviceValueType::UINT24, FL_(wwStarts2), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DHW1, &wwStartsHp_, DeviceValueType::UINT24, FL_(wwStartsHp), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &poolStarts_, DeviceValueType::UINT24, FL_(poolStarts), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nrgConsTotal_, DeviceValueType::UINT24, FL_(nrgConsTotal), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &nrgConsCompTotal_, DeviceValueType::UINT24, FL_(nrgConsCompTotal), DeviceValueUOM::KWH);
@@ -555,6 +555,15 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(pvMaxComp),
DeviceValueUOM::KW,
MAKE_CF_CB(set_pvMaxComp));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&powerReduction_,
DeviceValueType::UINT8,
DeviceValueNumOp::DV_NUMOP_MUL10,
FL_(powerReduction),
DeviceValueUOM::PERCENT,
MAKE_CF_CB(set_powerReduction),
30,
60);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpSetDiffPress_,
DeviceValueType::UINT8,
@@ -675,7 +684,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(auxHeaterOff),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_additionalHeater));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &auxHeaterStatus_, DeviceValueType::UINT8, FL_(auxHeaterStatus), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &auxHeaterStatus_, DeviceValueType::BOOL, FL_(auxHeaterStatus), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &auxHeaterLevel_, DeviceValueType::UINT8, FL_(auxHeaterLevel), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&auxHeaterDelay_,
DeviceValueType::UINT16,
@@ -1425,7 +1435,7 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
// at this point do a quick check to see if the hot water or heating is active
uint8_t state = EMS_VALUE_UINT8_NOTSET;
if (telegram->read_value(state, 11) && model() != EMSdevice::EMS_DEVICE_FLAG_HIU) {
if (telegram->read_value(state, 11) && model() != EMSdevice::EMS_DEVICE_FLAG_HIU && model() != EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) {
boilerState_ = state & 0x01 ? 0x08 : 0; // burnGas
boilerState_ |= state & 0x02 ? 0x01 : 0; // heatingPump
boilerState_ |= state & 0x04 ? 0x02 : 0; // 3-way-valve
@@ -1464,7 +1474,7 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
*/
void Boiler::process_UBAMonitorSlowPlus2(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, absBurnPow_, 13); // current burner absolute power (percent of rating plate power)
if (model() == EMSdevice::EMS_DEVICE_FLAG_HIU) {
if (model() == EMSdevice::EMS_DEVICE_FLAG_HIU || model() == EMSdevice::EMS_DEVICE_FLAG_HEATPUMP) {
uint8_t state = EMS_VALUE_UINT8_NOTSET;
boilerState_ = 0;
if (telegram->read_value(state, 2)) {
@@ -1605,7 +1615,7 @@ void Boiler::process_UBAInformation(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, totalCompStarts_, 20);
has_update(telegram, heatingStarts_, 28);
has_update(telegram, coolingStarts_, 36);
has_update(telegram, wwStarts2_, 24);
has_update(telegram, wwStartsHp_, 24);
has_update(telegram, poolStarts_, 32);
has_update(telegram, nrgConsTotal_, 64);
@@ -1652,7 +1662,7 @@ void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram) {
has_bitupdate(telegram, hpEA0_, 3, 6);
has_update(telegram, hpCircSpd_, 4);
has_update(telegram, hpBrinePumpSpd_, 5);
has_update(telegram, auxHeaterStatus_, 6);
has_update(telegram, auxHeaterLevel_, 6);
has_update(telegram, hpActivity_, 7);
has_update(telegram, hpPower_, 11);
has_update(telegram, hpCompSpd_, 17);
@@ -1819,6 +1829,10 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
uint8_t min = telegram->message_data[8];
uint16_t duration = telegram->message_data[9] * 256 + telegram->message_data[10];
uint32_t date = (year - 2000) * 535680UL + month * 44640UL + day * 1440UL + hour * 60 + min + duration;
// check valid https://github.com/emsesp/EMS-ESP32/issues/2189
if (day == 0 || day > 31 || month == 0 || month > 12 || !std::isprint(code[0]) || !std::isprint(code[1])) {
return;
}
// store only the newest code from telegrams 10 and 11
if (date > lastCodeDate_ && lastCodeDate_) {
lastCodeDate_ = date;
@@ -1846,6 +1860,9 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
code[2] = telegram->message_data[7];
code[3] = 0;
telegram->read_value(codeNo, 8);
if (!std::isprint(code[0]) || !std::isprint(code[1]) || !std::isprint(code[2])) {
return;
}
// check for valid date, https://github.com/emsesp/EMS-ESP32/issues/204
if (telegram->message_data[10] & 0x80) {
@@ -1937,15 +1954,16 @@ void Boiler::process_HpSilentMode(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpHystPool_, 33); // is / 5
has_update(telegram, hpCircPumpWw_, 46);
has_update(telegram, hpMaxPower_, 31);
has_update(telegram, silentFrom_, 52); // in steps of 15 min
has_update(telegram, silentTo_, 53); // in steps of 15 min
has_update(telegram, pvMaxComp_, 54); // #2062
has_update(telegram, hpshutdown_, 58); // 1 powers off
has_update(telegram, silentFrom_, 52); // in steps of 15 min
has_update(telegram, silentTo_, 53); // in steps of 15 min
has_update(telegram, pvMaxComp_, 54); // #2062
has_update(telegram, hpshutdown_, 58); // 1 powers off
has_update(telegram, powerReduction_, 64); // 3..6 -> is *10
}
// Boiler(0x08) -B-> All(0x00), ?(0x0488), data: 8E 00 00 00 00 00 01 03
void Boiler::process_HpValve(std::shared_ptr<const Telegram> telegram) {
// has_bitupdate(telegram, auxHeaterStatus_, 0, 2);
has_bitupdate(telegram, auxHeaterStatus_, 0, 2);
has_update(telegram, auxHeatMixValve_, 7);
has_update(telegram, pc1Rate_, 13); // percent
}
@@ -2960,19 +2978,19 @@ bool Boiler::set_silentMode(const char * value, const int8_t id) {
}
bool Boiler::set_silentFrom(const char * value, const int8_t id) {
int v;
if (!Helpers::value2number(value, v)) {
if (value == nullptr || value[0] < '0' || value[0] > '9') {
return false;
}
auto v = Helpers::string2minutes(value);
write_command(0x484, 52, v / 15, 0x484);
return true;
}
bool Boiler::set_silentTo(const char * value, const int8_t id) {
int v;
if (!Helpers::value2number(value, v)) {
if (value == nullptr || value[0] < '0' || value[0] > '9') {
return false;
}
auto v = Helpers::string2minutes(value);
write_command(0x484, 53, v / 15, 0x484);
return true;
}
@@ -3154,6 +3172,15 @@ bool Boiler::set_hpPowerLimit(const char * value, const int8_t id) {
return false;
}
bool Boiler::set_powerReduction(const char * value, const int8_t id) {
int v;
if (Helpers::value2number(value, v)) {
write_command(0x484, 64, v / 10, 0x484);
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

@@ -85,7 +85,7 @@ class Boiler : public EMSdevice {
uint8_t wwMaxPower_; // DHW maximum power
uint8_t wwMaxTemp_; // DHW maximum temperature
uint32_t wwStarts_; // DHW starts
uint32_t wwStarts2_; // DHW control starts
uint32_t wwStartsHp_; // DHW starts Heatpump
uint32_t wwWorkM_; // DHW minutes
int8_t wwHystOn_;
int8_t wwHystOff_;
@@ -256,6 +256,7 @@ class Boiler : public EMSdevice {
uint8_t maxHeatDhw_;
uint8_t hpMaxPower_;
uint8_t pvMaxComp_;
uint8_t powerReduction_;
uint8_t pvCooling_;
uint8_t manDefrost_;
@@ -265,6 +266,7 @@ class Boiler : public EMSdevice {
uint8_t auxHeaterOnly_;
uint8_t auxHeaterOff_;
uint8_t auxHeaterStatus_;
uint8_t auxHeaterLevel_;
uint16_t auxHeaterDelay_;
uint8_t silentMode_;
int8_t minTempSilent_;
@@ -332,7 +334,6 @@ class Boiler : public EMSdevice {
uint8_t delayBoiler_; // minutes
uint8_t tempDiffBoiler_; // relative temperature degrees
*/
void process_UBAFactory(std::shared_ptr<const Telegram> telegram);
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
@@ -482,6 +483,7 @@ class Boiler : public EMSdevice {
bool set_pvMaxComp(const char * value, const int8_t id);
bool set_hpDiffPress(const char * value, const int8_t id);
bool set_hpPowerLimit(const char * value, const int8_t id);
bool set_powerReduction(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

@@ -35,6 +35,9 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
register_telegram_type(0x99C, "HPComp", false, MAKE_PF_CB(process_HPComp));
register_telegram_type(0x4AE, "HPEnergy", true, MAKE_PF_CB(process_HpEnergy));
register_telegram_type(0x4AF, "HPMeters", true, MAKE_PF_CB(process_HpMeters));
register_telegram_type(0x99A, "HPStarts", false, MAKE_PF_CB(process_HpStarts));
register_telegram_type(0x12E, "HPEnergy1", false, MAKE_PF_CB(process_HpEnergy1));
register_telegram_type(0x13B, "HPEnergy2", false, MAKE_PF_CB(process_HpEnergy2));
// device values
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &airHumidity_, DeviceValueType::UINT8, FL_(airHumidity), DeviceValueUOM::PERCENT);
@@ -81,40 +84,41 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
FL_(lowNoiseMode),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_lowNoiseMode));
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &lowNoiseStart_, DeviceValueType::UINT8, FL_(lowNoiseStart), DeviceValueUOM::NONE, MAKE_CF_CB(set_lowNoiseStart), 0, 23);
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &lowNoiseStop_, DeviceValueType::UINT8, FL_(lowNoiseStop), DeviceValueUOM::NONE, MAKE_CF_CB(set_lowNoiseStop), 0, 23);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hybridDHW_,
DeviceValueType::ENUM,
FL_(enum_comfort2),
FL_(hybridDHW),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_hybridDHW));
&lowNoiseStart_,
DeviceValueType::UINT8,
FL_(lowNoiseStart),
DeviceValueUOM::HOURS,
MAKE_CF_CB(set_lowNoiseStart),
0,
23);
register_device_value(
DeviceValueTAG::TAG_DEVICE_DATA, &lowNoiseStop_, DeviceValueType::UINT8, FL_(lowNoiseStop), DeviceValueUOM::HOURS, MAKE_CF_CB(set_lowNoiseStop), 0, 23);
register_device_value(
DeviceValueTAG::TAG_DHW1, &hybridDHW_, DeviceValueType::ENUM, FL_(enum_comfort2), FL_(hybridDHW), DeviceValueUOM::NONE, MAKE_CF_CB(set_hybridDHW));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&energyPriceGas_,
DeviceValueType::UINT8,
FL_(energyPriceGas),
DeviceValueUOM::NONE,
DeviceValueUOM::CTKWH,
MAKE_CF_CB(set_energyPriceGas));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&energyPriceEl_,
DeviceValueType::UINT8,
FL_(energyPriceEl),
DeviceValueUOM::NONE,
DeviceValueUOM::CTKWH,
MAKE_CF_CB(set_energyPriceEl));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&energyPricePV_,
DeviceValueType::UINT8,
FL_(energyPricePV),
DeviceValueUOM::NONE,
DeviceValueUOM::CTKWH,
MAKE_CF_CB(set_energyPricePV));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&switchOverTemp_,
DeviceValueType::INT8,
FL_(switchOverTemp),
DeviceValueUOM::NONE,
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_switchOverTemp));
// Function test
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
@@ -176,6 +180,20 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
FL_(meterHeat),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DHW1, &meterWw_, DeviceValueType::UINT24, DeviceValueNumOp::DV_NUMOP_DIV100, FL_(meterWw), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatStartsHp_, DeviceValueType::UINT24, FL_(heatingStarts), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DHW1, &wwStartsHp_, DeviceValueType::UINT24, FL_(wwStartsHp), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &fuelHeat_, DeviceValueType::UINT32, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(fuelHeat), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DHW1, &elDhw_, DeviceValueType::UINT32, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(fuelDhw), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &fuelHeat_, DeviceValueType::UINT32, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(elHeat), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DHW1, &elDhw_, DeviceValueType::UINT32, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(elDhw), DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&elGenHeat_,
DeviceValueType::UINT32,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(elGenHeat),
DeviceValueUOM::KWH);
register_device_value(DeviceValueTAG::TAG_DHW1, &elGenDhw_, DeviceValueType::UINT32, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(elGenDhw), DeviceValueUOM::KWH);
}
/*
@@ -270,6 +288,27 @@ void Heatpump::process_HpMeters(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, meterWw_, 32);
}
// Broadcast (0x099A), data: 05 00 00 00 00 00 00 37 00 00 1D 00 00 52 00 00 13 01 00 01 7C
void Heatpump::process_HpStarts(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, heatStartsHp_, 11, 3);
has_update(telegram, wwStartsHp_, 14, 3);
}
// 0x0112E energy consumption
void Heatpump::process_HpEnergy1(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, fuelHeat_, 3);
has_update(telegram, fuelDhw_, 7);
has_update(telegram, elHeat_, 11);
has_update(telegram, elDhw_, 15);
}
// 0x013B energy generated
void Heatpump::process_HpEnergy2(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, elGenHeat_, 3);
has_update(telegram, elGenDhw_, 7);
}
/*
* Broadcast (0x099A), data: 05 00 00 00 00 00 00 37 00 00 1D 00 00 52 00 00 13 01 00 01 7C
* Broadcast (0x099B), data: 80 00 80 00 01 3C 01 38 80 00 80 00 80 00 01 37 00 00 00 00 64

View File

@@ -76,6 +76,14 @@ class Heatpump : public EMSdevice {
uint32_t meterHeat_;
uint32_t meterWw_;
uint32_t heatStartsHp_;
uint32_t wwStartsHp_;
uint32_t fuelHeat_;
uint32_t fuelDhw_;
uint32_t elHeat_;
uint32_t elDhw_;
uint32_t elGenHeat_;
uint32_t elGenDhw_;
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);
@@ -86,6 +94,9 @@ class Heatpump : public EMSdevice {
void process_HPComp(std::shared_ptr<const Telegram> telegram);
void process_HpEnergy(std::shared_ptr<const Telegram> telegram);
void process_HpMeters(std::shared_ptr<const Telegram> telegram);
void process_HpStarts(std::shared_ptr<const Telegram> telegram);
void process_HpEnergy1(std::shared_ptr<const Telegram> telegram);
void process_HpEnergy2(std::shared_ptr<const Telegram> telegram);
bool set_controlStrategy(const char * value, const int8_t id);
bool set_lowNoiseMode(const char * value, const int8_t id);

View File

@@ -206,6 +206,18 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(cyl2BottomTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&cylBottomTemp3_,
DeviceValueType::INT16,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(cyl3BottomTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&cylTopTemp_,
DeviceValueType::INT16,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(cylTopTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&heatExchangerTemp_,
DeviceValueType::INT16,
@@ -215,6 +227,9 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylPumpMod_, DeviceValueType::UINT8, FL_(cylPumpMod), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &valveStatus_, DeviceValueType::BOOL, FL_(valveStatus), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vs1Status_, DeviceValueType::BOOL, FL_(vs1Status), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vs3Status_, DeviceValueType::BOOL, FL_(vs3Status), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &transferPump_, DeviceValueType::BOOL, FL_(transferPump), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &transferPumpMod_, DeviceValueType::UINT8, FL_(transferPumpMod), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&collectorMaxTemp_,
DeviceValueType::UINT8,
@@ -580,20 +595,22 @@ void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, cylBottomTemp2_, 16); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool
has_update(telegram, heatExchangerTemp_, 20); // is *10 - TS6: Heat exchanger temperature sensor
has_update(telegram, collector2Temp_, 6); // is *10 - TS7: Temperature sensor for collector array 2
has_update(telegram, cylMiddleTemp_, 8); // is *10 - TS14: cylinder middle temperature
has_update(telegram, retHeatAssist_, 10); // is *10 - TS15: return temperature heating assistance
has_update(telegram, collector2Temp_, 6); // is *10 - TS7: Temperature sensor for collector array 2
has_update(telegram, cylMiddleTemp_, 8); // is *10 - TS14: cylinder middle temperature
has_update(telegram, retHeatAssist_, 10); // is *10 - TS15: return temperature heating assistance
has_update(telegram, cylBottomTemp3_, 24); // is *10 - TS5: Temperature sensor cylinder 3, bottom
}
// SM100Monitor2 - 0x0363 Heatcounter
// e.g. B0 00 FF 00 02 63 80 00 80 00 00 00 80 00 80 00 80 00 00 80 00 5A
// Solar(0x30) -> All(0x00), SM100Monitor2(0x363), data: 01 E1 01 6B 00 00 01 5D 02 8E 80 00 0F 80 00
void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(heatCntFlowTemp_, 0)); // is *10
has_update(telegram->read_value(heatCntRetTemp_, 2)); // is *10
has_update(telegram->read_value(heatCnt_, 12));
has_update(telegram->read_value(swapRetTemp_, 6)); // is *10
has_update(telegram->read_value(swapFlowTemp_, 8)); // is *10
has_update(telegram, heatCntFlowTemp_, 0); // is *10
has_update(telegram, heatCntRetTemp_, 2); // is *10
has_update(telegram, heatCnt_, 12);
has_update(telegram, swapRetTemp_, 6); // is *10
has_update(telegram, swapFlowTemp_, 8); // is *10
has_update(telegram, cylTopTemp_, 10); // is *10 - TS10: cylinder top temperature
}
// SM100Config - 0x0366
@@ -607,7 +624,7 @@ void Solar::process_SM100Config(std::shared_ptr<const Telegram> telegram) {
// SM100Config1 - 0x035F
// e.g. Solar(0x30) -> Me(0x0B), ?(0x35F), data: 00 00 41 01 1E 0A 0C 19 00 3C 19
void Solar::process_SM100Config1(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(cylPriority_, 3));
has_update(telegram, cylPriority_, 3);
}
/*
@@ -644,6 +661,8 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
solarpumpmod = solarPumpMinMod_ * 5; // set to minimum
}
has_update(solarPump2Mod_, solarpumpmod);
has_update(telegram, transferPumpMod_, 14);
}
/*
@@ -653,11 +672,12 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
* byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3)
*/
void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
has_bitupdate(telegram, vs1Status_, 0, 2); // on if bit 2 set
has_bitupdate(telegram, valveStatus_, 4, 2); // on if bit 2 set
has_bitupdate(telegram, solarPump_, 10, 2); // on if bit 2 set
has_bitupdate(telegram, solarPump2_, 1, 2); // on if bit 2 set
has_bitupdate(telegram, m1Valve_, 7, 2); // values 8/4 seen
has_bitupdate(telegram, vs1Status_, 0, 2); // on if bit 2 set
has_bitupdate(telegram, valveStatus_, 4, 2); // on if bit 2 set
has_bitupdate(telegram, solarPump_, 10, 2); // on if bit 2 set
has_bitupdate(telegram, solarPump2_, 1, 2); // on if bit 2 set
has_bitupdate(telegram, m1Valve_, 7, 2); // values 8/4 seen
has_bitupdate(telegram, transferPump_, 11, 2); // #2212
}
/*

View File

@@ -33,6 +33,8 @@ class Solar : public EMSdevice {
int16_t collectorTemp_; // TS1: Temperature sensor for collector array 1
int16_t cylBottomTemp_; // TS2: Temperature sensor 1 cylinder, bottom cyl (solar thermal system)
int16_t cylBottomTemp2_; // TS5: Temperature sensor 2 cylinder, bottom cyl, or swimming pool (solar thermal system)
int16_t cylBottomTemp3_; // TS11: Temperature sensor 3. cylinder
int16_t cylTopTemp_; // TS10: Temperature sensor 1 cylinder, Top
int16_t heatExchangerTemp_; // TS6: Heat exchanger temperature sensor
int16_t collector2Temp_; // TS7: Temperature sensor for collector array 2
int16_t cylMiddleTemp_; // TS14: Cylinder middle temp
@@ -46,6 +48,9 @@ class Solar : public EMSdevice {
uint8_t m1Valve_; // M1: heat assistance valve
uint8_t m1Power_; // M1: heat assistance valve
uint8_t vs1Status_; // VS1: status
uint8_t vs3Status_; // VS3: status
uint8_t transferPump_;
uint8_t transferPumpMod_;
// 0x363 heat counter
uint16_t heatCntFlowTemp_;

View File

@@ -1561,22 +1561,34 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
return; // not supported
}
static uint8_t setTimeRetry = 0;
uint8_t dst = 0xFE;
bool use_dst = !telegram->read_value(dst, 9) || dst == 0xFF;
if ((telegram->message_data[7] & 0x0C) && has_command(&dateTime_)) { // date and time not valid
set_datetime("ntp", -1); // set from NTP
if (setTimeRetry < 3) {
if (!use_dst) {
set_datetime("ntp", 0); // set from NTP without dst
} else {
set_datetime("ntp", -1); // set from NTP
}
setTimeRetry++;
}
return;
}
// check clock
time_t now = time(nullptr);
tm * tm_ = localtime(&now);
bool tset_ = tm_->tm_year > 110; // year 2010 and up, time is valid
tm_->tm_year = (telegram->message_data[0] & 0x7F) + 100; // IVT
tm_->tm_mon = telegram->message_data[1] - 1;
tm_->tm_mday = telegram->message_data[3];
tm_->tm_hour = telegram->message_data[2];
tm_->tm_min = telegram->message_data[4];
tm_->tm_sec = telegram->message_data[5];
tm_->tm_isdst = telegram->message_data[7] & 0x01;
time_t now = time(nullptr);
tm * tm_ = localtime(&now);
bool tset_ = tm_->tm_year > 110; // year 2010 and up, time is valid
tm_->tm_year = (telegram->message_data[0] & 0x7F) + 100; // IVT
tm_->tm_mon = telegram->message_data[1] - 1;
tm_->tm_mday = telegram->message_data[3];
tm_->tm_hour = telegram->message_data[2];
tm_->tm_min = telegram->message_data[4];
tm_->tm_sec = telegram->message_data[5];
if (use_dst) {
tm_->tm_isdst = telegram->message_data[7] & 0x01;
}
// render date to DD.MM.YYYY HH:MM and publish
char newdatetime[sizeof(dateTime_)];
@@ -1590,8 +1602,18 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
if (!ivtclock && !junkersclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
double difference = difftime(now, ttime);
if (difference > 15 || difference < -15) {
set_datetime("ntp", -1); // set from NTP
LOG_INFO("thermostat time correction from ntp");
if (setTimeRetry < 3) {
if (!use_dst) {
set_datetime("ntp", 0); // set from NTP without dst
LOG_INFO("thermostat time correction from ntp, ignoring dst");
} else {
set_datetime("ntp", -1); // set from NTP
LOG_INFO("thermostat time correction from ntp");
}
setTimeRetry++;
}
} else {
setTimeRetry = 0;
}
}
#ifndef EMSESP_STANDALONE
@@ -2685,8 +2707,8 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
data[3] = tm_->tm_mday;
data[4] = tm_->tm_min;
data[5] = tm_->tm_sec;
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
data[7] = tm_->tm_isdst + 2; // set DST and flag for ext. clock
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
data[7] = (id == 0) ? 2 : tm_->tm_isdst + 2; // set DST and flag for ext. clock
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
data[6]++; // Junkers use 1-7;
data[7] = 0;
@@ -4890,7 +4912,7 @@ void Thermostat::register_device_values_dhw(std::shared_ptr<Thermostat::DhwCircu
DeviceValueUOM::MINUTES,
MAKE_CF_CB(set_wwchargeduration));
register_device_value(tag, &dhw->wwCharge_, DeviceValueType::BOOL, FL_(wwCharge), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwcharge));
register_device_value(tag, &dhw->wwExtra_, DeviceValueType::UINT8, FL_(wwExtra), DeviceValueUOM::DEGREES);
register_device_value(tag, &dhw->wwExtra_, DeviceValueType::BOOL, FL_(wwExtra), DeviceValueUOM::NONE);
register_device_value(tag, &dhw->wwDisinfecting_, DeviceValueType::BOOL, FL_(wwDisinfecting), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwDisinfect));
register_device_value(
tag, &dhw->wwDisinfectDay_, DeviceValueType::ENUM, FL_(enum_dayOfWeek), FL_(wwDisinfectDay), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwDisinfectDay));

View File

@@ -28,7 +28,7 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
dhw_ = device_id - EMSdevice::EMS_DEVICE_ID_DHW1;
int8_t tag = DeviceValueTAG::TAG_DHW1 + dhw_;
if (device_id == 0x2A) { // SM100, DHW3
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) { // device_id 0x2A, DHW3
// telegram handlers
register_telegram_type(0x07D6, "SM100wwTemperature", false, MAKE_PF_CB(process_SM100wwTemperature));
register_telegram_type(0x07AA, "SM100wwStatus", false, MAKE_PF_CB(process_SM100wwStatus));
@@ -63,7 +63,7 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_device_value(tag, &wwDeltaTRet_, DeviceValueType::UINT8, FL_(deltaTRet), DeviceValueUOM::K, MAKE_CF_CB(set_wwDeltaTRet));
register_device_value(tag, &errorDisp_, DeviceValueType::ENUM, FL_(enum_errorDisp), FL_(errorDisp), DeviceValueUOM::NONE, MAKE_CF_CB(set_errorDisp));
} else if (device_id >= EMSdevice::EMS_DEVICE_ID_DHW1 && device_id <= EMSdevice::EMS_DEVICE_ID_DHW2) {
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) { // dhw1 and dhw 2
register_telegram_type(0x331 + dhw_, "MMPLUSStatusMessage_WWC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
register_telegram_type(0x313 + dhw_, "MMPLUSConfigMessage_WWC", true, MAKE_PF_CB(process_MMPLUSConfigMessage_WWC));
// register_telegram_type(0x33B + type_offset, "MMPLUSSetMessage_WWC", true, MAKE_PF_CB(process_MMPLUSSetMessage_WWC));
@@ -78,7 +78,7 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_device_value(tag, &wwRequiredTemp_, DeviceValueType::UINT8, FL_(wwRequiredTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwRequiredTemp));
register_device_value(tag, &wwCirc_, DeviceValueType::BOOL, FL_(wwCirc), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCirc));
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
} else if (device_id == 0x40) { // flags == EMSdevice::EMS_DEVICE_FLAG_IPM, special DHW pos 10
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
dhw_ = 0;
tag = DeviceValueTAG::TAG_DHW1;
register_telegram_type(0x34, "UBAMonitorWW", false, MAKE_PF_CB(process_IPMMonitorWW));

View File

@@ -135,7 +135,7 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
case DeviceType::GATEWAY:
return F_(gateway);
case DeviceType::SWITCH:
return F_(switch);
return F_(switcher);
case DeviceType::CONTROLLER:
return F_(controller);
case DeviceType::CONNECT:
@@ -233,7 +233,7 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
if (!strcmp(lowtopic, F_(mixer))) {
return DeviceType::MIXER;
}
if (!strcmp(lowtopic, F_(switch))) {
if (!strcmp(lowtopic, F_(switcher))) {
return DeviceType::SWITCH;
}
if (!strcmp(lowtopic, F_(gateway))) {
@@ -1271,7 +1271,7 @@ void EMSdevice::getCustomizationEntities(std::vector<std::string> & entity_ids)
// pipe symbols (|) are escaped so they can be converted to Markdown in the Wiki
// format is: device name,device type,product id,shortname,fullname,type [options...] \\| (min/max),uom,writeable,discovery entityid v3.4, discovery entityid
#if defined(EMSESP_STANDALONE)
void EMSdevice::dump_value_info() {
void EMSdevice::dump_devicevalue_info() {
for (auto & dv : devicevalues_) {
if (dv.fullname != nullptr) {
Serial.print('\"');
@@ -1438,15 +1438,13 @@ void EMSdevice::dump_value_info() {
Serial.print(",");
// modbus specific infos
Serial.print(device_type());
Serial.print(device_type()); // modbus unit identifier
Serial.print(',');
Serial.print(dv.tag);
Serial.print(dv.tag); // modbus block
Serial.print(',');
// numeric operator -> scale factor
// numeric operator -> modbus scale factor
if (dv.numeric_operator == 0)
Serial.print("1");
else if (dv.numeric_operator > 0)
@@ -1455,10 +1453,10 @@ void EMSdevice::dump_value_info() {
Serial.print(-dv.numeric_operator);
Serial.print(",");
Serial.printf("%d", EMSESP::modbus_->getRegisterOffset(dv));
Serial.printf("%d", EMSESP::modbus_->getRegisterOffset(dv)); // modbus offset
Serial.print(",");
Serial.printf("%d", EMSESP::modbus_->getRegisterCount(dv));
Serial.printf("%d", EMSESP::modbus_->getRegisterCount(dv)); // modbus count
// /modbus specific infos
@@ -1845,9 +1843,9 @@ void EMSdevice::mqtt_ha_entity_config_create() {
uint16_t count = 0;
// check the state of each of the device values
// create climate if roomtemp is visible
// create the discovery topic if if hasn't already been created, not a command (like reset) and is active and visible
for (auto & dv : devicevalues_) {
// create climate when we reach the haclimate entity
if (!strcmp(dv.short_name, FL_(haclimate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) {
if (*(int8_t *)(dv.value_p) == 1 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
if (Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max)) { // roomTemp
@@ -1930,7 +1928,7 @@ const char * EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> teleg
bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
for (auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == telegram->type_id) {
// for telegram desitnation only read telegram
// for telegram destination only read telegram
if (telegram->dest == device_id_ && telegram->message_length > 0) {
tf.process_function_(telegram);
return true;
@@ -1982,9 +1980,14 @@ void EMSdevice::read_command(const uint16_t type_id, const uint8_t offset, const
// returns either default or custom name
std::string EMSdevice::name() {
if (custom_name_.empty()) {
return default_name_;
// return default name prefixed with a model if exists
if (model().empty()) {
return default_name();
}
return model() + "/" + default_name();
}
return custom_name_;
return custom_name();
}
// copy a raw value (i.e. without applying the numeric_operator) to the output buffer.
@@ -1993,8 +1996,9 @@ int EMSdevice::get_modbus_value(uint8_t tag, const std::string & shortname, std:
// find device value by shortname
// TODO replace linear search which is inefficient
const auto & it = std::find_if(devicevalues_.begin(), devicevalues_.end(), [&](const DeviceValue & x) { return x.tag == tag && x.short_name == shortname; });
if (it == devicevalues_.end())
if (it == devicevalues_.end() && (it->short_name != shortname || it->tag != tag)) {
return -1;
}
auto & dv = *it;
@@ -2079,7 +2083,7 @@ int EMSdevice::modbus_value_to_json(uint8_t tag, const std::string & shortname,
// find device value by shortname
const auto & it = std::find_if(devicevalues_.begin(), devicevalues_.end(), [&](const DeviceValue & x) { return x.tag == tag && x.short_name == shortname; });
if (it == devicevalues_.end()) {
if (it == devicevalues_.end() && (it->short_name != shortname || it->tag != tag)) {
return -1;
}

View File

@@ -53,28 +53,48 @@ class EMSdevice {
static uint8_t decode_brand(uint8_t value);
static bool export_values(uint8_t device_type, JsonObject output, const int8_t id, const uint8_t output_target);
// non static
// non static functions
const char * device_type_name(); // returns short non-translated device type name
const char * device_type_2_device_name_translated(); // returns translated device type name
bool has_tags(const int8_t tag) const;
bool has_cmd(const char * cmd, const int8_t id) const;
const char * brand_to_char();
std::string to_string();
std::string to_string_short();
std::string name(); // returns either default or custom name of a device (if defined)
bool has_tags(const int8_t tag) const;
bool has_cmd(const char * cmd, const int8_t id) const;
bool is_device_id(uint8_t device_id) const {
return ((device_id & 0x7F) == (device_id_ & 0x7F));
}
inline uint8_t device_id() const {
// Getters
uint8_t device_id() const {
return device_id_;
}
inline uint8_t product_id() const {
uint8_t product_id() const {
return product_id_;
}
void product_id(uint8_t product_id) {
product_id_ = product_id;
uint8_t device_type() const {
return device_type_; // see enum DeviceType below
}
inline bool is_device_id(uint8_t device_id) const {
return ((device_id & 0x7F) == (device_id_ & 0x7F));
const char * version() const {
return version_;
}
uint8_t brand() const {
return brand_;
}
void active(bool active) {
active_ = active;
}
const char * default_name() const {
return default_name_;
}
// flags
@@ -91,66 +111,46 @@ class EMSdevice {
return flags_;
}
inline uint8_t device_type() const {
return device_type_; // see enum DeviceType below
}
inline void version(const char * version) {
strlcpy(version_, version, sizeof(version_));
}
inline const char * version() const {
return version_;
}
inline void brand(uint8_t brand) {
brand_ = brand;
}
inline uint8_t brand() const {
return brand_;
}
inline void active(bool active) {
active_ = active;
}
// set custom device name
inline void custom_name(std::string const & custom_name) {
void custom_name(std::string const & custom_name) {
custom_name_ = custom_name;
}
std::string name(); // returns either default or custom name if defined
// default name
inline void default_name(const char * default_name) {
default_name_ = default_name;
std::string custom_name() const {
return custom_name_;
}
inline const char * default_name() const {
return default_name_;
// set device model
void model(std::string const & model) {
model_ = model;
}
std::string model() const {
return model_;
}
inline uint8_t unique_id() const {
return unique_id_;
}
inline void unique_id(uint8_t unique_id) {
void unique_id(uint8_t unique_id) {
unique_id_ = unique_id;
}
inline bool has_update() const {
bool has_update() const {
return has_update_;
}
inline void has_update(bool flag) {
void has_update(bool flag) {
has_update_ = flag;
}
inline void has_update(void * value) {
void has_update(void * value) {
has_update_ = true;
publish_value(value);
}
inline void has_update(char * value, const char * newvalue, size_t len) {
void has_update(char * value, const char * newvalue, size_t len) {
if (strcmp(value, newvalue) != 0) {
strlcpy(value, newvalue, len);
has_update_ = true;
@@ -158,7 +158,7 @@ class EMSdevice {
}
}
inline void has_update(uint8_t & value, uint8_t newvalue) {
void has_update(uint8_t & value, uint8_t newvalue) {
if (value != newvalue) {
value = newvalue;
has_update_ = true;
@@ -166,7 +166,7 @@ class EMSdevice {
}
}
inline void has_update(uint16_t & value, uint16_t newvalue) {
void has_update(uint16_t & value, uint16_t newvalue) {
if (value != newvalue) {
value = newvalue;
has_update_ = true;
@@ -174,7 +174,7 @@ class EMSdevice {
}
}
inline void has_update(uint32_t & value, uint32_t newvalue) {
void has_update(uint32_t & value, uint32_t newvalue) {
if (value != newvalue) {
value = newvalue;
has_update_ = true;
@@ -182,7 +182,7 @@ class EMSdevice {
}
}
inline void has_enumupdate(std::shared_ptr<const Telegram> telegram, uint8_t & value, const uint8_t index, int8_t s = 0) {
void has_enumupdate(std::shared_ptr<const Telegram> telegram, uint8_t & value, const uint8_t index, int8_t s = 0) {
if (telegram->read_enumvalue(value, index, s)) {
has_update_ = true;
publish_value((void *)&value);
@@ -190,7 +190,7 @@ class EMSdevice {
}
template <typename Value>
inline void has_update(std::shared_ptr<const Telegram> telegram, Value & value, const uint8_t index, uint8_t s = 0) {
void has_update(std::shared_ptr<const Telegram> telegram, Value & value, const uint8_t index, uint8_t s = 0) {
if (telegram->read_value(value, index, s)) {
has_update_ = true;
publish_value((void *)&value);
@@ -198,22 +198,18 @@ class EMSdevice {
}
template <typename BitValue>
inline void has_bitupdate(std::shared_ptr<const Telegram> telegram, BitValue & value, const uint8_t index, uint8_t b) {
void has_bitupdate(std::shared_ptr<const Telegram> telegram, BitValue & value, const uint8_t index, uint8_t b) {
if (telegram->read_bitvalue(value, index, b)) {
has_update_ = true;
publish_value((void *)&value);
}
}
// modbus
int get_modbus_value(uint8_t tag, const std::string & shortname, std::vector<uint16_t> & result);
int modbus_value_to_json(uint8_t tag, const std::string & shortname, const std::vector<uint8_t> & modbus_data, JsonObject jsonValue);
const char * brand_to_char();
std::string to_string();
std::string to_string_short();
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING, IGNORED };
void show_telegram_handlers(uuid::console::Shell & shell) const;
char * show_telegram_handlers(char * result, const size_t len, const uint8_t handlers);
void show_mqtt_handlers(uuid::console::Shell & shell) const;
@@ -227,10 +223,9 @@ class EMSdevice {
bool handle_telegram(std::shared_ptr<const Telegram> telegram);
std::string get_value_uom(const std::string & shortname) const;
bool get_value_info(JsonObject root, const char * cmd, const int8_t id);
void get_value_json(JsonObject output, DeviceValue & dv);
void get_dv_info(JsonObject json);
bool get_value_info(JsonObject root, const char * cmd, const int8_t id);
void get_value_json(JsonObject output, DeviceValue & dv);
void get_dv_info(JsonObject json);
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
bool generate_values(JsonObject output, const int8_t tag_filter, const bool nested, const uint8_t output_target);
@@ -314,23 +309,20 @@ class EMSdevice {
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0) const;
bool is_readable(const void * value_p) const;
bool is_readonly(const std::string & cmd, const int8_t id) const;
bool has_command(const void * value_p) const;
void set_minmax(const void * value_p, int16_t min, uint32_t max);
void publish_value(void * value_p) const;
void publish_all_values();
void mqtt_ha_entity_config_create();
bool is_readable(const void * value_p) const;
bool is_readonly(const std::string & cmd, const int8_t id) const;
bool has_command(const void * value_p) const;
void set_minmax(const void * value_p, int16_t min, uint32_t max);
void publish_value(void * value_p) const;
void publish_all_values();
void mqtt_ha_entity_config_create();
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
void fetch_values();
void toggle_fetch(uint16_t telegram_id, bool toggle);
bool is_fetch(uint16_t telegram_id) const;
bool is_received(uint16_t telegram_id) const;
bool has_telegram_id(uint16_t id) const;
void ha_config_clear();
void fetch_values();
void toggle_fetch(uint16_t telegram_id, bool toggle);
bool is_fetch(uint16_t telegram_id) const;
bool is_received(uint16_t telegram_id) const;
bool has_telegram_id(uint16_t id) const;
void ha_config_clear();
bool ha_config_done() const {
return ha_config_done_;
@@ -491,7 +483,7 @@ class EMSdevice {
}
};
void dump_telegram_info(std::vector<TelegramFunctionDump> & telegram_functions_dump);
void dump_value_info();
void dump_devicevalue_info();
#endif
private:
@@ -502,6 +494,7 @@ class EMSdevice {
char version_[6];
const char * default_name_; // the fixed name the EMS model taken from the device library
std::string custom_name_ = ""; // custom name
std::string model_ = ""; // model, taken from the 0x01 telegram. see process_deviceName()
uint8_t flags_ = 0;
uint8_t brand_ = Brand::NO_BRAND;
bool active_ = true;
@@ -525,14 +518,13 @@ class EMSdevice {
}
};
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
std::vector<uint16_t> handlers_ignored_;
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
public: // so we can call it from WebCustomizationService::test()
public: // so we can call it from WebCustomizationService::test() and EMSESP::dump_all_entities()
#endif
std::vector<DeviceValue> devicevalues_; // all the device values
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
std::vector<DeviceValue> devicevalues_; // all the device values
};
} // namespace emsesp

View File

@@ -108,9 +108,10 @@ DeviceValue::DeviceValue(uint8_t device_type,
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_mbar), F_(uom_lh), F_(uom_blank)
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_mbar), F_(uom_lh), F_(uom_ctkwh), F_(uom_blank)
};

View File

@@ -74,10 +74,11 @@ class DeviceValue {
VOLTS, // 23 - V
MBAR, // 24 - mbar
LH, // 25 - l/h
CONNECTIVITY // 26 - used in HA
CTKWH, // 26 - ct/kWh
CONNECTIVITY // 27 - used in HA
};
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
// TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp
enum DeviceValueTAG : int8_t {
TAG_NONE = -1, // wild card
TAG_DEVICE_DATA = 0,

View File

@@ -332,7 +332,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
// Dump all entities to Serial out
// this is intended to run within the OS with lots of available memory!
#if defined(EMSESP_STANDALONE)
void EMSESP::dump_all_values(uuid::console::Shell & shell) {
void EMSESP::dump_all_entities(uuid::console::Shell & shell) {
Serial.println("---- CSV START ----"); // marker use by py script
// add header for CSV
Serial.println("device name,device type,product id,shortname,fullname,type [options...] \\| (min/max),uom,writeable,discovery entityid v3.4,discovery "
@@ -343,25 +343,27 @@ void EMSESP::dump_all_values(uuid::console::Shell & shell) {
for (const auto & device : device_library_) {
if (device_class.first == device.device_type) {
uint8_t device_id = 0;
// Mixer class looks at device_id to determine type and the tag
// so fixing to 0x28 which will give all the settings except flowSetTemp
if (device.device_type == DeviceType::MIXER) {
// Water class looks at device_id to determine type and the tag
if (device.device_type == DeviceType::WATER) {
if (device.flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
if (device.product_id == 160) { // MM100
device_id = 0x28; // dhw
} else {
device_id = 0x20; // hc
}
} else {
device_id = 0x20; // should cover all the other device types
device_id = 0x28; // dhw 1/2
} else if (device.flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
device_id = 0x28; // fix to dhw1, normally SM100 can only use dhw 3
} else if (device.flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
device_id = 0x40; // dhw 1, not needed
}
}
// For a Mixer, fix device_id to 0x20 to give us all the settings
if (device.device_type == DeviceType::MIXER) {
device_id = 0x20; // hc
}
// add the device and print out all the entities
// for testing the mixer use ... if (device.product_id == 69) {
emsdevices.push_back(
EMSFactory::add(device.device_type, device_id, device.product_id, "1.0", device.default_name, device.flags, EMSdevice::Brand::NO_BRAND));
emsdevices.back()->dump_value_info();
emsdevices.back()->dump_devicevalue_info();
}
}
}
@@ -385,20 +387,22 @@ void EMSESP::dump_all_telegrams(uuid::console::Shell & shell) {
for (const auto & device : device_library_) {
if (device_class.first == device.device_type) {
uint8_t device_id = 0;
// Mixer class looks at device_id to determine type and the tag
// so fixing to 0x28 which will give all the settings except flowSetTemp
if (device.device_type == DeviceType::MIXER) {
// Water class looks at device_id to determine type and the tag
if (device.device_type == DeviceType::WATER) {
if (device.flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
if (device.product_id == 160) { // MM100
device_id = 0x28; // dhw
} else {
device_id = 0x20; // hc
}
} else {
device_id = 0x20; // should cover all the other device types
device_id = 0x28; // dhw 1/2
} else if (device.flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
device_id = 0x2A; // dhw 3 needed to calculate right telegram numbers
} else if (device.flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
device_id = 0x40; // dhw 1, not needed
}
}
// For a Mixer, fix device_id to 0x20 to give us all the settings
if (device.device_type == DeviceType::MIXER) {
device_id = 0x20; // hc
}
// add the device and print out all the entities
emsdevices.push_back(
EMSFactory::add(device.device_type, device_id, device.product_id, "1.0", device.default_name, device.flags, EMSdevice::Brand::NO_BRAND));
@@ -910,14 +914,14 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
std::string str;
str.reserve(200);
if (telegram->operation == Telegram::Operation::RX_READ) {
str = src_name + "(" + Helpers::hextoa(src) + ") -R-> " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "("
str = src_name + "(" + Helpers::hextoa(src) + ") -> " + dest_name + "(" + Helpers::hextoa(dest) + "), R, " + type_name + "("
+ Helpers::hextoa(telegram->type_id) + "), length: " + Helpers::itoa(telegram->message_data[0])
+ ((telegram->message_length > 1) ? ", data: " + Helpers::data_to_hex(telegram->message_data + 1, telegram->message_length - 1) : "");
} else if (telegram->dest == 0) {
str = src_name + "(" + Helpers::hextoa(src) + ") -B-> " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "("
str = src_name + "(" + Helpers::hextoa(src) + ") -> " + dest_name + "(" + Helpers::hextoa(dest) + "), B, " + type_name + "("
+ Helpers::hextoa(telegram->type_id) + "), data: " + telegram->to_string_message();
} else {
str = src_name + "(" + Helpers::hextoa(src) + ") -W-> " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "("
str = src_name + "(" + Helpers::hextoa(src) + ") -> " + dest_name + "(" + Helpers::hextoa(dest) + "), W, " + type_name + "("
+ Helpers::hextoa(telegram->type_id) + "), data: " + telegram->to_string_message();
}
@@ -971,15 +975,19 @@ void EMSESP::process_deviceName(std::shared_ptr<const Telegram> telegram) {
char name[16];
uint8_t len = telegram->offset + telegram->message_length - 27;
strlcpy(name, (const char *)&telegram->message_data[27 - telegram->offset], len < 16 ? len : 16);
if (strlen(name)) {
webCustomizationService.read([&](WebCustomization const & settings) {
for (EntityCustomization e : settings.entityCustomizations) {
if ((e.device_id == telegram->src) && e.custom_name.empty()) {
e.custom_name = name;
break;
}
char * c = name;
while (isprint(*c)) {
c++;
};
*c = '\0';
if (strlen(name) > 2) { // https://github.com/emsesp/EMS-ESP32/issues/2166#issuecomment-2454488657
LOG_DEBUG("Model name received for device 0x%02X: %s", telegram->src, name);
for (const auto & emsdevice : emsdevices) {
if (emsdevice->is_device_id(telegram->src)) {
emsdevice->model(name);
break;
}
});
}
}
}
@@ -1017,13 +1025,14 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// some devices store the protocol type (HT3, Buderus) in the last byte
uint8_t brand;
if (telegram->message_length >= 10) {
brand = EMSdevice::decode_brand(telegram->message_data[9]); // TODO should be offset + 9?
brand = EMSdevice::decode_brand(telegram->message_data[9]);
} else {
brand = EMSdevice::Brand::NO_BRAND; // unknown
}
// add it - will be overwritten if device already exists
(void)add_device(device_id, product_id, version, brand);
// request the deviceName from telegram 0x01
send_read_request(EMSdevice::EMS_TYPE_NAME, device_id, 27);
}
@@ -1204,8 +1213,8 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_class.first)) {
shell.printf("%s: %s", emsdevice->device_type_name(), emsdevice->to_string().c_str());
shell.println();
// print header, with device type translated
shell.printfln("%s: %s (%d)", emsdevice->device_type_2_device_name_translated(), emsdevice->to_string().c_str(), emsdevice->count_entities());
emsdevice->show_telegram_handlers(shell);
#if defined(EMSESP_DEBUG)
@@ -1462,11 +1471,12 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
} else if (first_value == TxService::TX_WRITE_FAIL) {
LOG_ERROR("Last Tx write rejected by host");
txservice_.send_poll(); // close the bus
txservice_.reset_retry_count();
tx_successful = true; // no retries
} else {
LOG_ERROR("Last Tx write host reply: 0x%02X", first_value);
}
} else if (tx_state == Telegram::Operation::TX_READ && length == 1) {
EMSbus::tx_state(Telegram::Operation::TX_READ); // reset Tx wait state
return;
} else if (tx_state == Telegram::Operation::TX_READ) {
} else if (tx_state == Telegram::Operation::TX_READ && length > 1) {
// got a telegram with data in it. See if the src/dest matches that from the last one we sent and continue to process it
uint8_t src = data[0];
uint8_t dest = data[1];
@@ -1478,7 +1488,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0
// not for response to raw send commands without read_id set
if ((response_id_ == 0 || read_id_ > 0) && (txservice_.read_next_tx(data[3], length) == read_id_)) {
if ((response_id_ == 0 || read_id_ > 0) && (txservice_.read_next_tx(data[3], length) > 0)) {
read_next_ = true;
txservice_.send(); // read next part withing same poll or:
// txservice_.send_poll(); // close the bus, next request in new poll
@@ -1528,9 +1538,11 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// check for poll to us, if so send top message from Tx queue immediately and quit
if (poll_id == txservice_.get_send_id()) {
txservice_.send();
} else {
// send remote room temperature if active
Roomctrl::send(poll_id);
}
// send remote room temperature if active
Roomctrl::send(poll_id);
return;
} else {
#ifdef EMSESP_UART_DEBUG
@@ -1764,10 +1776,10 @@ void EMSESP::loop() {
void EMSESP::start_serial_console() {
shell_ = std::make_shared<EMSESPConsole>(*this, serial_console_, true);
shell_->maximum_log_messages(100);
shell_->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when running tests
shell_->start();
#if defined(EMSESP_DEBUG)
shell_->log_level(uuid::log::Level::DEBUG);
shell_->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when compiled with debug
#else
shell_->log_level(uuid::log::Level::TRACE);
#endif

View File

@@ -124,25 +124,23 @@ class EMSESP {
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
static bool device_exists(const uint8_t device_id);
static void device_active(const uint8_t device_id, const bool active);
static bool cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id);
static bool device_exists(const uint8_t device_id);
static void device_active(const uint8_t device_id, const bool active);
static bool cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id);
static uint8_t device_id_from_cmd(const uint8_t device_type, const char * cmd, const int8_t id);
static uint8_t count_devices(const uint8_t device_type);
static uint8_t count_devices();
static uint8_t device_index(const uint8_t device_type, const uint8_t unique_id);
static bool get_device_value_info(JsonObject root, const char * cmd, const int8_t id, const uint8_t devicetype);
static bool get_device_value_info(JsonObject root, const char * cmd, const int8_t id, const uint8_t devicetype);
static void show_device_values(uuid::console::Shell & shell);
static void show_sensor_values(uuid::console::Shell & shell);
static void dump_all_values(uuid::console::Shell & shell);
static void dump_all_telegrams(uuid::console::Shell & shell);
static void show_devices(uuid::console::Shell & shell);
static void show_ems(uuid::console::Shell & shell);
static void dump_all_entities(uuid::console::Shell & shell);
static void dump_all_telegrams(uuid::console::Shell & shell);
static void uart_init();
static void incoming_telegram(uint8_t * data, const uint8_t length);

View File

@@ -603,6 +603,10 @@ bool Helpers::value2bool(const char * value, bool & value_b) {
return true; // is a bool
}
#ifdef EMSESP_STANDALONE
emsesp::EMSESP::logger().debug("Error. value2bool: %s is not a boolean", value);
#endif
return false; // not a bool
}
@@ -764,7 +768,7 @@ uint8_t Helpers::count_items(const char * const ** list) {
// if force_en is true always take the EN non-translated word
const char * Helpers::translated_word(const char * const * strings, const bool force_en) {
uint8_t language_index = EMSESP::system_.language_index();
uint8_t index = 0;
uint8_t index = 0; // default en
if (!strings) {
return ""; // no translations
@@ -774,6 +778,7 @@ const char * Helpers::translated_word(const char * const * strings, const bool f
if (!force_en && (Helpers::count_items(strings) >= language_index + 1 && strlen(strings[language_index]))) {
index = language_index;
}
return strings[index];
}

View File

@@ -48,6 +48,8 @@ MAKE_WORD(send)
MAKE_WORD(telegram)
MAKE_WORD(bus_id)
MAKE_WORD(tx_mode)
MAKE_WORD(showertimer)
MAKE_WORD(showeralert)
MAKE_WORD(ems)
MAKE_WORD(devices)
MAKE_WORD(shower)
@@ -88,7 +90,7 @@ MAKE_WORD(coldshot)
// device types - lowercase, used in MQTT
MAKE_WORD(boiler)
MAKE_WORD(thermostat)
MAKE_WORD(switch)
MAKE_WORD_CUSTOM(switcher, "switch")
MAKE_WORD(solar)
MAKE_WORD(mixer)
MAKE_WORD(gateway)
@@ -229,7 +231,7 @@ MAKE_NOTRANSLATION(tpl_input4, "<inv>[<comp><aux><cool><heat><dhw><pv>]")
MAKE_NOTRANSLATION(test_cmd, "run a test")
#endif
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
// TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp
// use empty string if want to suppress showing tags
// mqtt tags must not have spaces
MAKE_NOTRANSLATION(tag_none, "")
@@ -260,6 +262,7 @@ MAKE_WORD_CUSTOM(uom_k, "K")
MAKE_WORD_CUSTOM(uom_volts, "V")
MAKE_WORD_CUSTOM(uom_mbar, "mbar")
MAKE_WORD_CUSTOM(uom_lh, "l/h")
MAKE_WORD_CUSTOM(uom_ctkwh, "ct/kWh")
// MQTT topics and prefixes
MAKE_WORD_CUSTOM(heating_active, "heating_active")

View File

@@ -43,7 +43,7 @@ MAKE_WORD_TRANSLATION(thermostat_device, "Thermostat", "Thermostat", "Thermostaa
MAKE_WORD_TRANSLATION(heatpump_device, "Heat Pump", "Wärmepumpe", "Warmtepomp", "Värmepump", "Pompa ciepła", "Varmepumpe", "", "Isı Pompası", "Pompa di Calore", "Tepelné čerpadlo", "Tepelné čerpadlo") // TODO translate
MAKE_WORD_TRANSLATION(solar_device, "Solar Module", "Solarmodul", "Solar Module", "Solmodul", "Moduł solarny", "Solmodul", "", "Güneş Enerjisi Cihazı", "Modulo Solare", "Solárny modul", "Solární modul") // TODO translate
MAKE_WORD_TRANSLATION(connect_device, "Connect Module", "Verbindungsmodul", "Connect Module", "Uppkopplingsmodul", "Moduł przyłączeń", "Sammenkoblingsmodul", "", "Güneş Enerjisi Cihazı", "Modulo connessione", "Pripojte modul", "Modul připojení") // TODO translate
MAKE_WORD_TRANSLATION(mixer_device, "Mixer Module", "Mischermodul", "Mixer Module", "Blandningsmodul", "Moduł mieszacza", "Miksermodul", "", "Karışım Cihazı", "Modulo Miscela", "Modul mixera", "Mixer modul") // TODO translate
MAKE_WORD_TRANSLATION(mixer_device, "Mixer Module", "Mischermodul", "Mixer Module", "Blandningsmodul", "Moduł mieszacza", "Miksermodul", "", "Karışım Cihazı", "Modulo Miscela", "Modul mixera", "Směšovací modul") // TODO translate
MAKE_WORD_TRANSLATION(controller_device, "Controller Module", "Regelmodul", "Controller Module", "Styrmodul", "Moduł sterujący", "Styremodul", "", "Kontrol Ünitesi", "Modulo Controllo", "Modul ovládača", "Řídicí modul") // TODO translate
MAKE_WORD_TRANSLATION(switch_device, "Switch Module", "Schaltmodul", "Switch Module", "Relämodul", "Moduł przełączający", "Switch modul", "", "Anahtar", "Modulo Switch", "Spínací modul", "Spínací modul") // TODO translate
MAKE_WORD_TRANSLATION(gateway_device, "Gateway Module", "Gateway-Modul", "Gateway Module", "Gateway", "Moduł IP", "Gateway", "", "Ağ Geçidi", "Modulo Gateway", "Modul brány", "Modul brány") // TODO translate
@@ -64,7 +64,7 @@ MAKE_WORD_TRANSLATION(info_cmd, "list all values (verbose)", "Liste aller Werte"
MAKE_WORD_TRANSLATION(commands_cmd, "list all commands", "Liste aller Kommandos", "lijst van alle commando's", "", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy", "vypsat všechny příkazy") // TODO translate
MAKE_WORD_TRANSLATION(entities_cmd, "list all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "", "wyświetl wszsytkie encje", "Viser alle enheter", "", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity", "vypsat všechny entity") // TODO translate
MAKE_WORD_TRANSLATION(send_cmd, "send a telegram", "Sende EMS-Telegramm", "stuur een telegram", "", "wyślij telegram", "send et telegram", "", "Bir telegram gönder", "invia un telegramma", "poslať telegram", "odeslat telegram") // TODO translate
MAKE_WORD_TRANSLATION(read_cmd, "send read request", "", "", "", "", "", "", "", "", "", "") // TODO translate
MAKE_WORD_TRANSLATION(read_cmd, "send read request", "", "", "", "", "", "", "", "", "odoslať žiadosť o prečítanie", "") // TODO translate
MAKE_WORD_TRANSLATION(setiovalue_cmd, "set I/O value", "Setze Werte E/A", "instellen standaardwaarde", "", "ustaw wartość", "sett en io verdi", "", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io", "nastavit hodnotu I/O") // TODO translate
MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Protokollebene", "aanpassen log niveau", "", "zmień poziom log-u", "endre loggnivå", "", "Kayıt seviyesini değiştir", "cambia livello registrazione", "zmeniť úroveň protokolu", "změnit úroveň protokolování") // TODO translate
MAKE_WORD_TRANSLATION(fetch_cmd, "refresh all EMS values", "Aktualisiere alle EMS-Werte", "Verversen alle EMS waardes", "", "odśwież wszystkie wartości EMS", "oppfrisk alle EMS verdier", "", "Bütün EMS değerlerini yenile", "aggiornare tutti i valori EMS", "obnoviť všetky hodnoty EMS", "aktualizovat všechny EMS hodnoty") // TODO translate
@@ -79,6 +79,8 @@ MAKE_WORD_TRANSLATION(commands_response, "get response", "Hole Antwort", "Verzoe
MAKE_WORD_TRANSLATION(coldshot_cmd, "send a cold shot of water", "Zugabe einer Menge kalten Wassers", "", "", "uruchom tryśnięcie zimnej wody", "", "", "soğuk su gönder", "", "pošlite studenú dávku vody", "poslat studenou vodu") // TODO translate
MAKE_WORD_TRANSLATION(message_cmd, "send a message", "Eine Nachricht senden", "", "", "", "", "", "", "", "poslať správu", "odeslat zprávu") // TODO translate
MAKE_WORD_TRANSLATION(values_cmd, "list all values", "Liste alle Werte auf", "", "", "", "", "", "", "", "vypísať všetky hodnoty", "vypsat všechny hodnoty") // TODO translate
MAKE_WORD_TRANSLATION(showertimer_cmd, "enable shower timer", "aktiviere Duschzeitmessung", "", "", "", "", "", "", "", "povoliť časovač sprchovania", "") // TODO translate
MAKE_WORD_TRANSLATION(showeralert_cmd, "enable shower alert", "aktiviere Duschzeitwarnung", "", "", "", "", "", "", "", "povoliť upozornenie na sprchu", "") // TODO translate
// tags
MAKE_WORD_TRANSLATION(tag_hc1, "hc1", "HK1", "hc1", "VK1", "OG1", "hc1", "hc1", "ID1", "hc1", "hc1", "hc1")
@@ -214,7 +216,7 @@ MAKE_WORD_TRANSLATION(hot_water, "hot water", "Warmwasser", "warm water", "varmv
MAKE_WORD_TRANSLATION(pool, "pool", "Pool", "zwembad", "pool", "basen", "basseng", "piscine", "havuz", "piscina", "bazén", "bazén")
MAKE_WORD_TRANSLATION(outside_temp_alt, "outside temperature alt.", "Außentemp. alternativ", "alternatieve buitentemperatuur", "Alternativ utomhustemp.", "temp. zewn. alternat.", "alternativ utendørstemp.", "température extérieure alternative", "alternatif dış sıcaklık", "temperatura esterna alternativa", "vonkajšia teplota altern.", "venkovní teplota alt.")
MAKE_WORD_TRANSLATION(outside_temp_par, "outside temperature parallel", "Außentemp. parallel", "buitentemperatuur parallel", "Parallell utomhustemp.", "temp. zewn. równoległa", "parallell utendørstemp.", "température extérieure parallèle", "paralel dış sıcaklık", "temperatura esterna parallela", "paralelne s vonkajšou teplotou", "paralelně s venkovní teplotou")
MAKE_WORD_TRANSLATION(hp_preferred, "heatpump prefered", "Wärmepumpe bevorzugt", "voorkeur warmtepomp", "Värmepump föredraget", "preferowana pompa ciepła", "varmepumpe prioritert", "pompe à chaleur préférée", "tercih edilen pompa", "pompa di calore preferita", "preferované tepelné čerpadlo", "preferované tepelné čerpadlo")
MAKE_WORD_TRANSLATION(hp_preferred, "heatpump preferred", "Wärmepumpe bevorzugt", "voorkeur warmtepomp", "Värmepump föredraget", "preferowana pompa ciepła", "varmepumpe prioritert", "pompe à chaleur préférée", "tercih edilen pompa", "pompa di calore preferita", "preferované tepelné čerpadlo", "preferované tepelné čerpadlo")
MAKE_WORD_TRANSLATION(boiler_only, "boiler only", "nur Kessel", "uitsluitend cv ketel", "Värmepanna enbart", "tylko kocioł", "kun kjele", "chaudière uniquement", "sadece kazan", "solo caldaia", "len kotol", "pouze kotel")
MAKE_WORD_TRANSLATION(reduced_output, "reduced output", "Reduzierte Leistung", "gereduceerde output", "Reducerad produktion", "zmniejszona wydajność", "redusert ytelse", "sortie réduite", "düşürülmüş çıkış", "riduzione uscita", "znížený výkon", "snížený výkon")
MAKE_WORD_TRANSLATION(switchoff, "switch off hp", "WP ausschalten", "WP uitschakelen", "Värmepump avstängd", "wyłącz pompę ciepła", "slå av varmepumpe", "éteindre la PAC", "ısı pompasını kapat", "spegnimento pompa calore", "vypnúť tep. čerpadlo", "vypnout tepelné čerpadlo")
@@ -362,7 +364,7 @@ MAKE_TRANSLATION(emergencyOps, "emergencyops", "emergency operation", "Notbetrie
MAKE_TRANSLATION(emergencyTemp, "emergencytemp", "emergency temperature", "Notfalltemperatur", "Noodtemperatuur", "Nöddrift temperatur", "temperatura w trybie awaryjnym", "nødtemperatur", "température d'urgence", "acil durum sıcaklığı", "temperatura di emergenza", "núdzová teplota", "nouzová teplota")
MAKE_TRANSLATION(pumpMode, "pumpmode", "boiler pump mode", "Kesselpumpenmodus", "Ketelpomp modus", "", "tryb pracy pompy kotła", "pumpemodus", "", "pompa modu", "modalità pompa caldaia", "režim kotlového čerpadla", "režim čerpadla kotle") // TODO translate
MAKE_TRANSLATION(pumpCharacter, "pumpcharacter", "boiler pump characteristic", "Charakteristik der Kesselpumpe", "karakteristiek ketelpomp", "pannpumpsegenskaper", "charakterystyka pompy kotłowej", "kjelepumpekarakteristikk", "caractéristique de la pompe de la chaudière", "gazan nasosy", "caratteristica della pompa della caldaia", "charakteristika kotlového čerpadla", "charakteristika čerpadla kotle") // TODO translate
MAKE_TRANSLATION(pumpOnTemp, "pumpontemp", "pump logic temperature", "Pumpenlogiktemperatur", "", "", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(pumpOnTemp, "pumpontemp", "pump logic temperature", "Pumpenlogiktemperatur", "", "", "", "", "", "", "", "teplota logiky čerpadla", "") // TODO translate
MAKE_TRANSLATION(headertemp, "headertemp", "low loss header", "Hydr. Weiche", "open verdeler", "", "sprzęgło hydrauliczne", "", "bouteille de déc. hydr.", "isı bloğu gidiş suyu sıc.", "comp. idr.", "nízkostratová hlavica", "") // TODO translate
MAKE_TRANSLATION(heatblock, "heatblock", "heating block", "Wärmezelle", "Aanvoertemp. warmtecel", "", "blok grzewczy", "", "départ corps de chauffe", "Hid.denge kabı sıcaklığı", "mandata scamb. pr.", "vykurovací blok", "blok topení") // TODO translate
@@ -443,6 +445,7 @@ MAKE_TRANSLATION(maxHeatDhw, "maxheat", "heat limit", "Heizstab Limit für WW",
MAKE_TRANSLATION(auxHeaterOff, "auxheateroff", "disable aux heater", "Zusatzheizer deaktivieren", "Bijverwarming uitsc", "Blockera eltillskott", "wyłącz dogrzewacz", "deaktiver tilleggsvarme", "Désactiver chauff. d'app", "ilave ısıtıcıyı kapat", "disattivare i riscaldatori addizionali", "vypnúť pomocný ohrievač", "zakázat pomocné topení")
MAKE_TRANSLATION(auxHeaterStatus, "auxheaterstatus", "aux heater status", "Zusatzheizerstatus", "Bijverwarming", "Eltillskott Status", "status dogrzewacza", "status el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "stato riscaldatori addizionali", "stav pomocného ohrievača", "stav pomocného topení")
MAKE_TRANSLATION(auxHeaterLevel, "auxheaterlevel", "aux heater level", "Zusatzheizer", "Bijverwarming", "Eltillskott", "dogrzewacza", "el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "riscaldatori addizionali", "pomocného ohrievača", "pomocného topení")
MAKE_TRANSLATION(auxHeaterOnly, "auxheateronly", "aux heater only", "nur Zusatzheizer", "Alleen bijverwarming", "Eltillskott Enbart", "tylko dogrzewacz", "kun el tilleggsvarme", "Que chauffage auxiliaire", "sadece ilave ısıtıvcı", "solo riscaldatori addizionali", "iba pomocný ohrievač", "pouze pomocné topení")
MAKE_TRANSLATION(auxHeaterDelay, "auxheaterdelay", "aux heater on delay", "Zusatzheizer verzögert ein", "Bijverw. vertraagd aan", "Eltillskottfördröjning på", "opóźnienie włączenia dogrzewacza", "Tilleggsvarmer forsinket på", "Chauff app tempo marche", "ilave ısıtıcı beklemede", "ritardo riscaldatori addizionali", "oneskorenie prídavného ohrievača", "zpoždění zapnutí pomocného topení")
MAKE_TRANSLATION(silentMode, "silentmode", "silent mode", "Silentmodus", "Stiller gebruik", "Tyst läge", "tryb cichy", "stille modus", "Fct silencieux", "sessiz mod", "modalità silenziosa", "tichý režim", "tichý režim")
@@ -492,13 +495,13 @@ MAKE_TRANSLATION(hpPumpMode, "hppumpmode", "primary heatpump mode", "primärer W
MAKE_TRANSLATION(instantstart, "instantstart", "instant start", "Sofortstart", "", "", "natychmiastowy start", "", "", "", "", "okamžité spustenie", "okamžité spuštění") // TODO translate
MAKE_TRANSLATION(heatondelay, "heatondelay", "heat-on delay", "Einschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "Oneskorenie zapnutia kúreni", "zpoždění zapnutí topení") // TODO translate
MAKE_TRANSLATION(heatoffdelay, "heatoffdelay", "heat-off delay", "Ausschaltverzögerung Heizen", "", "", "opóźnienie włączania ogrzewania", "", "", "", "", "Oneskorenie vypnutia kúrenia", "zpoždění vypnutí topení") // TODO translate
MAKE_TRANSLATION(hpSetDiffPress, "hpsetdiffpress", "set differental pressure", "Pumpensolldruck", "", "", "różnica ciśnień", "", "", "", "", "nastaviť diferenčný tlak", "nastavení rozdílového tlaku") // TODO translate
MAKE_TRANSLATION(hpSetDiffPress, "hpsetdiffpress", "set differential pressure", "Pumpensolldruck", "", "", "różnica ciśnień", "", "", "", "", "nastaviť diferenčný tlak", "nastavení rozdílového tlaku") // TODO translate
MAKE_TRANSLATION(hpFan, "fan", "fan", "Lüfter", "", "", "wentylator", "", "", "", "", "ventilátor", "ventilátor") // TODO translate
MAKE_TRANSLATION(hpShutdown, "shutdown", "shutdown", "Abschalten", "", "", "wyłączenie", "", "", "", "", "vypnutie", "vypnutí") // TODO translate
MAKE_TRANSLATION(pc0Flow, "pc0flow", "Flow PC0", "Durchfluss PC0", "", "", "", "", "", "", "", "", "průtok PC0") // TODO translate
MAKE_TRANSLATION(pc1Flow, "pc1flow", "Flow PC1", "Durchfluss PC1", "", "", "", "", "", "", "", "", "průtok PC1") // TODO translate
MAKE_TRANSLATION(pc1On, "pc1on", "PC1", "PC1", "", "", "", "", "", "", "", "", "PC1") // TODO translate
MAKE_TRANSLATION(pc1Rate, "pc1rate", "PC1 rate", "PC1 Rate", "", "", "", "", "", "", "", "", "míra PC1") // TODO translate
MAKE_TRANSLATION(pc0Flow, "pc0flow", "Flow PC0", "Durchfluss PC0", "", "", "", "", "", "", "", "prietok PC0", "průtok PC0") // TODO translate
MAKE_TRANSLATION(pc1Flow, "pc1flow", "Flow PC1", "Durchfluss PC1", "", "", "", "", "", "", "", "prietok PC1", "průtok PC1") // TODO translate
MAKE_TRANSLATION(pc1On, "pc1on", "PC1", "PC1", "", "", "", "", "", "", "", "PC1", "PC1") // TODO translate
MAKE_TRANSLATION(pc1Rate, "pc1rate", "PC1 rate", "PC1 Rate", "", "", "", "", "", "", "", "sadzba PC1", "míra PC1") // TODO translate
// hybrid heatpump
MAKE_TRANSLATION(hybridStrategy, "hybridstrategy", "hybrid control strategy", "Hybrid-Steuerungsstrategie", "Hybride strategie", "Hybrid kontrollstrategi", "strategia sterowania hybrydowego", "hybrid kontrollstrategi", "stratégie contrôle hybride", "hibrit kontrol stratejisi", "strategia comtrollo ibrido", "hybridná stratégia riadenia", "strategie hybridního řízení")
@@ -574,8 +577,15 @@ MAKE_TRANSLATION(meterCool, "metercool", "meter cooling", "Messung Kühlen", "",
MAKE_TRANSLATION(meterWw, "meter", "meter", "Messung", "", "", "licznik", "", "", "", "", "počítadlo", "počítadlo") // TODO translate
MAKE_TRANSLATION(gasMeterHeat, "gasmeterheat", "gas meter heating", "Gaszähler Heizen", "", "", "licznik gazu na ogrzewanie", "", "", "", "", "počítadlo plynu kúrenia", "počítadlo plynu pro vytápění") // TODO translate
MAKE_TRANSLATION(gasMeterWw, "gasmeter", "gas meter", "Gaszähler", "", "", "licznik gazu", "", "", "", "", "počítadlo plynu", "počítadlo plynu") // TODO translate
MAKE_TRANSLATION(hpCurrPower, "hpcurrpower", "compressor current power", "akt. Kompressorleistung", "", "", "", "", "", "", "", "", "aktuální výkon kompresoru") // TODO translate
MAKE_TRANSLATION(hpPowerLimit, "hppowerlimit", "power limit", "Leistungsgrenze", "", "", "", "", "", "", "", "", "omezení výkonu") // TODO translate
MAKE_TRANSLATION(hpCurrPower, "hpcurrpower", "compressor current power", "akt. Kompressorleistung", "", "", "", "", "", "", "", "aktuálny výkon kompresoru", "aktuální výkon kompresoru") // TODO translate
MAKE_TRANSLATION(hpPowerLimit, "hppowerlimit", "power limit", "Leistungsgrenze", "", "", "", "", "", "", "", "obmedzenie výkonu", "omezení výkonu") // TODO translate
MAKE_TRANSLATION(powerReduction, "powerreduction", "power reduction", "Leistungsverringerung", "", "", "", "", "", "", "", "obmedzenie výkonu", "omezení výkonu") // TODO translate
MAKE_TRANSLATION(fuelHeat, "fuelheat", "fuel consuption heating", "Verbrauch Heizen", "", "", "", "", "", "", "", "obmedzenie výkonu", "omezení výkonu") // TODO translate
MAKE_TRANSLATION(fuelDhw, "fueldhw", "fuel consuption", "Verbrauch", "", "", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(elHeat, "elheat", "el. consuption heating", "el. Verbrauch Heizen", "", "", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(elDhw, "eldhw", "el consuption", "el. Verbrauch", "", "", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(elGenHeat, "elgenheat", "el. generation heating", "el. Erzeugung Heizen", "", "", "", "", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(elGenDhw, "elgendhw", "el generation", "el. Erzeugung", "", "", "", "", "", "", "", "", "") // TODO translate
// HIU
MAKE_TRANSLATION(netFlowTemp, "netflowtemp", "heat network flow temp", "Systemvorlauftemperatur", "Netto aanvoertemperatuur", "", "temp. zasilania sieci cieplnej", "", "", "ısıtma şebekesi akış derecesi", "temperatura di mandata della rete di riscaldamento", "teplota prívodu tepelnej siete", "teplota přívodu tepelné sítě") // TODO translate
@@ -619,7 +629,7 @@ MAKE_TRANSLATION(wwActive, "active", "active", "aktiv", "Actief", "Aktiv", "akty
MAKE_TRANSLATION(ww3wayValve, "3wayvalve", "3-way valve active", "3-Wege-Ventil aktiv", "3-wegklep actief", "Trevägsventil aktiv", "zawór 3-drogowy aktywny", "aktiv trevisventil", "vanne 3 voies active", "3 yollu vana", "valvola 3-vie", "3-cestný ventil aktívny", "aktivní 3-cestný ventil")
MAKE_TRANSLATION(wwMixerTemp, "mixertemp", "mixer temperature", "Mischertemperatur", "Mixertemperatuur", "Blandningsventil-tempertur", "temperatura mieszacza", "temperatur blandeventil", "température mélangeur", "karıştırıcı sıcaklığı", "temperatura miscelatore", "teplota mixéra", "teplota směšovače")
MAKE_TRANSLATION(wwStarts, "starts", "starts", "Anzahl Starts", "Aantal starts", "Antal starter", "liczba załączeń", "antall starter", "démarrages", "başlıyor", "avvii", "Počet štartov", "Počet startů")
MAKE_TRANSLATION(wwStarts2, "starts2", "control starts2", "Anzahl Starts Kreis 2 ", "Aantal starts circuit 2", "Antal starter Krets 2", "liczba załączeń 2", "antall starter krets 2", "démarrages contrôle 2", "devre 2 başlıyor", "avvii controllati 2", "Okruh 2 počet štartov", "Počet startů okruh 2")
MAKE_TRANSLATION(wwStartsHp, "startshp", "starts hp", "Anzahl Starts WP", "", "", "", "", "", "", "", "Počet spustení hp", "") // TODO translate
MAKE_TRANSLATION(wwWorkM, "workm", "active time", "aktive Zeit", "Actieve tijd", "Aktiv Tid", "czas aktywności", "driftstid", "temps actif", "aktif zaman", "tempo attivo", "aktívny čas", "aktivní čas")
MAKE_TRANSLATION(wwHystOn, "hyston", "hysteresis on temperature", "Einschalttemperaturdifferenz", "Inschakeltemperatuurverschil", "Hysteres PÅ-temperatur", "histereza załączania", "innkoblingstemperaturforskjell", "hystérésis température allumage", "çalışma sıcaklığı farkı", "differenza di temperatura di accensione", "hysterézia teploty", "hystereze zapnutí")
MAKE_TRANSLATION(wwHystOff, "hystoff", "hysteresis off temperature", "Ausschalttemperaturdifferenz", "Uitschakeltemperatuurverschil", "Hysteres AV-temperatur", "histereza wyłączania", "utkoblingstemperaturforskjell", "hystérésis température extinction", "kapatma sıcaklığı farkı", "differenza di temperatura di spegnimento", "teplota hysterézie", "hystereze vypnutí")
@@ -810,6 +820,10 @@ MAKE_TRANSLATION(m1WorkTime, "m1worktime", "differential control working time",
MAKE_TRANSLATION(energyLastHour, "energylasthour", "energy last hour", "Energie letzte Std", "Energie laatste uur", "Energi Senaste Timmen", "energia w ciągu ostatniej godziny", "energi siste time", "énergie dernière heure", "son saat enerji", "Eenergia ultima ora", "energia za poslednú hodinu", "energie za poslední hodinu")
MAKE_TRANSLATION(energyTotal, "energytotal", "total energy", "Gesamtenergie", "Totale energie", "Total Energi", "energia całkowita", "total energi", "énergie totale", "toplam enerji", "energia totale", "celková energia", "celková energie")
MAKE_TRANSLATION(energyToday, "energytoday", "total energy today", "Energie heute", "Energie vandaag", "Total Energi Idag", "energia całkowita dzisiaj", "total energi i dag", "énergie totale aujourd'hui", "bugün toplam enerji", "totale energia giornaliera", "celková energia dnes", "celková energie dnes")
MAKE_TRANSLATION(cyl3BottomTemp, "cyl3bottomtemp", "third cylinder bottom temperature (TS11)", "Speichertemperatur unten (TS11)") // TODO translate
MAKE_TRANSLATION(cylTopTemp, "cyltoptemp", "cylinder top temperature (TS10)", "Speichertemperatur oben (TS10)") // TODO translate
MAKE_TRANSLATION(transferPumpMod, "transferpumpmod", "transfer pump modulation", "Transferpumpenmodulation") // TODO translate
MAKE_TRANSLATION(transferPump, "transferpump", "transfer pump", "Transferpumpe") // TODO translate
// solar dhw
MAKE_TRANSLATION(wwColdTemp, "coldtemp", "cold water", "Kaltwasser", "", "", "zimna woda", "", "", "", "", "studená voda", "studená voda") // TODO translate
@@ -830,7 +844,7 @@ MAKE_TRANSLATION(wwKeepWarm, "keepwarm", "keep warm", "Warmhalten", "Warm houde"
MAKE_TRANSLATION(wwStatus2, "status2", "status 2", "Status 2", "Status 2", "Status 2", "status 2", "status 2", "statut 2", "durum 2", "Status 2", "stav 2", "stav 2")
MAKE_TRANSLATION(wwPumpMod, "pumpmod", "pump modulation", "Pumpenmodulation", "Pompmodulatie", "Pumpmodulering", "modulacja pompy", "pumpemodulering", "modulation de pompe", "pompa modülasyonu", "modulazione pompa", "modulácia čerpadla", "modulace čerpadla")
MAKE_TRANSLATION(wwFlow, "flow", "flow rate", "Volumenstrom", "Doorstroomsnelheid", "Flöde", "przepływ", "strømningshastighet", "débit", "akış hızı", "portata flusso", "prietok", "průtok")
// MAKE_TRANSLATION(wwRetValve, "retvalve", "return valve", "Rücklauf Ventil", "", "", "", "", "", "", "", "", "zpětný ventil")
// MAKE_TRANSLATION(wwRetValve, "retvalve", "return valve", "Rücklauf Ventil", "", "", "", "", "", "", "", "spätný ventil", "zpětný ventil")
// extra mixer dhw
MAKE_TRANSLATION(wwRequiredTemp, "requiredtemp", "required temperature", "benötigte Temperatur", "Benodigde temperatuur", "Nödvändig Temperatur", "temperatura wymagana", "nødvendig temperatur", "température requise", "gerekli sıcaklık", "temperatura richiesta", "požadovaná teplota", "požadovaná teplota")

View File

@@ -38,7 +38,7 @@ void Modbus::start(uint8_t systemServerId, uint16_t port, uint8_t max_clients, u
}
}
modbusServer_->start(port, max_clients, timeout);
LOG_INFO("Modbus server with ID %d started on port %d", systemServerId, port);
LOG_INFO("Starting Modbus service (ID %d, port %d)", systemServerId, port);
#else
if (!check_parameter_order()) {
LOG_ERROR("Unable to enable Modbus - the parameter list order is corrupt. This is a firmware bug.");
@@ -275,20 +275,8 @@ ModbusMessage Modbus::handleRead(const ModbusMessage & request) {
auto register_offset = start_address - tag * REGISTER_BLOCK_SIZE;
const auto & dev_it =
std::find_if(EMSESP::emsdevices.begin(), EMSESP::emsdevices.end(), [&](const std::unique_ptr<EMSdevice> & x) { return x->device_type() == device_type; });
if (dev_it == EMSESP::emsdevices.end()) {
// device not found => invalid server ID
LOG_ERROR("device with type %d not found => invalid server ID", device_type);
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
return response;
}
const auto & dev = *dev_it;
// binary search in modbus infos
auto key = EntityModbusInfoKey(dev->device_type(), tag_type, register_offset);
auto key = EntityModbusInfoKey(device_type, tag_type, register_offset);
const auto & modbusInfo = std::lower_bound(std::begin(modbus_register_mappings),
std::end(modbus_register_mappings),
@@ -312,7 +300,15 @@ ModbusMessage Modbus::handleRead(const ModbusMessage & request) {
}
auto buf = std::vector<uint16_t>(num_words);
auto error_code = dev->get_modbus_value(tag, modbusInfo->short_name, buf);
int error_code = -1;
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice->device_type() == device_type) {
error_code = emsdevice->get_modbus_value(tag, modbusInfo->short_name, buf);
if (!error_code) {
break;
}
}
}
if (error_code) {
LOG_ERROR("Unable to read raw device value %s for tag=%d - error_code = %d", modbusInfo->short_name, (int)tag, error_code);
response.setError(request.getServerID(), request.getFunctionCode(), SERVER_DEVICE_FAILURE);
@@ -370,22 +366,9 @@ ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
LOG_DEBUG("Tag %d, offset %d", tag, register_offset);
const auto & dev_it =
std::find_if(EMSESP::emsdevices.begin(), EMSESP::emsdevices.end(), [&](const std::unique_ptr<EMSdevice> & x) { return x->device_type() == device_type; });
if (dev_it == EMSESP::emsdevices.end()) {
// device not found => invalid server ID
LOG_ERROR("device_type (%d) not found => invalid server ID", device_type);
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
return response;
}
const auto & dev = *dev_it;
LOG_DEBUG("found device '%s' of type %d", dev->name().c_str(), dev->device_type());
// binary search in modbus infos
auto key = EntityModbusInfoKey(dev->device_type(), tag_type, register_offset);
auto key = EntityModbusInfoKey(device_type, tag_type, register_offset);
auto modbusInfo = std::lower_bound(std::begin(modbus_register_mappings),
std::end(modbus_register_mappings),
key,
@@ -412,9 +395,16 @@ ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
}
JsonDocument input_doc;
JsonObject input = input_doc.to<JsonObject>();
auto error_code = dev->modbus_value_to_json(tag, modbusInfo->short_name, data, input);
JsonObject input = input_doc.to<JsonObject>();
int error_code = -1;
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice->device_type() == device_type) {
error_code = emsdevice->modbus_value_to_json(tag, modbusInfo->short_name, data, input);
if (!error_code) {
break;
}
}
}
if (error_code) {
// error getting modbus value as json
LOG_ERROR("error getting modbus value as json, error code = %d", error_code);
@@ -424,9 +414,9 @@ ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
std::string path;
if (tag < DeviceValueTAG::TAG_HC1) {
path = std::string("ems-esp/") + std::string(EMSdevice::device_type_2_device_name(dev->device_type())) + "/" + modbusInfo->short_name;
path = std::string("ems-esp/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + modbusInfo->short_name;
} else {
path = std::string("ems-esp/") + std::string(EMSdevice::device_type_2_device_name(dev->device_type())) + "/" + EMSdevice::tag_to_mqtt(tag) + "/"
path = std::string("ems-esp/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + EMSdevice::tag_to_mqtt(tag) + "/"
+ modbusInfo->short_name;
}

View File

@@ -2,7 +2,7 @@
#include "emsdevice.h"
/*
* This file is auto-generated by the update_modbus_registers.sh script. Do not modify.
* This file is auto-generated. Do not modify.
*/
// clang-format off
@@ -87,114 +87,116 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPower), 137, 1), // hppower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpMaxPower), 138, 1), // hpmaxpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pvMaxComp), 139, 1), // pvmaxcomp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpSetDiffPress), 140, 1), // hpsetdiffpress
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCompOn), 141, 1), // hpcompon
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpActivity), 142, 1), // hpactivity
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrinePumpSpd), 143, 1), // hpbrinepumpspd
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpSwitchValve), 144, 1), // hpswitchvalve
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCompSpd), 145, 1), // hpcompspd
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCircSpd), 146, 1), // hpcircspd
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrineIn), 147, 1), // hpbrinein
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrineOut), 148, 1), // hpbrineout
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc0), 149, 1), // hptc0
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc1), 150, 1), // hptc1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc3), 151, 1), // hptc3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr1), 152, 1), // hptr1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr3), 153, 1), // hptr3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr4), 154, 1), // hptr4
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr5), 155, 1), // hptr5
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr6), 156, 1), // hptr6
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr7), 157, 1), // hptr7
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTl2), 158, 1), // hptl2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPl1), 159, 1), // hppl1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPh1), 160, 1), // hpph1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTa4), 161, 1), // hpta4
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTw1), 162, 1), // hptw1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(poolSetTemp), 163, 1), // poolsettemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hp4wayValve), 164, 1), // hp4way
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput1), 165, 1), // hpin1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn1Opt), 166, 8), // hpin1opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput2), 174, 1), // hpin2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn2Opt), 175, 8), // hpin2opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput3), 183, 1), // hpin3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn3Opt), 184, 8), // hpin3opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput4), 192, 1), // hpin4
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn4Opt), 193, 8), // hpin4opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maxHeatComp), 201, 1), // maxheatcomp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maxHeatHeat), 202, 1), // maxheatheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(manDefrost), 203, 1), // mandefrost
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pvCooling), 204, 1), // pvcooling
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterOnly), 205, 1), // auxheateronly
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterOff), 206, 1), // auxheateroff
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterStatus), 207, 1), // auxheaterstatus
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterDelay), 208, 1), // auxheaterdelay
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxMaxLimit), 209, 1), // auxmaxlimit
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxLimitStart), 210, 1), // auxlimitstart
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeatMode), 211, 1), // auxheatrmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystHeat), 212, 1), // hphystheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystCool), 213, 1), // hphystcool
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystPool), 214, 1), // hphystpool
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentMode), 215, 1), // silentmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentFrom), 216, 1), // silentfrom
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentTo), 217, 1), // silentto
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(minTempSilent), 218, 1), // mintempsilent
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempParMode), 219, 1), // tempparmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeatMixValve), 220, 1), // auxheatmix
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempDiffHeat), 221, 1), // tempdiffheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempDiffCool), 222, 1), // tempdiffcool
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(vp_cooling), 223, 1), // vpcooling
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 224, 1), // heatcable
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(VC0valve), 225, 1), // vc0valve
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(primePump), 226, 1), // primepump
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(primePumpMod), 227, 1), // primepumpmod
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hp3wayValve), 228, 1), // hp3way
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep1), 229, 1), // elheatstep1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep2), 230, 1), // elheatstep2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep3), 231, 1), // elheatstep3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpEA0), 232, 1), // hpea0
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPumpMode), 233, 1), // hppumpmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpFan), 234, 1), // fan
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpShutdown), 235, 1), // shutdown
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCurrPower), 236, 1), // hpcurrpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPowerLimit), 237, 1), // hppowerlimit
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc0Flow), 238, 1), // pc0flow
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1Flow), 239, 1), // pc1flow
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1On), 240, 1), // pc1on
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1Rate), 241, 1), // pc1rate
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(exhaustTemp), 242, 1), // exhausttemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnGas), 243, 1), // burngas
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnGas2), 244, 1), // burngas2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(flameCurr), 245, 1), // flamecurr
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(fanWork), 246, 1), // fanwork
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(ignWork), 247, 1), // ignwork
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(oilPreHeat), 248, 1), // oilpreheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPower), 249, 1), // burnminpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMaxPower), 250, 1), // burnmaxpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPeriod), 251, 1), // burnminperiod
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(absBurnPow), 252, 1), // absburnpow
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatblock), 253, 1), // heatblock
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOn), 254, 1), // boilhyston
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOff), 255, 1), // boilhystoff
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOn), 256, 1), // boil2hyston
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOff), 257, 1), // boil2hystoff
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveOn), 258, 1), // curveon
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveBase), 259, 1), // curvebase
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveEnd), 260, 1), // curveend
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(summertemp), 261, 1), // summertemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrostmode), 262, 1), // nofrostmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrosttemp), 263, 1), // nofrosttemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(gasMeterHeat), 264, 2), // gasmeterheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat2), 266, 2), // nrgheat2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nomPower), 268, 1), // nompower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(netFlowTemp), 269, 1), // netflowtemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatValve), 270, 1), // heatvalve
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(keepWarmTemp), 271, 1), // keepwarmtemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(setReturnTemp), 272, 1), // setreturntemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingOn), 273, 1), // heating
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(powerReduction), 140, 1), // powerreduction
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpSetDiffPress), 141, 1), // hpsetdiffpress
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCompOn), 142, 1), // hpcompon
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpActivity), 143, 1), // hpactivity
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrinePumpSpd), 144, 1), // hpbrinepumpspd
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpSwitchValve), 145, 1), // hpswitchvalve
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCompSpd), 146, 1), // hpcompspd
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCircSpd), 147, 1), // hpcircspd
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrineIn), 148, 1), // hpbrinein
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrineOut), 149, 1), // hpbrineout
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc0), 150, 1), // hptc0
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc1), 151, 1), // hptc1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc3), 152, 1), // hptc3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr1), 153, 1), // hptr1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr3), 154, 1), // hptr3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr4), 155, 1), // hptr4
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr5), 156, 1), // hptr5
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr6), 157, 1), // hptr6
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr7), 158, 1), // hptr7
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTl2), 159, 1), // hptl2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPl1), 160, 1), // hppl1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPh1), 161, 1), // hpph1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTa4), 162, 1), // hpta4
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTw1), 163, 1), // hptw1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(poolSetTemp), 164, 1), // poolsettemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hp4wayValve), 165, 1), // hp4way
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput1), 166, 1), // hpin1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn1Opt), 167, 8), // hpin1opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput2), 175, 1), // hpin2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn2Opt), 176, 8), // hpin2opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput3), 184, 1), // hpin3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn3Opt), 185, 8), // hpin3opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput4), 193, 1), // hpin4
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn4Opt), 194, 8), // hpin4opt
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maxHeatComp), 202, 1), // maxheatcomp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maxHeatHeat), 203, 1), // maxheatheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(manDefrost), 204, 1), // mandefrost
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pvCooling), 205, 1), // pvcooling
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterOnly), 206, 1), // auxheateronly
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterOff), 207, 1), // auxheateroff
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterStatus), 208, 1), // auxheaterstatus
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterLevel), 209, 1), // auxheaterlevel
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterDelay), 210, 1), // auxheaterdelay
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxMaxLimit), 211, 1), // auxmaxlimit
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxLimitStart), 212, 1), // auxlimitstart
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeatMode), 213, 1), // auxheatrmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystHeat), 214, 1), // hphystheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystCool), 215, 1), // hphystcool
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystPool), 216, 1), // hphystpool
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentMode), 217, 1), // silentmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentFrom), 218, 1), // silentfrom
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentTo), 219, 1), // silentto
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(minTempSilent), 220, 1), // mintempsilent
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempParMode), 221, 1), // tempparmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeatMixValve), 222, 1), // auxheatmix
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempDiffHeat), 223, 1), // tempdiffheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempDiffCool), 224, 1), // tempdiffcool
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(vp_cooling), 225, 1), // vpcooling
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 226, 1), // heatcable
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(VC0valve), 227, 1), // vc0valve
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(primePump), 228, 1), // primepump
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(primePumpMod), 229, 1), // primepumpmod
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hp3wayValve), 230, 1), // hp3way
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep1), 231, 1), // elheatstep1
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep2), 232, 1), // elheatstep2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep3), 233, 1), // elheatstep3
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpEA0), 234, 1), // hpea0
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPumpMode), 235, 1), // hppumpmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpFan), 236, 1), // fan
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpShutdown), 237, 1), // shutdown
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCurrPower), 238, 1), // hpcurrpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPowerLimit), 239, 1), // hppowerlimit
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc0Flow), 240, 1), // pc0flow
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1Flow), 241, 1), // pc1flow
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1On), 242, 1), // pc1on
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1Rate), 243, 1), // pc1rate
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(exhaustTemp), 244, 1), // exhausttemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnGas), 245, 1), // burngas
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnGas2), 246, 1), // burngas2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(flameCurr), 247, 1), // flamecurr
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(fanWork), 248, 1), // fanwork
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(ignWork), 249, 1), // ignwork
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(oilPreHeat), 250, 1), // oilpreheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPower), 251, 1), // burnminpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMaxPower), 252, 1), // burnmaxpower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPeriod), 253, 1), // burnminperiod
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(absBurnPow), 254, 1), // absburnpow
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatblock), 255, 1), // heatblock
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOn), 256, 1), // boilhyston
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOff), 257, 1), // boilhystoff
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOn), 258, 1), // boil2hyston
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOff), 259, 1), // boil2hystoff
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveOn), 260, 1), // curveon
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveBase), 261, 1), // curvebase
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveEnd), 262, 1), // curveend
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(summertemp), 263, 1), // summertemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrostmode), 264, 1), // nofrostmode
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrosttemp), 265, 1), // nofrosttemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(gasMeterHeat), 266, 2), // gasmeterheat
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat2), 268, 2), // nrgheat2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nomPower), 270, 1), // nompower
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(netFlowTemp), 271, 1), // netflowtemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatValve), 272, 1), // heatvalve
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(keepWarmTemp), 273, 1), // keepwarmtemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(setReturnTemp), 274, 1), // setreturntemp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingOn), 275, 1), // heating
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgWw), 0, 2), // nrg
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(meterWw), 2, 2), // meter
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(upTimeCompWw), 4, 2), // uptimecomp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwStarts2), 6, 2), // starts2
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwStartsHp), 6, 2), // startshp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgConsCompWw), 8, 2), // nrgconscomp
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(auxElecHeatNrgConsWw), 10, 2), // auxelecheatnrgcons
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgSuppWw), 12, 2), // nrgsupp
@@ -398,13 +400,6 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(mixerSetTime), 5, 1), // valvesettime
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowTempVf), 6, 1), // flowtempvf
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowtempoffset), 7, 1), // flowtempoffset
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(flowTempHc), 0, 1), // flowtemphc
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(valveStatus), 1, 1), // valvestatus
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(flowSetTemp), 2, 1), // flowsettemp
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(wwPumpStatus), 3, 1), // pumpstatus
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(activated), 4, 1), // activated
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(mixerSetTime), 5, 1), // valvesettime
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_DHW, FL_(flowtempoffset), 6, 1), // flowtempoffset
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorTemp), 0, 1), // collectortemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylBottomTemp), 1, 1), // cylbottomtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump), 2, 1), // solarpump
@@ -428,40 +423,45 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2), 22, 1), // solarpump2
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2Mod), 23, 1), // solarpump2mod
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cyl2BottomTemp), 24, 1), // cyl2bottomtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatExchangerTemp), 25, 1), // heatexchangertemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylPumpMod), 26, 1), // cylpumpmod
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(valveStatus), 27, 1), // valvestatus
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(vs1Status), 28, 1), // vs1status
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorMaxTemp), 29, 1), // collectormaxtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorMinTemp), 30, 1), // collectormintemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyToday), 31, 2), // energytoday
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyTotal), 33, 2), // energytotal
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pump2WorkTime), 35, 2), // pump2worktime
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(m1WorkTime), 37, 2), // m1worktime
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatTransferSystem), 39, 1), // heattransfersystem
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(externalCyl), 40, 1), // externalcyl
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(thermalDisinfect), 41, 1), // thermaldisinfect
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatMetering), 42, 1), // heatmetering
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(activated), 43, 1), // activated
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpMode), 44, 1), // solarpumpmode
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpKick), 45, 1), // solarpumpkick
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(plainWaterMode), 46, 1), // plainwatermode
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(doubleMatchFlow), 47, 1), // doublematchflow
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pump2MinMod), 48, 1), // pump2minmod
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2TurnonDiff), 49, 1), // turnondiff2
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2TurnoffDiff), 50, 1), // turnoffdiff2
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2Kick), 51, 1), // pump2kick
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(climateZone), 52, 1), // climatezone
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector1Area), 53, 1), // collector1area
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector1Type), 54, 1), // collector1type
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Area), 55, 1), // collector2area
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Type), 56, 1), // collector2type
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylPriority), 57, 1), // cylpriority
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCntFlowTemp), 58, 1), // heatcntflowtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCntRetTemp), 59, 1), // heatcntrettemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCnt), 60, 1), // heatcnt
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(swapFlowTemp), 61, 1), // swapflowtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(swapRetTemp), 62, 1), // swaprettemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cyl3BottomTemp), 25, 1), // cyl3bottomtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylTopTemp), 26, 1), // cyltoptemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatExchangerTemp), 27, 1), // heatexchangertemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylPumpMod), 28, 1), // cylpumpmod
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(valveStatus), 29, 1), // valvestatus
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(vs1Status), 30, 1), // vs1status
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(vs3Status), 31, 1), // vs3status
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(transferPump), 32, 1), // transferpump
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(transferPumpMod), 33, 1), // transferpumpmod
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorMaxTemp), 34, 1), // collectormaxtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorMinTemp), 35, 1), // collectormintemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyToday), 36, 2), // energytoday
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyTotal), 38, 2), // energytotal
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pump2WorkTime), 40, 2), // pump2worktime
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(m1WorkTime), 42, 2), // m1worktime
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatTransferSystem), 44, 1), // heattransfersystem
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(externalCyl), 45, 1), // externalcyl
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(thermalDisinfect), 46, 1), // thermaldisinfect
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatMetering), 47, 1), // heatmetering
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(activated), 48, 1), // activated
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpMode), 49, 1), // solarpumpmode
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpKick), 50, 1), // solarpumpkick
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(plainWaterMode), 51, 1), // plainwatermode
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(doubleMatchFlow), 52, 1), // doublematchflow
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pump2MinMod), 53, 1), // pump2minmod
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2TurnonDiff), 54, 1), // turnondiff2
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2TurnoffDiff), 55, 1), // turnoffdiff2
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2Kick), 56, 1), // pump2kick
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(climateZone), 57, 1), // climatezone
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector1Area), 58, 1), // collector1area
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector1Type), 59, 1), // collector1type
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Area), 60, 1), // collector2area
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Type), 61, 1), // collector2type
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylPriority), 62, 1), // cylpriority
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCntFlowTemp), 63, 1), // heatcntflowtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCntRetTemp), 64, 1), // heatcntrettemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCnt), 65, 1), // heatcnt
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(swapFlowTemp), 66, 1), // swapflowtemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(swapRetTemp), 67, 1), // swaprettemp
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DHW, FL_(wwMinTemp), 0, 1), // mintemp
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airHumidity), 0, 1), // airhumidity
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(dewTemperature), 1, 1), // dewtemperature
@@ -483,25 +483,33 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseMode), 17, 1), // lownoisemode
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStart), 18, 1), // lownoisestart
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStop), 19, 1), // lownoisestop
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hybridDHW), 20, 1), // hybriddhw
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceGas), 21, 1), // energypricegas
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceEl), 22, 1), // energypriceel
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPricePV), 23, 1), // energyfeedpv
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(switchOverTemp), 24, 1), // switchovertemp
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airPurgeMode), 25, 1), // airpurgemode
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatPumpOutput), 26, 1), // heatpumpoutput
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(coolingCircuit), 27, 1), // coolingcircuit
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(compStartMod), 28, 1), // compstartmod
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatDrainPan), 29, 1), // heatdrainpan
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 30, 1), // heatcable
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgTotal), 31, 2), // nrgtotal
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat), 33, 2), // nrgheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterTotal), 35, 2), // metertotal
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterComp), 37, 2), // metercomp
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterEHeat), 39, 2), // metereheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterHeat), 41, 2), // meterheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(nrgWw), 0, 2), // nrg
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(meterWw), 2, 2), // meter
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceGas), 20, 1), // energypricegas
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceEl), 21, 1), // energypriceel
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPricePV), 22, 1), // energyfeedpv
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(switchOverTemp), 23, 1), // switchovertemp
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airPurgeMode), 24, 1), // airpurgemode
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatPumpOutput), 25, 1), // heatpumpoutput
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(coolingCircuit), 26, 1), // coolingcircuit
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(compStartMod), 27, 1), // compstartmod
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatDrainPan), 28, 1), // heatdrainpan
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 29, 1), // heatcable
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgTotal), 30, 2), // nrgtotal
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat), 32, 2), // nrgheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterTotal), 34, 2), // metertotal
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterComp), 36, 2), // metercomp
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterEHeat), 38, 2), // metereheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterHeat), 40, 2), // meterheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatingStarts), 42, 2), // heatingstarts
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(fuelHeat), 44, 2), // fuelheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elHeat), 46, 2), // elheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elGenHeat), 48, 2), // elgenheat
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(hybridDHW), 0, 1), // hybriddhw
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(nrgWw), 1, 2), // nrg
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(meterWw), 3, 2), // meter
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(wwStartsHp), 5, 2), // startshp
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(fuelDhw), 7, 2), // fueldhw
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(elDhw), 9, 2), // eldhw
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(elGenDhw), 11, 2), // elgendhw
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(activated), 0, 1), // activated
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(flowTempHc), 1, 1), // flowtemphc
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(status), 2, 1), // status
@@ -527,6 +535,36 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(ventMode), 6, 1), // ventmode
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(airquality), 7, 1), // airquality
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(airHumidity), 8, 1), // airhumidity
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(selRoomTemp), 0, 1), // seltemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTemp), 1, 1), // temp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCurTemp2), 2, 1), // curtemp2
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(hydrTemp), 3, 1), // hydrTemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwPump), 4, 1), // pump
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(flowtempoffset), 5, 1), // flowtempoffset
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHystOn), 6, 1), // hyston
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHystOff), 7, 1), // hystoff
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDisinfectionTemp), 8, 1), // disinfectiontemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCirc), 9, 1), // circ
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCircMode), 10, 1), // circmode
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTempStatus), 11, 1), // tempstatus
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwMaxTemp), 12, 1), // maxtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDiffTemp), 13, 1), // difftemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRedTemp), 14, 1), // redtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRequiredTemp), 15, 1), // requiredtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwStorageTemp1), 16, 1), // storagetemp1
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwColdTemp), 17, 1), // coldtemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTemp5), 18, 1), // temp5
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(retTemp), 19, 1), // rettemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHotTemp), 20, 1), // hottemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDailyTemp), 21, 1), // dailytemp
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCircTc), 22, 1), // circtc
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwKeepWarm), 23, 1), // keepwarm
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwStatus2), 24, 1), // status2
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwPumpMod), 25, 1), // pumpmod
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwFlow), 26, 1), // flow
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(valveReturn), 27, 1), // valvereturn
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(deltaTRet), 28, 1), // deltatret
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(errorDisp), 29, 1), // errordisp
};
} // namespace emsesp

View File

@@ -386,16 +386,6 @@ void Mqtt::start() {
// add the 'publish' command ('call system publish' in console or via API)
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, FL_(publish_cmd));
// create last will topic with the base prefixed. It has to be static because the client destroys the reference
static char will_topic[MQTT_TOPIC_MAX_SIZE];
if (!Mqtt::base().empty()) {
snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "%s/status", Mqtt::base().c_str());
} else {
snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "status");
}
EMSESP::esp8266React.setWill(will_topic); // with qos 1, retain true
#if defined(EMSESP_STANDALONE)
Mqtt::on_connect(); // simulate an MQTT connection
#endif
@@ -468,6 +458,7 @@ void Mqtt::on_disconnect(espMqttClientTypes::DisconnectReason reason) {
return;
}
connecting_ = false;
connectcount_++; // count # reconnects
if (reason == espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED) {
LOG_WARNING("MQTT disconnected: TCP");
@@ -499,7 +490,6 @@ void Mqtt::on_connect() {
LOG_INFO("MQTT connected");
connecting_ = true;
connectcount_++; // count # reconnects. not currently used.
queuecount_ = mqttClient_->queueSize();
load_settings(); // reload MQTT settings - in case they have changes
@@ -513,7 +503,7 @@ void Mqtt::on_connect() {
// with disabled HA we subscribe and the broker sends all stored HA-emsesp-configs.
// Around line 272 they are removed (search for "// remove HA topics if we don't use discover")
// If HA is enabled the subscriptions are removed.
// As described in the doc (https://emsesp.github.io/docs/#/Troubleshooting?id=home-assistant):
// As described in the doc (https://docs.emsesp.org/Troubleshooting?id=home-assistant):
// disable HA, wait 5 minutes (to allow the broker to send all), than reenable HA again.
queue_subscribe_message(discovery_prefix_ + "/+/" + mqtt_basename_ + "/#");
}
@@ -584,7 +574,6 @@ void Mqtt::ha_status() {
publish_system_ha_sensor_config(DeviceValueType::STRING, "EMS Bus", "bus_status", DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::STRING, "Uptime", "uptime", DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT8, "Uptime (sec)", "uptime_sec", DeviceValueUOM::SECONDS);
publish_system_ha_sensor_config(DeviceValueType::BOOL, "NTP status", "ntp_status", DeviceValueUOM::CONNECTIVITY);
publish_system_ha_sensor_config(DeviceValueType::INT8, "Free memory", "freemem", DeviceValueUOM::KB);
publish_system_ha_sensor_config(DeviceValueType::INT8, "Max Alloc", "max_alloc", DeviceValueUOM::KB);
publish_system_ha_sensor_config(DeviceValueType::INT8, "MQTT fails", "mqttfails", DeviceValueUOM::NONE);
@@ -593,8 +582,10 @@ void Mqtt::ha_status() {
publish_system_ha_sensor_config(DeviceValueType::INT8, "Tx reads", "txreads", DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT8, "Tx writes", "txwrites", DeviceValueUOM::NONE);
publish_system_ha_sensor_config(DeviceValueType::INT8, "Tx fails", "txfails", DeviceValueUOM::NONE);
// This comes from the info MQTT topic
if (!EMSESP::system_.ethernet_connected()) {
publish_system_ha_sensor_config(DeviceValueType::INT16, "WiFi reconnects", "wifireconnects", DeviceValueUOM::NONE);
}
// This comes from the info MQTT topic - and handled in the publish_ha_sensor_config function
publish_system_ha_sensor_config(DeviceValueType::STRING, "Version", "version", DeviceValueUOM::NONE);
}
@@ -934,14 +925,13 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
char config_topic[70];
snprintf(config_topic, sizeof(config_topic), "%s/%s_%s/config", mqtt_basename_.c_str(), device_name, entity_with_tag);
bool readonly_sensors = true;
// create the topic
// depending on the type and whether the device entity is writable (a command)
// depending on the type and whether the device entity is writable (i.e. a command)
// https://developers.home-assistant.io/docs/core/entity
char topic[MQTT_TOPIC_MAX_SIZE];
// if it's a command then we can use Number, Switch, Select or Text. Otherwise stick to Sensor
topic[0] = '\0'; // nullify, making it empty
if (has_cmd) {
// if it's a command then we can use Number, Switch, Select or Text. Otherwise stick to Sensor
switch (type) {
case DeviceValueType::INT8:
case DeviceValueType::UINT8:
@@ -950,48 +940,40 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
case DeviceValueType::UINT24:
case DeviceValueType::UINT32:
// number - https://www.home-assistant.io/integrations/number.mqtt
// older Domoticz does not support number, use sensor
// older Domoticz does not support number, will default to Sensor
if (discovery_type() == discoveryType::HOMEASSISTANT || discovery_type() == discoveryType::DOMOTICZ_LATEST) {
snprintf(topic, sizeof(topic), "number/%s", config_topic);
readonly_sensors = false;
} else {
snprintf(topic, sizeof(topic), "sensor/%s", config_topic);
}
break;
case DeviceValueType::BOOL:
// switch - https://www.home-assistant.io/integrations/switch.mqtt
snprintf(topic, sizeof(topic), "switch/%s", config_topic);
readonly_sensors = false;
break;
case DeviceValueType::ENUM:
snprintf(topic, sizeof(topic), "select/%s", config_topic);
readonly_sensors = false;
break;
case DeviceValueType::CMD: // hardcoded commands are always ENUMS
// select - https://www.home-assistant.io/integrations/select.mqtt
snprintf(topic, sizeof(topic), "select/%s", config_topic);
break;
case DeviceValueType::CMD:
if (uom == DeviceValueUOM::NONE) {
snprintf(topic, sizeof(topic), "select/%s", config_topic);
snprintf(topic, sizeof(topic), "select/%s", config_topic); // hardcoded commands are always ENUMS
} else if (discovery_type() == discoveryType::HOMEASSISTANT || discovery_type() == discoveryType::DOMOTICZ_LATEST) {
snprintf(topic, sizeof(topic), "number/%s", config_topic);
} else {
snprintf(topic, sizeof(topic), "sensor/%s", config_topic);
}
readonly_sensors = false;
break;
case DeviceValueType::STRING:
// text - https://www.home-assistant.io/integrations/text.mqtt
snprintf(topic, sizeof(topic), "text/%s", config_topic); // e.g. set_datetime, set_holiday, set_wwswitchtime
readonly_sensors = false;
// Domoticz does not support text, will default to Sensor
if (discovery_type() == discoveryType::HOMEASSISTANT) {
snprintf(topic, sizeof(topic), "text/%s", config_topic); // e.g. set_datetime, set_holiday, set_wwswitchtime
}
break;
default:
// plain old sensor, and make it read-only
break;
}
}
// For read-only sensors there are either sensor or binary_sensor
// for both we also set the device class and state class
if (readonly_sensors) {
// if at this point we don't have a topic created yet, create a default sensor one. We always need a topic.
if (!strnlen(topic, sizeof(topic))) {
snprintf(topic, sizeof(topic), (type == DeviceValueType::BOOL) ? "binary_sensor/%s" : "sensor/%s", config_topic); // binary sensor (for booleans)
}
@@ -1057,21 +1039,6 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
doc["max"] = dv_set_max;
snprintf(sample_val, sizeof(sample_val), "%i", dv_set_min);
}
// set icons
// since these don't have a device class we need to add the icon ourselves
switch (uom) {
case DeviceValueUOM::DEGREES:
case DeviceValueUOM::DEGREES_R:
case DeviceValueUOM::K:
doc["ic"] = F_(icondegrees);
break;
case DeviceValueUOM::PERCENT:
doc["ic"] = F_(iconpercent);
break;
default:
break;
}
}
// friendly name = <tag> <name>
@@ -1106,7 +1073,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
char val_obj[100];
char val_cond[200];
if (is_nested() && tag >= DeviceValueTAG::TAG_HC1) {
snprintf(val_obj, sizeof(val_obj), "value_json.%s['%s']", EMSdevice::tag_to_mqtt(tag), entity);
snprintf(val_obj, sizeof(val_obj), "value_json['%s']['%s']", EMSdevice::tag_to_mqtt(tag), entity);
snprintf(val_cond, sizeof(val_cond), "value_json.%s is defined and %s is defined", EMSdevice::tag_to_mqtt(tag), val_obj);
} else {
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", entity);
@@ -1121,27 +1088,34 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
strlcpy(sample_val, "false", sizeof(sample_val)); // default is "false"
}
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
// don't bother with value template conditions if using Domoticz which doesn't fully support MQTT Discovery
if (discovery_type() == discoveryType::HOMEASSISTANT) {
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
// add the dev json object to the end, not for commands
add_ha_sections_to_doc(nullptr, stat_t, doc, false, val_cond); // no name, since the "dev" has already been adde
}
// Add the state class, device class and sometimes the icon. Used only for read-only sensors like Sensor and Binary Sensor
if (readonly_sensors) {
// first set the catagory for System entities
// https://github.com/emsesp/EMS-ESP32/discussions/1459#discussioncomment-7694873
if (device_type == EMSdevice::DeviceType::SYSTEM) {
doc["ent_cat"] = "diagnostic";
// adds availability, dev, ids to the config section to HA Discovery config
// except for commands
add_ha_sections_to_doc(nullptr, stat_t, doc, false, val_cond); // no name, since the "dev" has already been added
} else {
// Domoticz doesn't support value templates, so we just use the value directly
// Also omit the uom and other state classes
doc["val_tpl"] = (std::string) "{{" + val_obj + "}}";
}
add_ha_uom(doc.as<JsonObject>(), type, uom, entity); // add the UoM, device and state class
}
// Add the state class, device class and an optional icon based on the uom
// first set the catagory for System entities
// https://github.com/emsesp/EMS-ESP32/discussions/1459#discussioncomment-7694873
if (device_type == EMSdevice::DeviceType::SYSTEM) {
doc["ent_cat"] = "diagnostic"; // instead of config
}
add_ha_uom(doc.as<JsonObject>(), type, uom, entity);
doc["dev"] = dev_json;
return queue_ha(topic, doc.as<JsonObject>());
}
// Add the state class, device class and an optional icon based on the uom
void Mqtt::add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, const char * entity) {
const char * dc_ha = "dev_cla"; // device class
const char * sc_ha = "stat_cla"; // state class
@@ -1158,20 +1132,26 @@ void Mqtt::add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, con
doc[uom_ha] = "s";
} else if (uom != DeviceValueUOM::NONE) {
doc[uom_ha] = EMSdevice::uom_to_string(uom); // default
} else if (discovery_type() != discoveryType::HOMEASSISTANT) {
// Domoticz use " " for a no-uom
doc[uom_ha] = " ";
}
}
// set state and device class
// also icon, when there is no device class that sets one
switch (uom) {
case DeviceValueUOM::DEGREES:
case DeviceValueUOM::DEGREES_R:
case DeviceValueUOM::K:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = "temperature";
doc["ic"] = F_(icondegrees); // icon
break;
case DeviceValueUOM::PERCENT:
doc[sc_ha] = F_(measurement);
doc[dc_ha] = "power_factor";
doc["ic"] = F_(iconpercent); // icon
break;
case DeviceValueUOM::SECONDS:
case DeviceValueUOM::MINUTES:

View File

@@ -166,24 +166,22 @@ void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
// empty message back if temperature not set or unknown message type
if (data[2] == EMSdevice::EMS_TYPE_VERSION) {
version(addr, data[0], hc);
} else if (length == 6 && remotetemp_[hc] == EMS_VALUE_INT16_NOTSET) {
unknown(addr, data[0], data[2], data[3]);
} else if (length == 8 && remotetemp_[hc] == EMS_VALUE_INT16_NOTSET) {
unknown(addr, data[0], data[3], data[5], data[6]);
} else if (data[2] == 0xAF && data[3] == 0) {
temperature(addr, data[0], hc);
} else if (length == 6) { // all other ems queries
unknown(addr, data[0], data[2], data[3]);
} else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 0 && data[6] == 0x23) { // Junkers
temperature(addr, data[0], hc);
} else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x2B + hc) { // EMS+ temperature
temperature(addr, data[0], hc);
} else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x7B + hc && remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) { // EMS+ humidity
humidity(addr, data[0], hc);
} else if (length == 6) { // ems query
unknown(addr, data[0], data[2], data[3]);
} else if (length == 8 && data[2] == 0xFF) { // ems+ query
unknown(addr, data[0], data[3], data[5], data[6]);
} else if (data[2] == 0xF7) { // ems+ query with 3 bytes type src dst 7F offset len=FF FF HIGH LOW
replyF7(addr, data[0], data[3], data[5], data[6], data[7], hc);
} else if (length == 8) {
unknown(addr, data[0], data[3], data[5], data[6]);
}
}

View File

@@ -51,6 +51,48 @@ void Shower::start() {
FL_(coldshot_cmd),
CommandFlag::ADMIN_ONLY);
Command::add(
EMSdevice::DeviceType::SYSTEM,
F_(showertimer),
[&](const char * value, const int8_t id, JsonObject output) {
bool b;
if (!Helpers::value2bool(value, b)) {
return false;
}
shower_timer_ = b;
EMSESP::webSettingsService.update([&](WebSettings & settings) {
if (settings.shower_timer != b) {
settings.shower_timer = b;
return StateUpdateResult::CHANGED;
}
return StateUpdateResult::UNCHANGED;
});
return true;
},
FL_(showertimer_cmd),
CommandFlag::ADMIN_ONLY);
Command::add(
EMSdevice::DeviceType::SYSTEM,
F_(showeralert),
[&](const char * value, const int8_t id, JsonObject output) {
bool b;
if (!Helpers::value2bool(value, b)) {
return false;
}
shower_alert_ = b;
EMSESP::webSettingsService.update([&](WebSettings & settings) {
if (settings.shower_alert != b) {
settings.shower_alert = b;
return StateUpdateResult::CHANGED;
}
return StateUpdateResult::UNCHANGED;
});
return true;
},
FL_(showeralert_cmd),
CommandFlag::ADMIN_ONLY);
if (shower_timer_) {
set_shower_state(false, true); // turns shower to off and creates HA topic if not already done
}

View File

@@ -52,12 +52,17 @@
namespace emsesp {
// Languages supported. Note: the order is important and must match locale_translations.h
#if defined(EMSESP_TEST) || defined(EMSESP_EN_ONLY)
// in Debug mode use one language (en) to save flash memory needed for the tests
// Languages supported. Note: the order is important
// and must match locale_translations.h and common.h
#if defined(EMSESP_TEST)
// in Test mode use two languages (en & de) to save flash memory needed for the tests
const char * const languages[] = {EMSESP_LOCALE_EN, EMSESP_LOCALE_DE};
#elif defined(EMSESP_EN_ONLY)
// EN only
const char * const languages[] = {EMSESP_LOCALE_EN};
#elif defined(EMSESP_DE_ONLY)
const char * const languages[] = {EMSESP_LOCALE_DE};
// EN + DE
const char * const languages[] = {EMSESP_LOCALE_EN, EMSESP_LOCALE_DE};
#else
const char * const languages[] = {EMSESP_LOCALE_EN,
EMSESP_LOCALE_DE,
@@ -92,7 +97,7 @@ uint8_t System::language_index() {
return i;
}
}
return 0; // EN
return 0; // EN only
}
// send raw to ems
@@ -678,15 +683,7 @@ void System::heartbeat_json(JsonObject output) {
output["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
output["uptime_sec"] = uuid::get_uptime_sec();
bool value_b = ntp_connected();
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
output["ntp_status"] = value_b;
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
output["ntp_status"] = value_b ? 1 : 0;
} else {
char s[12];
output["ntp_status"] = Helpers::render_boolean(s, value_b);
}
output["rxreceived"] = EMSESP::rxservice_.telegram_count();
output["rxfails"] = EMSESP::rxservice_.telegram_error_count();
output["txreads"] = EMSESP::txservice_.telegram_read_count();
@@ -694,9 +691,9 @@ void System::heartbeat_json(JsonObject output) {
output["txfails"] = EMSESP::txservice_.telegram_read_fail_count() + EMSESP::txservice_.telegram_write_fail_count();
if (Mqtt::enabled()) {
output["mqttcount"] = Mqtt::publish_count();
output["mqttfails"] = Mqtt::publish_fails();
output["mqttconnects"] = Mqtt::connect_count();
output["mqttcount"] = Mqtt::publish_count();
output["mqttfails"] = Mqtt::publish_fails();
output["mqttreconnects"] = Mqtt::connect_count();
}
output["apicalls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
output["apifails"] = WebAPIService::api_fails();
@@ -716,9 +713,10 @@ void System::heartbeat_json(JsonObject output) {
#ifndef EMSESP_STANDALONE
if (!ethernet_connected_) {
int8_t rssi = WiFi.RSSI();
output["rssi"] = rssi;
output["wifistrength"] = wifi_quality(rssi);
int8_t rssi = WiFi.RSSI();
output["rssi"] = rssi;
output["wifistrength"] = wifi_quality(rssi);
output["wifireconnects"] = EMSESP::esp8266React.getWifiReconnects();
}
#endif
}
@@ -1135,6 +1133,7 @@ void System::show_system(uuid::console::Shell & shell) {
}
// see if there is a restore of an older settings file that needs to be applied
// note there can be only one file at a time
bool System::check_restore() {
bool reboot_required = false; // true if we need to reboot
@@ -1168,7 +1167,7 @@ bool System::check_restore() {
// it's a custom support file - save it to /config
new_file.close();
if (LittleFS.rename(TEMP_FILENAME_PATH, EMSESP_CUSTOMSUPPORT_FILE)) {
LOG_DEBUG("Custom support information found");
LOG_INFO("Custom support file stored");
return false; // no need to reboot
} else {
LOG_ERROR("Failed to save custom support file");
@@ -1498,9 +1497,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
// node["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6());
// }
} else if (WiFi.status() == WL_CONNECTED) {
node["network"] = "WiFi";
node["hostname"] = WiFi.getHostname();
node["RSSI"] = WiFi.RSSI();
node["network"] = "WiFi";
node["hostname"] = WiFi.getHostname();
node["RSSI"] = WiFi.RSSI();
node["WIFIReconnects"] = EMSESP::esp8266React.getWifiReconnects();
// node["MAC"] = WiFi.macAddress();
// node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
// node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
@@ -1529,6 +1529,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["CORSOrigin"] = settings.CORSOrigin;
}
});
#ifndef EMSESP_STANDALONE
EMSESP::esp8266React.getAPSettingsService()->read([&](const APSettings & settings) {
const char * pM[] = {"always", "disconnected", "never"};
@@ -1556,7 +1557,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["MQTTPublishes"] = Mqtt::publish_count();
node["MQTTQueued"] = Mqtt::publish_queued();
node["MQTTPublishFails"] = Mqtt::publish_fails();
node["MQTTConnects"] = Mqtt::connect_count();
node["MQTTReconnects"] = Mqtt::connect_count();
}
EMSESP::esp8266React.getMqttSettingsService()->read([&](const MqttSettings & settings) {
node["enabled"] = settings.enabled;

View File

@@ -148,7 +148,7 @@ void RxService::add(uint8_t * data, uint8_t length) {
// validate the CRC. if it fails then increment the number of corrupt/incomplete telegrams and only report to console/syslog
uint8_t crc = calculate_crc(data, length - 1);
if (data[length - 1] != crc) {
if ((data[0] & 0x7F) != ems_bus_id()) { // do not count echos as errors
if (data[0] != EMSuart::last_tx_src()) { // do not count echos as errors
telegram_error_count_++;
LOG_WARNING("Incomplete Rx: %s", Helpers::data_to_hex(data, length).c_str()); // include CRC
} else {

View File

@@ -482,7 +482,7 @@ void TemperatureSensor::publish_values(const bool force) {
char val_obj[70];
char val_cond[170];
if (Mqtt::is_nested()) {
snprintf(val_obj, sizeof(val_obj), "value_json['%s'].temp", sensor.id().c_str());
snprintf(val_obj, sizeof(val_obj), "value_json['%s']['temp']", sensor.id().c_str());
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined and %s is defined", sensor.id().c_str(), val_obj);
} else {
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());

View File

@@ -50,7 +50,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "general") {
EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat");
EMSESP::logger().notice("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,6 +77,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
return true;
}
//
// the tests take a lot of memory when built for the ESP32
// so only including the full set in standalone, otherwise a limited selection of basic tests
@@ -84,7 +85,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
#ifdef EMSESP_STANDALONE
if (cmd == "heat_exchange") {
EMSESP::logger().info("Testing heating exchange...");
EMSESP::logger().notice("Testing heating exchange...");
add_device(0x08, 219); // Greenstar HIU/Logamax kompakt WS170
@@ -96,7 +97,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "2thermostats") {
EMSESP::logger().info("Testing with multiple thermostats...");
EMSESP::logger().notice("Testing with multiple thermostats...");
add_device(0x08, 123); // GB072
add_device(0x10, 158); // RC310
@@ -128,7 +129,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "310") {
EMSESP::logger().info("Adding a GB072/RC310 combo...");
EMSESP::logger().notice("Adding a GB072/RC310 combo...");
add_device(0x08, 123); // GB072
add_device(0x10, 158); // RC310
@@ -155,7 +156,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "gateway") {
EMSESP::logger().info("Adding a Gateway...");
EMSESP::logger().notice("Adding a Gateway...");
// add 0x48 KM200, via a version command
rx_telegram({0x48, 0x0B, 0x02, 0x00, 0xBD, 0x04, 0x06, 00, 00, 00, 00, 00, 00, 00});
@@ -175,7 +176,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "mixer") {
EMSESP::logger().info("Adding a mixer...");
EMSESP::logger().notice("Adding a mixer...");
// add controller
add_device(0x09, 114);
@@ -197,7 +198,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "boiler") {
EMSESP::logger().info("Adding boiler...");
EMSESP::logger().notice("Adding boiler...");
add_device(0x08, 123); // Nefit Trendline
// UBAuptime
@@ -214,7 +215,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "thermostat") {
EMSESP::logger().info("Adding thermostat...");
EMSESP::logger().notice("Adding thermostat...");
add_device(0x10, 192); // FW120
@@ -227,7 +228,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "solar") {
EMSESP::logger().info("Adding solar...");
EMSESP::logger().notice("Adding solar...");
add_device(0x30, 163); // SM100
@@ -246,7 +247,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
}
if (cmd == "heatpump") {
EMSESP::logger().info("Adding heatpump...");
EMSESP::logger().notice("Adding heatpump...");
add_device(0x38, 200); // Enviline module
add_device(0x10, 192); // FW120 thermostat
@@ -263,7 +264,7 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
return false;
}
// These next tests are run from the Consol via the test command, so inherit the Shell
// These next tests are run from the Console via the test command, so inherit the Shell
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & id1_s, const std::string & id2_s) {
bool ok = false; // default tests fail
@@ -307,7 +308,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
}
shell.printfln("Testing Adding a device (product_id %d), with all values...", id2);
test("add", id1, id2);
shell.invoke_command("show values");
shell.invoke_command("show devices");
ok = true;
}
// set the language
if (command == "locale") {
shell.printfln("Testing setting locale to %s", id1_s.c_str());
EMSESP::system_.locale(id1_s.c_str());
shell.invoke_command("show");
ok = true;
}
@@ -315,15 +324,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Testing adding a boiler, thermostat, all sensors, scheduler and custom entities...");
// setup fake data
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
// EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
// add devices
test("general");
EMSESP::webCustomEntityService.test(); // add custom entities
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.test(); // add temperature sensors
EMSESP::webSchedulerService.test(); // add scheduler items
// EMSESP::webCustomEntityService.test(); // add custom entities
// EMSESP::temperaturesensor_.test(); // add temperature sensors
// EMSESP::webSchedulerService.test(); // add scheduler items
// shell.invoke_command("show devices");
// shell.invoke_command("show values");
@@ -434,7 +442,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (command == "entity_dump") {
System::test_set_all_active(true);
EMSESP::dump_all_values(shell);
EMSESP::dump_all_entities(shell);
ok = true;
}
@@ -543,7 +551,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
}
if (command == "620") {
EMSESP::logger().info("Testing 620...");
EMSESP::logger().notice("Testing 620...");
// Version Controller
uart_telegram({0x09, 0x0B, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
@@ -1006,25 +1014,35 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// request.url("/api");
// EMSESP::webAPIService.webAPIService(&request, doc.as<JsonVariant>());
char data2[] = "{\"action\":\"customSupport\", \"param\":\"hello\"}";
char data2[] = "{\"action\":\"getCustomSupport\", \"param\":\"hello\"}";
deserializeJson(doc, data2);
request.url("/rest/action");
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
char data3[] = "{\"action\":\"export\", \"param\":\"schedule\"}";
deserializeJson(doc, data3);
request.url("/rest/action");
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// char data3[] = "{\"action\":\"export\", \"param\":\"schedule\"}";
// deserializeJson(doc, data3);
// request.url("/rest/action");
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
char data4[] = "{\"action\":\"export\", \"param\":\"allvalues\"}";
deserializeJson(doc, data4);
request.url("/rest/action");
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// char data4[] = "{\"action\":\"export\", \"param\":\"allvalues\"}";
// deserializeJson(doc, data4);
// request.url("/rest/action");
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
char data5[] = "{\"action\":\"checkUpgrade\", \"param\":\"3.7.0-dev.99\"}";
deserializeJson(doc, data5);
request.url("/rest/action");
EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// test version checks
// test with "current_version_s = "3.7.1-dev.8" in WebStatusService::checkUpgrade()
// request.url("/rest/action");
// deserializeJson(doc, "{\"action\":\"checkUpgrade\", \"param\":\"3.7.1-dev.9,3.7.0\"}"); // is upgradable
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// deserializeJson(doc, "{\"action\":\"checkUpgrade\", \"param\":\"3.7.1-dev.7,3.7.0\"}"); // is not upgradable
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// test with "current_version_s = "3.6.5" in WebStatusService::checkUpgrade()
// request.url("/rest/action");
// deserializeJson(doc, "{\"action\":\"checkUpgrade\", \"param\":\"3.7.1-dev.9,3.6.5\"}"); // is noy upgradable
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// deserializeJson(doc, "{\"action\":\"checkUpgrade\", \"param\":\"3.7.1-dev.7,3.7.0\"}"); // is upgradable
// EMSESP::webStatusService.action(&request, doc.as<JsonVariant>());
// char data6[] = "{\"device\":\"system\", \"cmd\":\"read\",\"value\":\"8 2 27 1\"}";
// deserializeJson(doc, data6);
@@ -2220,8 +2238,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
#endif
if (!ok) {
shell.printfln("Unknown test command: %s", command.c_str());
EMSESP::logger().notice("Unknown test command: %s", command.c_str());
shell.printfln("Unknown test %s", command.c_str());
EMSESP::logger().notice("Unknown test %s", command.c_str());
}
}

View File

@@ -31,6 +31,7 @@
#include "emsesp.h"
namespace emsesp {
uint8_t EMSuart::last_tx_src_ = 0;
static QueueHandle_t uart_queue;
uint8_t tx_mode_ = 0xFF;
@@ -144,6 +145,8 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
return EMS_TX_STATUS_OK;
}
last_tx_src_ = len < 4 ? 0 : buf[0];
if (tx_mode_ == EMS_TXMODE_HW) { // hardware controlled mode
uart_write_bytes_with_break(EMSUART_NUM, buf, len, 10);
return EMS_TX_STATUS_OK;

View File

@@ -65,10 +65,14 @@ class EMSuart {
static void send_poll(const uint8_t data);
static void stop();
static uint16_t transmit(const uint8_t * buf, const uint8_t len);
static uint8_t last_tx_src() {
return last_tx_src_;
}
private:
static void IRAM_ATTR uart_gen_break(uint32_t length_us);
static void uart_event_task(void * pvParameters);
static uint8_t last_tx_src_;
};
} // namespace emsesp

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.0"
#define EMSESP_APP_VERSION "3.7.1"

View File

@@ -32,13 +32,12 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security
// POST|GET api/{device}
// POST|GET api/{device}/{entity}
void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant json) {
JsonObject input;
JsonDocument input_doc; // has no body JSON so create dummy as empty input object
JsonObject input;
// if no body then treat it as a secure GET
if ((request->method() == HTTP_GET) || (!json.is<JsonObject>())) {
// HTTP GET
JsonDocument input_doc; // has no body JSON so create dummy as empty input object
input = input_doc.to<JsonObject>();
} else {
// HTTP_POST
input = json.as<JsonObject>(); // extract values from the json. these will be used as default values

View File

@@ -415,7 +415,16 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) {
JsonObject dv = node["dv"].to<JsonObject>();
dv["id"] = "00" + sensor.name();
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN) {
#if CONFIG_IDF_TARGET_ESP32
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT && (sensor.gpio() == 25 || sensor.gpio() == 26)) {
obj["v"] = Helpers::transformNumFloat(sensor.value(), 0);
} else
#elif CONFIG_IDF_TARGET_ESP32S2
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT && (sensor.gpio() == 17 || sensor.gpio() == 18)) {
obj["v"] = Helpers::transformNumFloat(sensor.value(), 0);
} else
#endif
if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN) {
char s[12];
dv["v"] = Helpers::render_boolean(s, sensor.value() != 0, true);
JsonArray l = dv["l"].to<JsonArray>();

View File

@@ -322,8 +322,7 @@ bool WebSchedulerService::command(const char * name, const std::string & command
JsonDocument doc;
if (deserializeJson(doc, cmd) == DeserializationError::Ok) {
HTTPClient http;
int httpResult = 0;
std::string url = doc["url"] | "";
std::string url = doc["url"] | "";
// for a GET with parameters replace commands with values
// don't search the complete url, it may contain a devicename in path
auto q = url.find_first_of('?');
@@ -342,6 +341,7 @@ bool WebSchedulerService::command(const char * name, const std::string & command
std::string method = doc["method"] | "GET"; // default GET
// if there is data, force a POST
int httpResult = 0;
if (value.length() || method == "post") { // we have all lowercase
if (value.find_first_of('{') != std::string::npos) {
http.addHeader("Content-Type", "application/json"); // auto-set to JSON

View File

@@ -179,8 +179,8 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
if (has_param) {
ok = exportData(root, param);
}
} else if (action == "customSupport") {
ok = customSupport(root);
} else if (action == "getCustomSupport") {
ok = getCustomSupport(root);
} else if (action == "uploadURL" && is_admin) {
ok = uploadURL(param.c_str());
}
@@ -203,21 +203,44 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
}
// action = checkUpgrade
bool WebStatusService::checkUpgrade(JsonObject root, std::string & latest_version) {
root["emsesp_version"] = EMSESP_APP_VERSION;
if (!latest_version.empty()) {
#if defined(EMSESP_DEBUG)
emsesp::EMSESP::logger().debug("Checking for upgrade: %s > %s", EMSESP_APP_VERSION, latest_version.c_str());
// versions holds the latest development version and stable version in one string, comma separated
bool WebStatusService::checkUpgrade(JsonObject root, std::string & versions) {
std::string current_version_s;
#ifndef EMSESP_STANDALONE
current_version_s = EMSESP_APP_VERSION;
#else
// for testing only - see api3 test in test.cpp
// current_version_s = "3.6.5";
current_version_s = "3.7.1-dev.8";
#endif
version::Semver200_version settings_version(EMSESP_APP_VERSION);
version::Semver200_version this_version(latest_version);
if (!versions.empty()) {
version::Semver200_version current_version(current_version_s);
bool using_dev_version = !current_version.prerelease().find("dev"); // look for dev in the name to determine if we're using dev version
version::Semver200_version latest_version(using_dev_version ? versions.substr(0, versions.find(',')) : versions.substr(versions.find(',') + 1));
bool upgradeable = (latest_version > current_version);
root["upgradeable"] = (this_version > settings_version);
#if defined(EMSESP_DEBUG)
emsesp::EMSESP::logger()
.debug("Checking Version upgrade. Using %s release branch. current version=%d.%d.%d-%s, latest version=%d.%d.%d-%s (%s upgradeable)",
(using_dev_version ? "dev" : "stable"),
current_version.major(),
current_version.minor(),
current_version.patch(),
current_version.prerelease().c_str(),
latest_version.major(),
latest_version.minor(),
latest_version.patch(),
latest_version.prerelease().c_str(),
upgradeable ? "IS" : "NOT");
#endif
root["upgradeable"] = upgradeable;
}
return true; // always ok
root["emsesp_version"] = current_version_s; // always send back current version
return true;
}
// action = allvalues
@@ -278,19 +301,28 @@ bool WebStatusService::exportData(JsonObject root, std::string & type) {
return true;
}
// action = customSupport
// action = getCustomSupport
// reads any upload customSupport.json file and sends to to Help page to be shown as Guest
bool WebStatusService::customSupport(JsonObject root) {
#ifndef EMSESP_STANDALONE
bool WebStatusService::getCustomSupport(JsonObject root) {
JsonDocument doc;
#if defined(EMSESP_STANDALONE)
// dummy test data for "test api3"
deserializeJson(
doc, "{\"type\":\"customSupport\",\"Support\":{\"html\":[\"html code\",\"here\"], \"img_url\": \"https://docs.emsesp.org/_media/images/designer.png\"}");
#else
// check if we have custom support file uploaded
File file = LittleFS.open(EMSESP_CUSTOMSUPPORT_FILE, "r");
if (!file) {
// there is no custom file, return empty object
#if defined(EMSESP_DEBUG)
emsesp::EMSESP::logger().debug("No custom support file found");
#endif
return true;
}
// read the contents of the file into the root output json object
DeserializationError error = deserializeJson(root, file);
// read the contents of the file into a json doc. We can't do this direct to object since 7.2.1
DeserializationError error = deserializeJson(doc, file);
if (error) {
emsesp::EMSESP::logger().err("Failed to read custom support file");
return false;
@@ -298,6 +330,13 @@ bool WebStatusService::customSupport(JsonObject root) {
file.close();
#endif
#if defined(EMSESP_DEBUG)
emsesp::EMSESP::logger().debug("Showing custom support page");
#endif
root.set(doc.as<JsonObject>()); // add to web response root object
return true;
}

View File

@@ -25,7 +25,7 @@ class WebStatusService {
// actions
bool checkUpgrade(JsonObject root, std::string & latest_version);
bool exportData(JsonObject root, std::string & type);
bool customSupport(JsonObject root);
bool getCustomSupport(JsonObject root);
bool uploadURL(const char * url);
void allvalues(JsonObject output);