Merge pull request #1733 from MichaelDvP/dev

fix show commands, add hp input states
This commit is contained in:
Proddy
2024-05-03 09:44:27 +02:00
committed by GitHub
21 changed files with 305 additions and 262 deletions

View File

@@ -16,6 +16,7 @@
- reset history [#1695](https://github.com/emsesp/EMS-ESP32/issues/1695)
- heatpump entities `fan` and `shutdown` [#1690](https://github.com/emsesp/EMS-ESP32/discussions/1690)
- mqtt HA-mode 3 for v3.6 compatible HA entities, set on update v3.6->v3.7
- HP input states [#1723](https://github.com/emsesp/EMS-ESP32/discussions/1723)
## Fixed

View File

@@ -62,7 +62,7 @@
"rollup-plugin-visualizer": "^5.12.0",
"terser": "^5.31.0",
"typescript-eslint": "^7.8.0",
"vite": "^5.2.10",
"vite": "^5.2.11",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^4.3.2"
},

View File

@@ -1741,7 +1741,7 @@ __metadata:
typesafe-i18n: "npm:^5.26.2"
typescript: "npm:^5.4.5"
typescript-eslint: "npm:^7.8.0"
vite: "npm:^5.2.10"
vite: "npm:^5.2.11"
vite-plugin-imagemin: "npm:^0.6.1"
vite-tsconfig-paths: "npm:^4.3.2"
languageName: unknown
@@ -7062,9 +7062,9 @@ __metadata:
languageName: node
linkType: hard
"vite@npm:^5.2.10":
version: 5.2.10
resolution: "vite@npm:5.2.10"
"vite@npm:^5.2.11":
version: 5.2.11
resolution: "vite@npm:5.2.11"
dependencies:
esbuild: "npm:^0.20.1"
fsevents: "npm:~2.3.3"
@@ -7098,7 +7098,7 @@ __metadata:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/d50630ac8de807a6185cd9b5763b3969b2950a454cf6a4482f3780f183865e8d6f7e3aa57dd70ede1c493aaa861efb25b43562287efbcf8b471b7f3b88857a33
checksum: 10c0/664b8d68e4f5152ae16bd2041af1bbaf11c43630ac461835bc31ff7d019913b33e465386e09f66dc1037d7aeefbb06939e0173787c063319bc2bd30c3b9ad8e4
languageName: node
linkType: hard

View File

@@ -187,7 +187,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
return_code = Command::call(device_type, command_p, data.as<const char *>(), is_admin, id_n, output);
} else if (data.is<int>()) {
char data_str[10];
return_code = Command::call(device_type, command_p, Helpers::itoa((int16_t)data.as<int>(), data_str), is_admin, id_n, output);
return_code = Command::call(device_type, command_p, Helpers::itoa(data.as<int32_t>(), data_str), is_admin, id_n, output);
} else if (data.is<float>()) {
char data_str[10];
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, data.as<float>(), 2), is_admin, id_n, output);
@@ -240,10 +240,10 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
id = command[2] - '0';
command += 3;
} else if (!strncmp(lowerCmd, "dhw", 3) && command[3] == '1' && command[4] == '0') {
id = DeviceValueTAG::TAG_DHW10 - DeviceValueTAG::TAG_HC1 + 1; //18;
id = DeviceValueTAG::TAG_DHW10; //18;
command += 5;
} else if (!strncmp(lowerCmd, "dhw", 3) && command[3] >= '1' && command[3] <= '9') {
id = command[3] - '1' + DeviceValueTAG::TAG_DHW1 - DeviceValueTAG::TAG_HC1 + 1; //9;
id = command[3] - '1' + DeviceValueTAG::TAG_DHW1; //9;
command += 4;
} else if (!strncmp(lowerCmd, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') {
id = command[3] - '0' + 10;
@@ -252,16 +252,16 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
id = command[2] - '0';
command += 3;
} else if (!strncmp(lowerCmd, "ahs", 3) && command[3] >= '1' && command[3] <= '1') { // only ahs1 for now
id = command[3] - '1' + DeviceValueTAG::TAG_AHS1 - DeviceValueTAG::TAG_HC1 + 1; // 19;
id = command[3] - '1' + DeviceValueTAG::TAG_AHS1; // 19;
command += 4;
} else if (!strncmp(lowerCmd, "hs", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '6') {
id = command[3] - '0' + DeviceValueTAG::TAG_HS10 - DeviceValueTAG::TAG_HC1 + 1; //29;
id = command[3] - '0' + DeviceValueTAG::TAG_HS10; //29;
command += 4;
} else if (!strncmp(lowerCmd, "hs", 2) && command[2] >= '1' && command[2] <= '9') {
id = command[2] - '1' + DeviceValueTAG::TAG_HS1 - DeviceValueTAG::TAG_HC1 + 1; //20;
id = command[2] - '1' + DeviceValueTAG::TAG_HS1; //20;
command += 3;
} else if (!strncmp(lowerCmd, "dhw", 3)) { // no number
id = 9;
id = DeviceValueTAG::TAG_DHW1;
command += 3;
}
@@ -302,16 +302,16 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
auto dname = EMSdevice::device_type_2_device_name(device_type);
uint8_t device_id = EMSESP::device_id_from_cmd(device_type, cmd, id);
uint8_t flag = 0;
uint8_t tag = id - 1 + DeviceValueTAG::TAG_HC1;
uint8_t flag = CommandFlag::CMD_FLAG_DEFAULT;
int8_t tag = id;
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
flag = CommandFlag::MQTT_SUB_FLAG_HC;
flag = CommandFlag::CMD_FLAG_HC;
} else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) {
flag = CommandFlag::MQTT_SUB_FLAG_DHW;
flag = CommandFlag::CMD_FLAG_DHW;
} else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) {
flag = CommandFlag::MQTT_SUB_FLAG_HS;
flag = CommandFlag::CMD_FLAG_HS;
} else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) {
flag = CommandFlag::MQTT_SUB_FLAG_AHS;
flag = CommandFlag::CMD_FLAG_AHS;
}
// see if there is a command registered
auto cf = find_command(device_type, device_id, cmd, flag);
@@ -381,9 +381,9 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
// report back. If not OK show output from error, other return the HTTP code
if (return_code != CommandRet::OK) {
if ((value == nullptr) || (strlen(value) == 0)) {
LOG_ERROR("Command '%s' failed with error code %d", cmd, FL_(cmdRet)[return_code]);
LOG_ERROR("Command '%s' failed with error '%s'", cmd, FL_(cmdRet)[return_code]);
} else {
LOG_ERROR("Command '%s/%s' failed with error code %c", cmd, value, return_code);
LOG_ERROR("Command '%s/%s' failed with error '%s'", cmd, value, FL_(cmdRet)[return_code]);
}
return message(return_code, "callback function failed", output);
}
@@ -430,7 +430,7 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ui
for (auto & cf : cmdfunctions_) {
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type) && (!device_id || cf.device_id_ == device_id)
&& (!(flag & 0x3F) || (flag & 0x3F) == (cf.flags_ & 0x3F))) {
&& (flag & 0x3F) == (cf.flags_ & 0x3F)) {
return &cf;
}
}
@@ -438,13 +438,13 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ui
return nullptr; // command not found
}
void Command::erase_command(const uint8_t device_type, const char * cmd) {
void Command::erase_command(const uint8_t device_type, const char * cmd, uint8_t flag) {
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
return;
}
auto it = cmdfunctions_.begin();
for (auto & cf : cmdfunctions_) {
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type)) {
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type) && ((flag & 0x3F) == (cf.flags_ & 0x3F))) {
cmdfunctions_.erase(it);
return;
}
@@ -452,6 +452,22 @@ void Command::erase_command(const uint8_t device_type, const char * cmd) {
}
}
// get the tagged command
std::string Command::tagged_cmd(std::string cmd, const uint8_t flag) {
switch (flag & 0x3F) {
case CommandFlag::CMD_FLAG_HC:
return "[hc<n>.]" + cmd;
case CommandFlag::CMD_FLAG_DHW:
return "dhw[n]." + cmd;
case CommandFlag::CMD_FLAG_HS:
return "hs<n>." + cmd;
case CommandFlag::CMD_FLAG_AHS:
return "ahs<n>." + cmd;
default:
return cmd;
}
}
// list all commands for a specific device, output as json
bool Command::list(const uint8_t device_type, JsonObject output) {
// force add info and commands for those non-EMS devices
@@ -467,40 +483,28 @@ bool Command::list(const uint8_t device_type, JsonObject output) {
std::list<std::string> sorted_cmds;
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
sorted_cmds.push_back((cf.cmd_));
sorted_cmds.push_back(tagged_cmd(cf.cmd_, cf.flags_));
}
}
sorted_cmds.sort();
for (const auto & cl : sorted_cmds) {
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == std::string(cf.cmd_))) {
if (cf.has_flags(CommandFlag::MQTT_SUB_FLAG_DHW)) {
char s[100];
snprintf(s, sizeof(s), "%s %s", EMSdevice::tag_to_string(DeviceValueTAG::TAG_DHW1), Helpers::translated_word(cf.description_));
output[cl] = s;
} else {
output[cl] = Helpers::translated_word(cf.description_);
}
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == tagged_cmd(cf.cmd_, cf.flags_))) {
output[cl] = Helpers::translated_word(cf.description_);
}
}
}
return true;
}
// output list of all commands to console for a specific DeviceType
void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbose) {
if (cmdfunctions_.empty()) {
shell.println("No commands available");
return;
}
// create list of commands we have registered
std::list<std::string> sorted_cmds;
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
sorted_cmds.push_back((cf.cmd_));
sorted_cmds.push_back(tagged_cmd(cf.cmd_, cf.flags_));
}
}
@@ -539,22 +543,9 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
for (const auto & cl : sorted_cmds) {
// find and print the description
for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == std::string(cf.cmd_))) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == tagged_cmd(cf.cmd_, cf.flags_))) {
uint8_t i = cl.length();
shell.print(" ");
if (cf.has_flags(MQTT_SUB_FLAG_HC)) {
shell.print("[hc<n>.]");
i += 8;
} else if (cf.has_flags(MQTT_SUB_FLAG_DHW)) {
shell.print("[dhw<n>.]");
i += 9;
} else if (cf.has_flags(MQTT_SUB_FLAG_AHS)) {
shell.print("[ahs<n>.]");
i += 9;
} else if (cf.has_flags(MQTT_SUB_FLAG_HS)) {
shell.print("[hs<n>.]");
i += 8;
}
shell.print(cl);
// pad with spaces
while (i++ < 30) {

View File

@@ -31,13 +31,13 @@ namespace emsesp {
// mqtt flags for command subscriptions
enum CommandFlag : uint8_t {
MQTT_SUB_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
MQTT_SUB_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC8
MQTT_SUB_FLAG_DHW = (1 << 1), // 2 TAG_DHW1 - TAG_DHW4
MQTT_SUB_FLAG_AHS = (1 << 2), // 4 TAG_DEVICE_DATA_AHS
MQTT_SUB_FLAG_HS = (1 << 3), // 8 TAG_DEVICE_DATA_HS
HIDDEN = (1 << 6), // 64 do not show in API or Web
ADMIN_ONLY = (1 << 7) // 128 requires authentication
CMD_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
CMD_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC8
CMD_FLAG_DHW = (1 << 1), // 2 TAG_DHW1 - TAG_DHW4
CMD_FLAG_AHS = (1 << 2), // 4 TAG_AHS1
CMD_FLAG_HS = (1 << 3), // 8 TAG_HS1 - TAG_HS16
HIDDEN = (1 << 6), // 64 do not show in API or Web
ADMIN_ONLY = (1 << 7) // 128 requires authentication
};
@@ -110,26 +110,23 @@ class Command {
const char * cmd,
const cmd_function_p cb,
const char * const * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
// same for system/temperature/analog devices
static void add(const uint8_t device_type,
const char * cmd,
const cmd_function_p cb,
const char * const * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
static void
add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
// callback function taking value, id and a json object for its output
static void add(const uint8_t device_type,
const char * cmd,
const cmd_json_function_p cb,
const char * const * description,
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
static void show_all(uuid::console::Shell & shell);
static Command::CmdFunction * find_command(const uint8_t device_type, const uint8_t device_id, const char * cmd, const uint8_t flag);
static std::string tagged_cmd(std::string cmd, const uint8_t flag);
static void erase_command(const uint8_t device_type, const char * cmd);
static void erase_command(const uint8_t device_type, const char * cmd, uint8_t flag = CommandFlag::CMD_FLAG_DEFAULT);
static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose);
static void show_devices(uuid::console::Shell & shell);
static bool device_has_commands(const uint8_t device_type);

View File

@@ -579,7 +579,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
DeviceValueUOM::DEGREES,
MAKE_CF_CB(set_pool_temp));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hp4wayValve_, DeviceValueType::ENUM, FL_(enum_4way), FL_(hp4wayValve), DeviceValueUOM::NONE);
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[0].state, DeviceValueType::BOOL, FL_(hpInput1), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[0].state, DeviceValueType::BOOL, FL_(hpInput1), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[0].option,
DeviceValueType::STRING,
@@ -587,7 +587,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(hpIn1Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn1Logic));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[1].state, DeviceValueType::BOOL, FL_(hpInput2), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[1].state, DeviceValueType::BOOL, FL_(hpInput2), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[1].option,
DeviceValueType::STRING,
@@ -595,7 +595,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(hpIn2Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn2Logic));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[2].state, DeviceValueType::BOOL, FL_(hpInput3), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[2].state, DeviceValueType::BOOL, FL_(hpInput3), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[2].option,
DeviceValueType::STRING,
@@ -603,7 +603,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
FL_(hpIn3Opt),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_HpIn3Logic));
// register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[3].state, DeviceValueType::BOOL, FL_(hpInput4), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpInput[3].state, DeviceValueType::BOOL, FL_(hpInput4), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&hpInput[3].option,
DeviceValueType::STRING,
@@ -1603,6 +1603,11 @@ void Boiler::process_HpPower(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpPower_, 11);
has_update(telegram, hpCompSpd_, 17);
has_bitupdate(telegram, hpInput[0].state, 1, 4);
has_bitupdate(telegram, hpInput[1].state, 1, 5);
has_bitupdate(telegram, hpInput[2].state, 1, 6);
has_bitupdate(telegram, hpInput[3].state, 1, 7);
// has_update(hpHeatingOn_, hpActivity_ == 1 ? 0xFF : 0);
// has_update(hpCoolingOn_, hpActivity_ == 2 ? 0xFF : 0);
// has_update(hpWwOn_, hpActivity_ == 3 ? 0xFF : 0);
@@ -1722,7 +1727,7 @@ void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
// 0x35 - not yet implemented
// 0x35 - not yet implemented, not readable, only for settings
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
}

View File

@@ -26,9 +26,9 @@ Heatsource::Heatsource(uint8_t device_type, uint8_t device_id, uint8_t product_i
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// AM200 alternative heatsource
if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER || (device_id >= EMSdevice::EMS_DEVICE_ID_AHS1 && device_id < EMSdevice::EMS_DEVICE_ID_HS1)) {
uint8_t tag = device_id == EMSdevice::EMS_DEVICE_ID_BOILER
? DeviceValueTAG::TAG_DEVICE_DATA
: DeviceValueTAG::TAG_AHS1 + device_id - EMSdevice::EMS_DEVICE_ID_AHS1; // heating source id, count from 0
int8_t tag = device_id == EMSdevice::EMS_DEVICE_ID_BOILER
? DeviceValueTAG::TAG_DEVICE_DATA
: DeviceValueTAG::TAG_AHS1 + device_id - EMSdevice::EMS_DEVICE_ID_AHS1; // heating source id, count from 0
register_telegram_type(0x54D, "AmTemperatures", false, MAKE_PF_CB(process_amTempMessage));
register_telegram_type(0x54E, "AmStatus", false, MAKE_PF_CB(process_amStatusMessage));
register_telegram_type(0x54F, "AmCommand", false, MAKE_PF_CB(process_amCommandMessage)); // not broadcasted, but actually not used
@@ -75,23 +75,18 @@ Heatsource::Heatsource(uint8_t device_type, uint8_t device_id, uint8_t product_i
// cascaded heating sources, only some values per individual heatsource (hs)
if (device_id >= EMSdevice::EMS_DEVICE_ID_HS1) {
uint8_t hs = device_id - EMSdevice::EMS_DEVICE_ID_HS1; // heating source id, count from 0
int8_t tag = DeviceValueTAG::TAG_HS1 + device_id - EMSdevice::EMS_DEVICE_ID_HS1; // heating source id, count from 0
// Runtime of each heatingsource in 0x06DC, ff
register_telegram_type(0x6DC + hs, "CascadeMessage", false, MAKE_PF_CB(process_CascadeMessage));
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &burnWorkMin_, DeviceValueType::TIME, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
register_telegram_type(0x6DC + device_id - EMSdevice::EMS_DEVICE_ID_HS1, "CascadeMessage", false, MAKE_PF_CB(process_CascadeMessage));
register_device_value(tag, &burnWorkMin_, DeviceValueType::TIME, FL_(burnWorkMin), DeviceValueUOM::MINUTES);
// selBurnpower in D2 and E4
// register_telegram_type(0xD2, "CascadePowerMessage", false, MAKE_PF_CB(process_CascadePowerMessage));
// individual Flowtemps and powervalues for each heatingsource in E4
register_telegram_type(0xE4, "UBAMonitorFastPlus", false, MAKE_PF_CB(process_UBAMonitorFastPlus));
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &setFlowTemp_, DeviceValueType::UINT8, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &selBurnPow_, DeviceValueType::UINT8, FL_(selBurnPow), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_HS1 + hs,
&curFlowTemp_,
DeviceValueType::UINT16,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(curFlowTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_HS1 + hs, &curBurnPow_, DeviceValueType::UINT8, FL_(curBurnPow), DeviceValueUOM::PERCENT);
register_device_value(tag, &setFlowTemp_, DeviceValueType::UINT8, FL_(setFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &selBurnPow_, DeviceValueType::UINT8, FL_(selBurnPow), DeviceValueUOM::PERCENT);
register_device_value(tag, &curFlowTemp_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(curFlowTemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &curBurnPow_, DeviceValueType::UINT8, FL_(curBurnPow), DeviceValueUOM::PERCENT);
return;
}
}

View File

@@ -26,13 +26,12 @@ uuid::log::Logger Mixer::logger_{F_(mixer), uuid::log::Facility::CONSOLE};
Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
int8_t tag = DeviceValueTAG::TAG_HC1 + device_id - 0x20;
// EMS+
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage_HC", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSSetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
register_telegram_type(device_id - 0x20 + 0x02CD, "MMPLUSSetMessage_HC", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
register_telegram_type(device_id - 0x20 + 0x02D7, "MMPLUSStatusMessage", false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
// register_telegram_type(device_id - 0x20 + 0x02E1, "MMPLUSSetMessage", true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
register_telegram_type(device_id - 0x20 + 0x02CD, "MMPLUSConfigMessage", true, MAKE_PF_CB(process_MMPLUSConfigMessage_HC));
register_device_value(tag, &flowTempHc_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flowTempHc), DeviceValueUOM::DEGREES);
register_device_value(tag, &status_, DeviceValueType::UINT8, FL_(mixerStatus), DeviceValueUOM::PERCENT);
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT8, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
@@ -55,8 +54,6 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_telegram_type(0x00AA, "MMConfigMessage", true, MAKE_PF_CB(process_MMConfigMessage));
register_telegram_type(0x00AB, "MMStatusMessage", false, MAKE_PF_CB(process_MMStatusMessage));
register_telegram_type(0x00AC, "MMSetMessage", false, MAKE_PF_CB(process_MMSetMessage));
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
register_device_value(tag, &flowTempHc_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flowTempHc), DeviceValueUOM::DEGREES);
register_device_value(tag, &status_, DeviceValueType::INT8, FL_(mixerStatus), DeviceValueUOM::PERCENT);
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT8, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
@@ -70,7 +67,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
DeviceValueUOM::SECONDS,
MAKE_CF_CB(set_setValveTime),
10,
120);
600);
}
// HT3
@@ -78,8 +75,6 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_telegram_type(0x010C, "IPMStatusMessage", false, MAKE_PF_CB(process_IPMStatusMessage));
register_telegram_type(0x011E, "IPMTempMessage", false, MAKE_PF_CB(process_IPMTempMessage));
// register_telegram_type(0x0123, "IPMSetMessage", false, MAKE_PF_CB(process_IPMSetMessage));
hc_ = device_id - 0x20 + 1;
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
register_device_value(tag, &flowTempHc_, DeviceValueType::UINT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flowTempHc), DeviceValueUOM::DEGREES);
register_device_value(tag, &status_, DeviceValueType::UINT8, FL_(mixerStatus), DeviceValueUOM::PERCENT);
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT8, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
@@ -151,10 +146,11 @@ void Mixer::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
}
// Mixer Setting 0x2DC, ..
void Mixer::process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram) {
// Mixer Config 0x2CD, ..
// mixer(0x20) -W-> Me(0x0B), ?(0x02CD), data: FF 0E 05 FF 1E 00
void Mixer::process_MMPLUSConfigMessage_HC(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, activated_, 0); // on = 0xFF
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, default 120 s, max 600 s
has_update(telegram, flowTempOffset_, 2); // Mixer increase [0-20 K]
}

View File

@@ -31,6 +31,7 @@ class Mixer : public EMSdevice {
static uuid::log::Logger logger_;
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
void process_MMPLUSConfigMessage_HC(std::shared_ptr<const Telegram> telegram);
void process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram);
void process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram);
void process_IPMTempMessage(std::shared_ptr<const Telegram> telegram);
@@ -54,8 +55,6 @@ class Mixer : public EMSdevice {
uint8_t activated_;
uint8_t setValveTime_;
uint8_t flowTempOffset_;
uint16_t hc_ = EMS_VALUE_UINT16_NOTSET;
};
} // namespace emsesp

View File

@@ -582,7 +582,7 @@ std::shared_ptr<Thermostat::DhwCircuit> Thermostat::dhw_circuit(const uint8_t of
// type 0xB1 - data from the RC10 thermostat (0x17)
// Data: 04 23 00 BA 00 00 00 BA
void Thermostat::process_RC10Monitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -599,7 +599,7 @@ void Thermostat::process_RC10Monitor(std::shared_ptr<const Telegram> telegram) {
// type 0xB0 - for reading the mode from the RC10 thermostat (0x17)
// Data: 00 FF 00 1C 20 08 01
void Thermostat::process_RC10Set(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -619,7 +619,7 @@ void Thermostat::process_RC10Set(std::shared_ptr<const Telegram> telegram) {
// type 0xB2, mode setting Data: 04 00
// not used, we read mode from monitor 0xB1
void Thermostat::process_RC10Set_2(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -633,7 +633,7 @@ void Thermostat::process_RC10Set_2(std::shared_ptr<const Telegram> telegram) {
// 0xA8 - for reading the mode from the RC20 thermostat (0x17)
// RC20Set(0xA8), data: 01 00 FF F6 01 06 00 01 0D 01 00 FF FF 01 02 02 02 00 00 05 1E 05 1E 02 1C 00 FF 00 00 26 02
void Thermostat::process_RC20Set(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -646,7 +646,7 @@ void Thermostat::process_RC20Set(std::shared_ptr<const Telegram> telegram) {
// 0x90 - for reading curve temperature from the RC20 thermostat (0x17)
//
void Thermostat::process_RC20Temp(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -662,7 +662,7 @@ void Thermostat::process_RC20Temp(std::shared_ptr<const Telegram> telegram) {
// data: E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 90 E7 (offset 54)
// data: 90 E7 90 01 00 00 01 01 00 01 01 00 01 01 00 01 01 00 00 (offset 81)
void Thermostat::process_RC20Timer(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -690,7 +690,7 @@ void Thermostat::process_RC20Timer(std::shared_ptr<const Telegram> telegram) {
// 17 00 AE 00 80 12 2E 00 D0 00 00 64 (#data=8)
// https://github.com/emsesp/EMS-ESP/issues/361
void Thermostat::process_RC20Monitor_2(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -709,7 +709,7 @@ void Thermostat::process_RC20Monitor_2(std::shared_ptr<const Telegram> telegram)
// 17 00 AD 00 01 27 29 01 4B 05 01 FF 28 19 0A 02 00 00
// RC25(0x17) -> All(0x00), ?(0xAD), data: 01 27 2D 00 44 05 01 FF 28 19 0A 07 00 00 F6 12 5A 11 00 28 05 05 00
void Thermostat::process_RC20Set_2(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -729,7 +729,7 @@ void Thermostat::process_RC20Set_2(std::shared_ptr<const Telegram> telegram) {
// 0xAF - for reading the roomtemperature from the RC20/ES72 thermostat (0x18, 0x19, ..)
void Thermostat::process_RC20Remote(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -777,7 +777,7 @@ void Thermostat::process_RemoteBattery(std::shared_ptr<const Telegram> telegram)
// type 0x0165, ff
void Thermostat::process_JunkersSet(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -805,7 +805,7 @@ void Thermostat::process_JunkersSet(std::shared_ptr<const Telegram> telegram) {
// type 0x0179, ff for Junkers_OLD
void Thermostat::process_JunkersSet2(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -839,7 +839,7 @@ void Thermostat::process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram)
// RC20Monitor(0x91), data: 90 2A 00 D5 1A 00 00 05 00 5A 04 00 D6 00
// offset 8: setburnpower to boiler, offset 9: setflowtemp to boiler (thermostat: targetflowtemp) send via 0x1A
void Thermostat::process_RC20Monitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -852,7 +852,7 @@ void Thermostat::process_RC20Monitor(std::shared_ptr<const Telegram> telegram) {
// type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
void Thermostat::process_EasyMonitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -971,7 +971,7 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr<const Telegram> telegram
return;
}
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1008,7 +1008,7 @@ void Thermostat::process_PVSettings(std::shared_ptr<const Telegram> telegram) {
}
void Thermostat::process_JunkersSetMixer(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1022,7 +1022,7 @@ void Thermostat::process_JunkersWW(std::shared_ptr<const Telegram> telegram) {
// type 0x02A5 - data from Worchester CRF200
void Thermostat::process_CRFMonitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1039,7 +1039,7 @@ void Thermostat::process_CRFMonitor(std::shared_ptr<const Telegram> telegram) {
// type 0x02A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10
// Rx: 10 0B FF 00 01 A5 80 00 01 30 23 00 30 28 01 E7 03 03 01 01 E7 02 33 00 00 11 01 03 FF FF 00 04
void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1078,7 +1078,7 @@ void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram)
// type 0x02B9 EMS+ for reading from RC300/RC310 thermostat
// Thermostat(0x10) -> Me(0x0B), RC300Set(0x2B9), data: FF 2E 2A 26 1E 02 4E FF FF 00 1C 01 E1 20 01 0F 05 00 00 02 1F
void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1132,7 +1132,7 @@ void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
// types 0x2AF ff
// RC300Summer(0x02AF), data: 00 28 00 00 3C 26 00 00 19 0F 00 (from a heatpump)
void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1158,7 +1158,7 @@ void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
// types 0x471 ff
// (0x473), data: 00 11 04 01 01 1C 08 04
void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1178,7 +1178,7 @@ void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram)
// types 0x29B ff
// Thermostat(0x10) -> Me(0x0B), RC300Curves(0x29B), data: 01 01 00 FF FF 01 05 30 52
void Thermostat::process_RC300Curve(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1276,7 +1276,7 @@ void Thermostat::process_RC300Set2(std::shared_ptr<const Telegram> telegram) {
// telegram is either offset 3 with data length of 1 and values 0/1 (radiators) - 10 0B FF 03 01 CC 01 F6
// or offset 0 with data length of 6 bytes - offset 3 values are 0x00 or 0xFF - 10 0B FF 00 01 CE FF 13 0A FF 1E 00 20
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1292,7 +1292,7 @@ void Thermostat::process_RC300Floordry(std::shared_ptr<const Telegram> telegram)
// 0x291 ff. HP mode
// thermostat(0x10) -W-> Me(0x0B), HPMode(0x0291), data: 01 00 00 03 FF 00
void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1302,7 +1302,7 @@ void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
// 0x467 ff HP settings
void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1314,7 +1314,7 @@ void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) {
// type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long
// RC30Monitor(0x41), data: 80 20 00 AC 00 00 00 02 00 05 09 00 AC 00
void Thermostat::process_RC30Monitor(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1329,7 +1329,7 @@ void Thermostat::process_RC30Monitor(std::shared_ptr<const Telegram> telegram) {
// RC30Set(0xA7), data: 01 00 FF F6 01 06 00 01 0D 00 00 FF FF 01 02 02 02 00 00 05 1F 05 1F 01 0E 00 FF
// RC30Set(0xA7), data: 00 00 20 02 (offset 27)
void Thermostat::process_RC30Set(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1357,7 +1357,7 @@ void Thermostat::process_RC30Temp(std::shared_ptr<const Telegram> telegram) {
return;
}
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1380,7 +1380,7 @@ void Thermostat::process_RC35Monitor(std::shared_ptr<const Telegram> telegram) {
return;
}
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1404,7 +1404,7 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
return;
}
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1446,7 +1446,7 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
// type 0x3F (HC1), 0x49 (HC2), 0x53 (HC3), 0x5D (HC4) - timer setting
void Thermostat::process_RC35Timer(std::shared_ptr<const Telegram> telegram) {
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
auto hc = heating_circuit(telegram);
if (hc == nullptr) {
return;
}
@@ -1517,23 +1517,25 @@ void Thermostat::process_RC35Timer(std::shared_ptr<const Telegram> telegram) {
// type 0x9A (HC1)
void Thermostat::process_RC30Vacation(std::shared_ptr<const Telegram> telegram) {
auto hc = heating_circuit(0x9A - telegram->type_id + 1);
if (hc == nullptr) {
if ((telegram->offset + telegram->message_length) > 57) {
return;
}
if (telegram->message_length + telegram->offset >= 7 && telegram->offset <= 1) {
char data[sizeof(hc->vacation)];
static uint8_t vacation_telegram[57] = {0}; // make a copy of the whole telegram to access blocks
memcpy(&vacation_telegram[telegram->offset], telegram->message_data, telegram->message_length);
for (uint8_t index = 0; index < 8; index++) {
char data[sizeof(vacation[0])];
snprintf(data,
sizeof(data),
"%02d.%02d.%04d-%02d.%02d.%04d",
telegram->message_data[1 - telegram->offset],
telegram->message_data[2 - telegram->offset],
telegram->message_data[3 - telegram->offset] + 2000,
telegram->message_data[4 - telegram->offset],
telegram->message_data[5 - telegram->offset],
telegram->message_data[7 - telegram->offset] + 2000);
has_update(hc->vacation, data, sizeof(hc->vacation));
vacation_telegram[1 + 7 * index],
vacation_telegram[2 + 7 * index],
vacation_telegram[3 + 7 * index] + 2000,
vacation_telegram[4 + 7 * index],
vacation_telegram[5 + 7 * index],
vacation_telegram[6 + 7 * index] + 2000);
if (data[1] > '0') {
has_update(vacation[index], data, sizeof(vacation[0]));
}
}
}
@@ -2459,6 +2461,27 @@ bool Thermostat::set_holiday(const char * value, const int8_t id, const bool vac
return true;
}
// set vacations as string dd.mm.yyyy-dd.mm.yyyy
bool Thermostat::set_RC30Vacation(const char * value, const int8_t id) {
if (strlen(value) != 21) {
return false;
}
uint8_t data[6];
data[0] = (value[0] - '0') * 10 + (value[1] - '0');
data[1] = (value[3] - '0') * 10 + (value[4] - '0');
data[2] = (value[7] - '0') * 100 + (value[8] - '0') * 10 + (value[9] - '0');
data[3] = (value[11] - '0') * 10 + (value[12] - '0');
data[4] = (value[14] - '0') * 10 + (value[15] - '0');
data[5] = (value[18] - '0') * 100 + (value[19] - '0') * 10 + (value[20] - '0');
if (!data[0] || data[0] > 31 || !data[1] || data[1] > 12 || !data[3] || data[3] > 31 || !data[4] || data[4] > 12) {
return false;
}
write_command(0xA9, 1 + 7 * (id - 1), data, 6, 0x9A);
return true;
}
// set pause in hours
bool Thermostat::set_pause(const char * value, const int8_t id) {
auto hc = heating_circuit(id);
@@ -3808,14 +3831,14 @@ bool Thermostat::set_temperature_value(const char * value, const int8_t id, cons
void Thermostat::register_device_values() {
// RF remote sensor seen at 0x40, maybe this is also for different hc with id 0x40 - 0x47? emsesp.cpp maps only 0x40
if (device_id() >= 0x40 && device_id() <= 0x47) {
uint8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x40;
int8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x40;
register_device_value(tag, &tempsensor1_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(RFTemp), DeviceValueUOM::DEGREES);
return;
}
// RC100H remote with humidity, this is also EMSdevice::EMS_DEVICE_FLAG_RC100 for set_calinttemp
if (device_id() >= 0x38 && device_id() <= 0x3F) {
// each device controls only one hc, so we tag the values
uint8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x38;
int8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x38;
register_device_value(tag, &tempsensor1_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(remotetemp), DeviceValueUOM::DEGREES);
register_device_value(tag, &dewtemperature_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(dewTemperature), DeviceValueUOM::DEGREES);
register_device_value(tag, &humidity_, DeviceValueType::UINT8, FL_(airHumidity), DeviceValueUOM::PERCENT);
@@ -3831,7 +3854,7 @@ void Thermostat::register_device_values() {
}
// Junkers FB10 remote, show only internal sensor
if (this->model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS && device_id() >= 0x18 && device_id() <= 0x1B) {
uint8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x18;
int8_t tag = DeviceValueTAG::TAG_HC1 + device_id() - 0x18;
register_device_value(tag, &tempsensor1_, DeviceValueType::INT16, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(remotetemp), DeviceValueUOM::DEGREES);
return;
}
@@ -4222,7 +4245,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
uint8_t model = hc->get_model();
// heating circuit
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc->hc();
int8_t tag = DeviceValueTAG::TAG_HC1 + hc->hc();
// different logic on how temperature values are stored, depending on model
uint8_t seltemp_divider;
@@ -4448,11 +4471,18 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
break;
case EMSdevice::EMS_DEVICE_FLAG_RC30:
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode));
register_device_value(tag, &hc->holiday, DeviceValueType::STRING, FL_(tpl_holidays), FL_(holidays), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday));
register_device_value(tag, &hc->vacation, DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations), DeviceValueUOM::NONE, MAKE_CF_CB(set_vacation));
// register_device_value(tag, &hc->holiday, DeviceValueType::STRING, FL_(tpl_holidays), FL_(holidays), DeviceValueUOM::NONE, MAKE_CF_CB(set_holiday));
// register_device_value(tag, &hc->vacation, DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations), DeviceValueUOM::NONE, MAKE_CF_CB(set_vacation));
// register_device_value(tag, &hc->pause, DeviceValueType::UINT8, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause));
// register_device_value(tag, &hc->party, DeviceValueType::UINT8, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party));
register_device_value(tag, &vacation[0], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations1), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation1));
register_device_value(tag, &vacation[1], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations2), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation2));
register_device_value(tag, &vacation[2], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations3), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation3));
register_device_value(tag, &vacation[3], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations4), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation4));
register_device_value(tag, &vacation[4], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations5), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation5));
register_device_value(tag, &vacation[5], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations6), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation6));
register_device_value(tag, &vacation[6], DeviceValueType::STRING, FL_(tpl_holidays), FL_(vacations7), DeviceValueUOM::NONE, MAKE_CF_CB(set_RC30Vacation7));
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode2), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, &hc->pause, DeviceValueType::UINT8, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause));
register_device_value(tag, &hc->party, DeviceValueType::UINT8, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party));
register_device_value(
tag, &hc->switchtime1, DeviceValueType::STRING, FL_(tpl_switchtime1), FL_(switchtime1), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime1));
register_device_value(
@@ -4639,7 +4669,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
// registers the values for a heating circuit
void Thermostat::register_device_values_dhw(std::shared_ptr<Thermostat::DhwCircuit> dhw) {
uint8_t tag = DeviceValueTAG::TAG_DHW1 + dhw->dhw();
int8_t tag = DeviceValueTAG::TAG_DHW1 + dhw->dhw();
switch (this->model()) {
case EMSdevice::EMS_DEVICE_FLAG_RC100:
case EMSdevice::EMS_DEVICE_FLAG_RC300:
@@ -4702,8 +4732,8 @@ void Thermostat::register_device_values_dhw(std::shared_ptr<Thermostat::DhwCircu
register_device_value(
tag, &dhw->wwDisinfectDay_, DeviceValueType::ENUM, FL_(enum_dayOfWeek), FL_(wwDisinfectDay), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwDisinfectDay));
register_device_value(tag, &dhw->wwDisinfectHour_, DeviceValueType::UINT8, FL_(wwDisinfectHour), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwDisinfectHour), 0, 23);
register_device_value(tag, &dhw->wwHoliday_, DeviceValueType::STRING, FL_(tpl_holidays), FL_(wwHolidays), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwHoliday));
register_device_value(tag, &dhw->wwVacation_, DeviceValueType::STRING, FL_(tpl_holidays), FL_(wwVacations), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwVacation));
// register_device_value(tag, &dhw->wwHoliday_, DeviceValueType::STRING, FL_(tpl_holidays), FL_(wwHolidays), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwHoliday));
// register_device_value(tag, &dhw->wwVacation_, DeviceValueType::STRING, FL_(tpl_holidays), FL_(wwVacations), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwVacation));
break;
case EMSdevice::EMS_DEVICE_FLAG_RC30_N:
register_device_value(tag, &dhw->wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode2), FL_(wwMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwmode));

View File

@@ -224,7 +224,7 @@ class Thermostat : public EMSdevice {
}
inline uint8_t id2dhw(const int8_t id) const {
return id - DeviceValueTAG::TAG_DHW1 + DeviceValueTAG::TAG_HC1 - 1;
return id - DeviceValueTAG::TAG_DHW1;
}
// each thermostat has a list of heating controller type IDs for reading and writing
@@ -271,6 +271,8 @@ class Thermostat : public EMSdevice {
uint8_t humidity_;
uint8_t battery_;
char vacation[8][22]; // RC30 only, only one hc
// HybridHP
uint8_t hybridStrategy_; // co2 = 1, cost = 2, temperature = 3, mix = 4
int8_t switchOverTemp_; // degrees
@@ -583,7 +585,28 @@ class Thermostat : public EMSdevice {
inline bool set_wwHoliday(const char * value, const int8_t id) {
return set_holiday(value, DeviceValueTAG::TAG_DHW1);
}
bool set_RC30Vacation(const char * value, const int8_t id);
inline bool set_RC30Vacation1(const char * value, const int8_t id) {
return set_RC30Vacation(value, 1);
}
inline bool set_RC30Vacation2(const char * value, const int8_t id) {
return set_RC30Vacation(value, 2);
}
inline bool set_RC30Vacation3(const char * value, const int8_t id) {
return set_RC30Vacation(value, 3);
}
inline bool set_RC30Vacation4(const char * value, const int8_t id) {
return set_RC30Vacation(value, 4);
}
inline bool set_RC30Vacation5(const char * value, const int8_t id) {
return set_RC30Vacation(value, 5);
}
inline bool set_RC30Vacation6(const char * value, const int8_t id) {
return set_RC30Vacation(value, 6);
}
inline bool set_RC30Vacation7(const char * value, const int8_t id) {
return set_RC30Vacation(value, 7);
}
bool set_datetime(const char * value, const int8_t id);
bool set_minexttemp(const char * value, const int8_t id);
bool set_clockoffset(const char * value, const int8_t id);

View File

@@ -26,9 +26,9 @@ uuid::log::Logger Water::logger_{F_(water), uuid::log::Facility::CONSOLE};
Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
uint8_t tag = DeviceValueTAG::TAG_DHW1 + device_id - EMSdevice::EMS_DEVICE_ID_DHW1;
dhw_ = device_id - EMSdevice::EMS_DEVICE_ID_DHW1;
int8_t tag = DeviceValueTAG::TAG_DHW1 + dhw_;
if (device_id == 0x2A) { // SM100, DHW3
dhw_ = 2;
// telegram handlers
register_telegram_type(0x07D6, "SM100wwTemperature", false, MAKE_PF_CB(process_SM100wwTemperature));
register_telegram_type(0x07AA, "SM100wwStatus", false, MAKE_PF_CB(process_SM100wwStatus));
@@ -64,7 +64,6 @@ Water::Water(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
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) {
dhw_ = device_id - EMSdevice::EMS_DEVICE_ID_DHW1;
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));
@@ -375,26 +374,24 @@ bool Water::set_wwKeepWarm(const char * value, const int8_t id) {
}
bool Water::set_wwDiffTemp(const char * value, const int8_t id) {
uint8_t dhw = device_id() - 0x28;
float v;
float v;
if (!Helpers::value2temperature(value, v)) {
return false;
}
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
write_command(0x313 + dhw, 7, (int8_t)(v * 10), 0x313 + dhw);
write_command(0x313 + dhw_, 7, (int8_t)(v * 10), 0x313 + dhw_);
return true;
}
return false;
}
bool Water::set_wwRequiredTemp(const char * value, const int8_t id) {
uint8_t dhw = device_id() - 0x28;
float v;
float v;
if (!Helpers::value2temperature(value, v)) {
return false;
}
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
write_command(0x313 + dhw, 4, (uint8_t)v, 0x313 + dhw);
write_command(0x313 + dhw_, 4, (uint8_t)v, 0x313 + dhw_);
return true;
}
return false;

View File

@@ -53,12 +53,12 @@ bool EMSdevice::has_entities() const {
}
// return translated tag name based on tag id
const char * EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
uint8_t tag_n = tag > DeviceValue::NUM_TAGS ? 0 : tag;
const char * EMSdevice::tag_to_string(int8_t tag, const bool translate) {
int8_t tag_n = tag > DeviceValue::NUM_TAGS ? 0 : tag;
return (translate ? Helpers::translated_word(DeviceValue::DeviceValueTAG_s[tag_n]) : DeviceValue::DeviceValueTAG_s[tag_n][0]);
}
const char * EMSdevice::tag_to_mqtt(uint8_t tag) {
const char * EMSdevice::tag_to_mqtt(int8_t tag) {
return (DeviceValue::DeviceValueTAG_mqtt[tag > DeviceValue::NUM_TAGS ? 0 : tag]);
}
@@ -358,7 +358,7 @@ bool EMSdevice::is_received(uint16_t telegram_id) const {
}
// check for a tag to create a nest
bool EMSdevice::has_tags(const uint8_t tag) const {
bool EMSdevice::has_tags(const int8_t tag) const {
for (const auto & dv : devicevalues_) {
if (dv.tag == tag && tag >= DeviceValueTAG::TAG_HC1) {
return true;
@@ -369,7 +369,7 @@ bool EMSdevice::has_tags(const uint8_t tag) const {
// check if the device has a command with this tag.
bool EMSdevice::has_cmd(const char * cmd, const int8_t id) const {
uint8_t tag = DeviceValueTAG::TAG_HC1 + id - 1;
int8_t tag = id;
for (const auto & dv : devicevalues_) {
if ((id < 1 || dv.tag == tag) && dv.has_cmd && strcmp(dv.short_name, cmd) == 0) {
return true;
@@ -502,7 +502,7 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const ch
}
// add to device value library, also know now as a "device entity"
void EMSdevice::add_device_value(uint8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
void EMSdevice::add_device_value(int8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
void * value_p, // pointer to the value from the .h file
uint8_t type, // one of DeviceValueType
const char * const ** options, // options for enum, which are translated as a list of lists
@@ -597,13 +597,13 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
uint8_t flags = CommandFlag::ADMIN_ONLY; // executing commands require admin privileges
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
flags |= CommandFlag::MQTT_SUB_FLAG_HC;
flags |= CommandFlag::CMD_FLAG_HC;
} else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) {
flags |= CommandFlag::MQTT_SUB_FLAG_DHW;
flags |= CommandFlag::CMD_FLAG_DHW;
} else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) {
flags |= CommandFlag::MQTT_SUB_FLAG_HS;
flags |= CommandFlag::CMD_FLAG_HS;
} else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) {
flags |= CommandFlag::MQTT_SUB_FLAG_AHS;
flags |= CommandFlag::CMD_FLAG_AHS;
}
// add the command to our library
@@ -612,7 +612,7 @@ void EMSdevice::add_device_value(uint8_t tag, // to b
}
// single list of options
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
@@ -624,7 +624,7 @@ void EMSdevice::register_device_value(uint8_t tag,
};
// single list of options, with no translations, with min and max
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
@@ -637,7 +637,7 @@ void EMSdevice::register_device_value(uint8_t tag,
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, min, max);
};
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
@@ -647,7 +647,7 @@ void EMSdevice::register_device_value(uint8_t tag,
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, 0, 0);
}
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
@@ -660,12 +660,12 @@ void EMSdevice::register_device_value(uint8_t tag,
}
// no options, no function
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f) {
void EMSdevice::register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f) {
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, 0, 0);
};
// no options, with min/max
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const * name,
@@ -679,7 +679,7 @@ void EMSdevice::register_device_value(uint8_t tag,
// function with min and max values
// adds a new command to the command list
// in this function we separate out the short and long names and take any translations
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
@@ -692,7 +692,7 @@ void EMSdevice::register_device_value(uint8_t tag,
}
// function with no min and max values (set to 0)
void EMSdevice::register_device_value(uint8_t tag,
void EMSdevice::register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
@@ -703,7 +703,7 @@ void EMSdevice::register_device_value(uint8_t tag,
}
// no associated command function, or min/max values
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom) {
void EMSdevice::register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom) {
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, nullptr, 0, 0);
}
@@ -720,7 +720,7 @@ bool EMSdevice::is_readable(const void * value_p) const {
// check if value/command is readonly
// matches valid tags too
bool EMSdevice::is_readonly(const std::string & cmd, const int8_t id) const {
uint8_t tag = id > 0 ? DeviceValueTAG::TAG_HC1 + id - 1 : DeviceValueTAG::TAG_NONE;
int8_t tag = id > 0 ? id : DeviceValueTAG::TAG_NONE;
for (const auto & dv : devicevalues_) {
// check command name and tag, id -1 is default hc and only checks name
if (dv.has_cmd && std::string(dv.short_name) == cmd && (dv.tag < DeviceValueTAG::TAG_HC1 || dv.tag == tag || id == -1)) {
@@ -845,10 +845,10 @@ std::string EMSdevice::get_value_uom(const std::string & shortname) const {
}
bool EMSdevice::export_values(uint8_t device_type, JsonObject output, const int8_t id, const uint8_t output_target) {
bool has_value = false;
uint8_t tag;
if (id >= 1 && id <= (1 + DeviceValueTAG::TAG_HS16 - DeviceValueTAG::TAG_HC1)) {
tag = DeviceValueTAG::TAG_HC1 + id - 1; // this sets also WWC and HS
bool has_value = false;
int8_t tag;
if (id >= 1 && id <= DeviceValueTAG::TAG_HS16) {
tag = id; // this sets also DHW and HS
} else if (id == -1 || id == 0) {
tag = DeviceValueTAG::TAG_NONE;
} else {
@@ -1109,7 +1109,7 @@ void EMSdevice::generate_values_web_customization(JsonArray output) {
});
}
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint32_t max) {
void EMSdevice::set_climate_minmax(int8_t tag, int16_t min, uint32_t max) {
for (auto & dv : devicevalues_) {
if (dv.tag == tag && (strcmp(dv.short_name, FL_(haclimate[0])) == 0)) {
if (dv.min != min || dv.max != max) {
@@ -1395,11 +1395,6 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
JsonObject json = output;
int8_t tag = id;
// check if we have hc or dhw or hs
if (id >= 1 && id <= (1 + DeviceValueTAG::TAG_HS16 - DeviceValueTAG::TAG_HC1)) {
tag = DeviceValueTAG::TAG_HC1 + id - 1;
}
// make a copy of the string command for parsing
char command_s[30];
strlcpy(command_s, cmd, sizeof(command_s));
@@ -1595,7 +1590,7 @@ void EMSdevice::publish_all_values() {
// For each value in the device create the json object pair and add it to given json
// return false if empty
// this is used to create the MQTT payloads, Console messages and Web API call responses
bool EMSdevice::generate_values(JsonObject output, const uint8_t tag_filter, const bool nested, const uint8_t output_target) {
bool EMSdevice::generate_values(JsonObject output, const int8_t tag_filter, const bool nested, const uint8_t output_target) {
bool has_values = false; // to see if we've added a value. it's faster than doing a json.size() at the end
uint8_t old_tag = 255; // NAN
JsonObject json = output;

View File

@@ -47,9 +47,9 @@ class EMSdevice {
// static functions, used outside the class like in console.cpp, command.cpp, emsesp.cpp, mqtt.cpp
static const char * device_type_2_device_name(const uint8_t device_type);
static uint8_t device_name_2_device_type(const char * topic);
static const char * tag_to_string(uint8_t tag, const bool translate = true);
static const char * tag_to_string(int8_t tag, const bool translate = true);
static const char * uom_to_string(uint8_t uom);
static const char * tag_to_mqtt(uint8_t tag);
static const char * tag_to_mqtt(int8_t tag);
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);
@@ -58,7 +58,7 @@ class EMSdevice {
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 uint8_t tag) const;
bool has_tags(const int8_t tag) const;
bool has_cmd(const char * cmd, const int8_t id) const;
inline uint8_t device_id() const {
@@ -207,7 +207,7 @@ class EMSdevice {
void list_device_entries(JsonObject output) const;
void add_handlers_ignored(const uint16_t handler);
void set_climate_minmax(uint8_t tag, int16_t min, uint32_t max);
void set_climate_minmax(int8_t tag, int16_t min, uint32_t max);
void setCustomizationEntity(const std::string & entity_id);
void getCustomizationEntities(std::vector<std::string> & entity_ids);
@@ -220,11 +220,11 @@ class EMSdevice {
void get_dv_info(JsonObject json);
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
bool generate_values(JsonObject output, const uint8_t tag_filter, const bool nested, const uint8_t output_target);
bool generate_values(JsonObject output, const int8_t tag_filter, const bool nested, const uint8_t output_target);
void generate_values_web(JsonObject output);
void generate_values_web_customization(JsonArray output);
void add_device_value(uint8_t tag,
void add_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
@@ -236,7 +236,7 @@ class EMSdevice {
int16_t min,
uint32_t max);
void register_device_value(uint8_t tag,
void register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
@@ -247,11 +247,11 @@ class EMSdevice {
uint32_t max);
void
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom);
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom);
void register_device_value(uint8_t tag,
void register_device_value(int8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
@@ -259,7 +259,7 @@ class EMSdevice {
uint8_t uom,
const cmd_function_p f = nullptr);
void register_device_value(uint8_t tag,
void register_device_value(int8_t tag,
void * value_p,
uint8_t type,
int8_t numeric_operator,
@@ -270,7 +270,7 @@ class EMSdevice {
uint32_t max);
// single list of options
void register_device_value(uint8_t tag,
void register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
@@ -279,7 +279,7 @@ class EMSdevice {
const cmd_function_p f = nullptr);
// single list of options, with no translations, with min and max
void register_device_value(uint8_t tag,
void register_device_value(int8_t tag,
void * value_p,
uint8_t type,
const char * const * options_single,
@@ -290,11 +290,10 @@ class EMSdevice {
uint32_t max);
// no options, optional function f
void register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f = nullptr);
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f = nullptr);
// no options, with min/max
void
register_device_value(uint8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint32_t max);
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint32_t max);
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const;

View File

@@ -24,7 +24,7 @@ namespace emsesp {
// constructor
DeviceValue::DeviceValue(uint8_t device_type,
uint8_t tag,
int8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,
@@ -117,8 +117,6 @@ const char * DeviceValue::DeviceValueUOM_s[] = {
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevicevalue.h
const char * const * DeviceValue::DeviceValueTAG_s[] = {
FL_(tag_none), // ""
FL_(tag_heartbeat), // ""
FL_(tag_device_data), // ""
FL_(tag_hc1), // "hc1"
FL_(tag_hc2), // "hc2"
@@ -128,7 +126,7 @@ const char * const * DeviceValue::DeviceValueTAG_s[] = {
FL_(tag_hc6), // "hc6"
FL_(tag_hc7), // "hc7"
FL_(tag_hc8), // "hc8"
FL_(tag_dhw1), // "dhw1"
FL_(tag_dhw1), // "dhw"
FL_(tag_dhw2), // "dhw2"
FL_(tag_dhw3), // "dhw3"
FL_(tag_dhw4), // "dhw4"
@@ -161,8 +159,6 @@ const char * const * DeviceValue::DeviceValueTAG_s[] = {
// tags used in MQTT topic names. Macthes sequence from DeviceValueTAG_s
const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
FL_(tag_none)[0], // ""
F_(heartbeat), // "heartbeat"
FL_(tag_device_data)[0], // ""
FL_(tag_hc1)[0], // "hc1"
FL_(tag_hc2)[0], // "hc2"
@@ -172,7 +168,7 @@ const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
FL_(tag_hc6)[0], // "hc6"
FL_(tag_hc7)[0], // "hc7"
FL_(tag_hc8)[0], // "hc8"
FL_(tag_dhw1)[0], // "dhw1"
FL_(tag_dhw1)[0], // "dhw"
FL_(tag_dhw2)[0], // "dhw2"
FL_(tag_dhw3)[0], // "dhw3"
FL_(tag_dhw4)[0], // "dhw4"
@@ -317,11 +313,11 @@ bool DeviceValue::get_custom_min(int16_t & val) {
bool has_min = (min_pos != std::string::npos);
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
if (has_min) {
int16_t v = Helpers::atoint(custom_fullname.substr(min_pos + 1).c_str());
int32_t v = Helpers::atoint(custom_fullname.substr(min_pos + 1).c_str());
if (fahrenheit) {
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
}
if (max > 0 && v > max) {
if (max > 0 && v > 0 && (uint32_t)v > max) {
return false;
}
val = v;
@@ -339,7 +335,7 @@ bool DeviceValue::get_custom_max(uint32_t & val) {
if (fahrenheit) {
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
}
if (v < 0 || v < min) {
if (v < 0 || v < (int32_t)min) {
return false;
}
val = v;

View File

@@ -77,10 +77,9 @@ class DeviceValue {
};
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
enum DeviceValueTAG : uint8_t {
TAG_NONE = 0, // wild card
TAG_HEARTBEAT,
TAG_DEVICE_DATA,
enum DeviceValueTAG : int8_t {
TAG_NONE = -1, // wild card
TAG_DEVICE_DATA = 0,
TAG_HC1,
TAG_HC2,
TAG_HC3,
@@ -148,7 +147,7 @@ class DeviceValue {
};
uint8_t device_type; // EMSdevice::DeviceType
uint8_t tag; // DeviceValueTAG::*
int8_t tag; // DeviceValueTAG::*
void * value_p; // pointer to variable of any type
uint8_t type; // DeviceValueType::*
const char * const ** options; // options as a flash char array
@@ -165,7 +164,7 @@ class DeviceValue {
uint8_t state; // DeviceValueState::*
DeviceValue(uint8_t device_type,
uint8_t tag,
int8_t tag,
void * value_p,
uint8_t type,
const char * const ** options,

View File

@@ -635,7 +635,7 @@ void EMSESP::publish_device_values(uint8_t device_type) {
bool nested = (Mqtt::is_nested());
// group by device type
for (uint8_t tag = DeviceValueTAG::TAG_DEVICE_DATA; tag <= DeviceValueTAG::TAG_HS16; tag++) {
for (int8_t tag = DeviceValueTAG::TAG_DEVICE_DATA; tag <= DeviceValueTAG::TAG_HS16; tag++) {
JsonObject json_tag = json;
bool nest_created = false;
for (const auto & emsdevice : emsdevices) {
@@ -647,7 +647,7 @@ void EMSESP::publish_device_values(uint8_t device_type) {
need_publish |= emsdevice->generate_values(json_tag, tag, false, EMSdevice::OUTPUT_TARGET::MQTT);
}
}
if (need_publish && (!nested && tag >= DeviceValueTAG::TAG_DEVICE_DATA)) {
if (need_publish && !nested) {
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, tag), json);
json = doc.to<JsonObject>();
need_publish = false;

View File

@@ -361,7 +361,7 @@ MAKE_TRANSLATION(upTimeTotal, "uptimetotal", "heatpump total uptime", "Wärmpepu
MAKE_TRANSLATION(upTimeControl, "uptimecontrol", "total operating time heat", "Betriebszeit Heizen gesamt", "Totale bedrijfstijd", "Total tid uppvärmning", "łączny czas generowania ciepła", "total driftstid", "durée totale de fonctionnement chauffage", "ısınma toplam işletme süresi", "Tempo di funzionamento totale riscaldamento", "celkový prevádzkový čas tepla")
MAKE_TRANSLATION(upTimeCompHeating, "uptimecompheating", "operating time compressor heating", "Betriebszeit Kompressor heizen", "Bedrijfstijd compressor verwarmingsbedrijf", "Total tid kompressor uppvärmning", "łączny czas ogrzewania (sprężarka)", "totaltid kompressor", "durée de fonctionnement compresseur chauffage", "ısı pompası ısınma işletme süresi", "tempo di funzionamento del compressore riscaldamento", "prevádzková doba vykurovania kompresora")
MAKE_TRANSLATION(upTimeCompCooling, "uptimecompcooling", "operating time compressor cooling", "Betriebszeit Kompressor kühlen", "Bedrijfstijd compressor koelbedrijf", "Total tid kompressor kyla", "łączny czas chłodzenia (sprężarka)", "Total tid kompressor kjøling", "durée de fonctionnement compresseur refroidissement", "ısı pompası soğuma işletme süresi", "tempo di funzionamento del compressore raffreddamento", "doba prevádzky chladenia kompresora")
MAKE_TRANSLATION(upTimeCompWw, "uptimecompdhw", "operating time compressor", "Betriebszeit Kompressor", "Bedrijfstijd compressor", "Total tid kompressor", "łączny czas grzania c.w.u. (sprężarka)", "Total tid kompressor", "durée de fonctionnement compresseur", "ısı pompası sıcak kullanım suyu işletme süresi", "tempo di funzionamento del compressore", "prevádzková doba kompresora")
MAKE_TRANSLATION(upTimeCompWw, "uptimecomp", "operating time compressor", "Betriebszeit Kompressor", "Bedrijfstijd compressor", "Total tid kompressor", "łączny czas grzania c.w.u. (sprężarka)", "Total tid kompressor", "durée de fonctionnement compresseur", "ısı pompası sıcak kullanım suyu işletme süresi", "tempo di funzionamento del compressore", "prevádzková doba kompresora")
MAKE_TRANSLATION(upTimeCompPool, "uptimecomppool", "operating time compressor pool", "Betriebszeit Kompressor Pool", "Bedrijfstijd compressor voor zwembadbedrijf", "Total tid kompressor pool", "łączny czas podgrzewania basenu (sprężarka)", "Total tid kompressor basseng", "durée de fonctionnement compresseur piscine", "ısı pompası havuz işletme süresi", "tempo di funzionamento del compressore piscina", "prevádzková doba kompresorového bazéna")
MAKE_TRANSLATION(totalCompStarts, "totalcompstarts", "total compressor control starts", "Kompressor Starts gesamt", "Totaal compressorstarts", "Kompressorstarter Totalt", "liczba załączeń sprężarki", "kompressorstarter totalt", "nombre démarrages total contrôle compresseur", "ısı pompası kontrolü toplam başlatma", "avvii totali del compressore", "spustí sa celkové riadenie kompresora")
MAKE_TRANSLATION(heatingStarts, "heatingstarts", "heating control starts", "Heizen Starts", "Starts verwarmingsbedrijf", "Kompressorstarter Uppvärmning", "liczba załączeń ogrzewania", "kompressorstarter oppvarming", "démarrages contrôle chauffage", "ısıtma kontrolü toplam başlatma", "avvii riscaldamento", "ovládanie vykurovania sa spustí")
@@ -370,17 +370,17 @@ MAKE_TRANSLATION(poolStarts, "poolstarts", "pool control starts", "Pool Starts",
MAKE_TRANSLATION(nrgConsTotal, "nrgconstotal", "total energy consumption", "Energieverbrauch gesamt", "Energieverbrauch gesamt", "Energiförbrukning totalt", "energia pobrana (sumarycznie)", "energiforbruk totalt", "consommation totale énergie", "toplam enerji tüketimi", "totale energia consumata", "celková spotreba energie")
MAKE_TRANSLATION(nrgConsCompTotal, "nrgconscomptotal", "total energy consumption compressor", "Energieverbrauch Kompressor gesamt", "Energieverbruik compressor totaal", "Energiförbrukning kompressor", "energia pobrana przez sprężarkę", "energiforbruk kompressor", "consommation totale énergie compresseur", "ısı pompası toplam enerji tüketimi", "totale energia consumata compressore", "kompresor s celkovou spotrebou energie")
MAKE_TRANSLATION(nrgConsCompHeating, "nrgconscompheating", "energy consumption compressor heating", "Energieverbrauch Kompressor heizen", "Energieverbruik compressor verwarmingsbedrijf", "Energiförbrukning uppvärmning", "energia pobrana przez sprężarkę na ogrzewanie", "energiforbruk oppvarming", "consommation énergie compresseur chauffage", "ısı pompası ısıtma toplam enerji tüketimi", "consumo energia compressore riscaldamento", "spotreba energie vykurovanie kompresorom")
MAKE_TRANSLATION(nrgConsCompWw, "nrgconscompdhw", "energy consumption compressor", "Energieverbrauch Kompressor", "Energieverbruik compressor", "Energiförbrukning", "energia pobrana przez sprężarkę na c.w.u.", "energiforbruk", "consommation énergie compresseur", "ısı pompası sıcak kullanım suyu toplam enerji tüketimi", "consumo energia compressore", "kompresor spotreby energie")
MAKE_TRANSLATION(nrgConsCompWw, "nrgconscomp", "energy consumption compressor", "Energieverbrauch Kompressor", "Energieverbruik compressor", "Energiförbrukning", "energia pobrana przez sprężarkę na c.w.u.", "energiforbruk", "consommation énergie compresseur", "ısı pompası sıcak kullanım suyu toplam enerji tüketimi", "consumo energia compressore", "kompresor spotreby energie")
MAKE_TRANSLATION(nrgConsCompCooling, "nrgconscompcooling", "energy consumption compressor cooling", "Energieverbrauch Kompressor kühlen", "Energieverbruik compressor koelbedrijf", "Energiförbrukning kyla", "energia pobrana przez sprężarkę na chłodzenie", "energiforbruk kjøling", "consommation énergie compresseur refroidissement", "ısı pompası soğutma toplam enerji tüketimi", "consumo energia compressore raffreddamento", "spotreba energie kompresorové chladenie")
MAKE_TRANSLATION(nrgConsCompPool, "nrgconscomppool", "energy consumption compressor pool", "Energieverbrauch Kompressor Pool", "Energiebedrijf compressor zwembadbedrijf", "Energiförbrukning pool", "energia pobrana przez sprężarkę na podgrzewanie basenu", "energiforbruk basseng", "consommation énergie compresseur piscine", "ısı pompası havuz toplam enerji tüketimi", "consumo energia compressore piscina", "spotreba energie kompresorový bazén")
MAKE_TRANSLATION(nrgSuppTotal, "nrgsupptotal", "total energy supplied", "gesamte Energieabgabe", "Totaal opgewekte energie", "Genererad energi", "energia oddana (sumarycznie)", "tilført energi", "énergie totale fournie", "sağlanan toplam enerji", "totale energia fornita", "celková dodaná energia")
MAKE_TRANSLATION(nrgSuppHeating, "nrgsuppheating", "total energy supplied heating", "gesamte Energieabgabe heizen", "Opgewekte energie verwarmingsbedrijf", "Genererad energi Uppvärmning", "energia oddana na ogrzewanie", "tilført energi oppvarming", "énergie totale fournie chauffage", "ısıtma sağlanan toplam enerji", "energia totale fornita - riscaldamento", "celková dodaná energia na vykurovanie")
MAKE_TRANSLATION(nrgSuppWw, "nrgsuppdhw", "total energy warm supplied", "gesamte Energieabgabe", "Opgewekte energie", "Genererad energi", "energia oddana na c.w.u.", "tilført energi", "énergie chaude totale fournie", "sıcak kullanım suyu sağlanan toplam enerji", "totale energia calorica fornita", "celková dodaná teplá energia")
MAKE_TRANSLATION(nrgSuppWw, "nrgsupp", "total energy warm supplied", "gesamte Energieabgabe", "Opgewekte energie", "Genererad energi", "energia oddana na c.w.u.", "tilført energi", "énergie chaude totale fournie", "sıcak kullanım suyu sağlanan toplam enerji", "totale energia calorica fornita", "celková dodaná teplá energia")
MAKE_TRANSLATION(nrgSuppCooling, "nrgsuppcooling", "total energy supplied cooling", "gesamte Energieabgabe kühlen", "Opgewekte energie koelbedrijf", "Genererad energi Kyla", "energia oddana na chłodzenie", "Tillført energi kjøling", "énergie totale fournie refroidissement", "soğutma sağlanan toplam enerji", "energia totale fornita - raffreddamento", "chladenie s celkovou dodanou energiou")
MAKE_TRANSLATION(nrgSuppPool, "nrgsupppool", "total energy supplied pool", "gesamte Energieabgabe Pool", "Opgewekte energie zwembadbedrijf", "Genererad energi Pool", "energia oddana na podgrzewanie basenu", "tilført energi basseng", "énergie totale fournie piscine", "havuz sağlanan toplam enerji", "totale di energia fornita- piscina", "celkový bazén dodanej energie")
MAKE_TRANSLATION(auxElecHeatNrgConsTotal, "auxelecheatnrgconstotal", "total aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Totaal energieverbruik electrisch verwarmingselement", "Energiförbrukning Eltillkott", "energia pobrana przez grzałki", "energiforbruk varmekolbe", "consommation totale énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı toplam enerji tüketimi", "consumo energetico riscaldamento elettrico supplementare", "celková spotreba energie prídavného elektrického ohrievača")
MAKE_TRANSLATION(auxElecHeatNrgConsHeating, "auxelecheatnrgconsheating", "aux elec. heater energy consumption heating", "Energieverbrauch el. Zusatzheizung Heizen", "Energieverbruik electrisch verwarmingselement voor verwarmingsbedrijf", "Energiförbrukning Eltillskott Uppvärmning", "energia pobrana przez grzałki na ogrzewanie", "energiforbruk varmekolbe oppvarming", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı ısınma toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario", "pomocný elektrický ohrievač spotreba energie vykurovanie")
MAKE_TRANSLATION(auxElecHeatNrgConsWw, "auxelecheatnrgconsdhw", "aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Energieverbruik electrisch verwarmingselement voor", "Energiförbrukning Eltillskott", "energia pobrana przez grzałki na c.w.u.", "energiförbruk varmekolbe", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı sıcak kullanım suyu toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario", "spotreba energie pomocného elektrického ohrievača")
MAKE_TRANSLATION(auxElecHeatNrgConsWw, "auxelecheatnrgcons", "aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Energieverbruik electrisch verwarmingselement voor", "Energiförbrukning Eltillskott", "energia pobrana przez grzałki na c.w.u.", "energiförbruk varmekolbe", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı sıcak kullanım suyu toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario", "spotreba energie pomocného elektrického ohrievača")
MAKE_TRANSLATION(auxElecHeatNrgConsPool, "auxelecheatnrgconspool", "aux elec. heater energy consumption pool", "Energieverbrauch el. Zusatzheizung Pool", "Energieverbruik electrisch verwarmingselement voor zwembadbedrijf", "Energiförbrukning Eltillskott Pool", "energia pobrana przez grzałki na podgrzewanie basenu", "energiforbruk el. tilleggsvarme basseng", "consommation énergie electrique auxiliaire chauffage piscine", "ilave elektrikli ısıtıcı havuz toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario piscina", "bazén spotreby energie pomocného elektrického ohrievača")
MAKE_TRANSLATION(hpCompOn, "hpcompon", "hp compressor", "WP Kompressor", "WP compressor", "VP Kompressor", "sprężarka pompy ciepła", "vp kompressor", "compresseur pompe à chaleur", "hp ısı pompası", "compressore pompa calore", "hp kompresor")
@@ -424,7 +424,7 @@ MAKE_TRANSLATION(hpIn3Opt, "hpin3opt", "input 3 options", "Eingang 3 Einstellung
MAKE_TRANSLATION(hpIn4Opt, "hpin4opt", "input 4 options", "Eingang 4 Einstellung", "Instelling input 4", "Inställningar Ingång 4", "opcje wejścia 4", "innstillinger inngang 4", "options entrée 4", "giriş 4 seçenekleri", "impostazioni ingresso 4", "možnosti vstupu 4")
MAKE_TRANSLATION(maxHeatComp, "maxheatcomp", "heat limit compressor", "Heizgrenze Kompressor", "heat limit compressor", "heat limit compressor", "ograniczenie mocy sprężarki", "max varmegrense kompressor", "limite chaleur compresseur", "ısı pompası ısıtma sınırı", "limite riscaldamento compressore", "tepelný limit kompresora")
MAKE_TRANSLATION(maxHeatHeat, "maxheatheat", "heat limit heating", "Heizgrenze Heizen", "heat limit heating", "heat limit heating", "ograniczenie mocy w trybie ogrzewania", "maks varmegrense oppvarming", "limite chaleur chauffage", "ısınma ısıtma sınırı", "limite calore riscaldamento", "vyhrievanie limitu tepla")
MAKE_TRANSLATION(maxHeatDhw, "maxheatdhw", "heat limit", "Heizgrenze", "heat limit", "heat limit", "ograniczenie mocy w trybie c.w.u.", "varmegrense", "limite chaleur", "sıcak kullanım suyu ısınma sınırı", "limite calore", "tepelný limit")
MAKE_TRANSLATION(maxHeatDhw, "maxheat", "heat limit", "Heizgrenze", "heat limit", "heat limit", "ograniczenie mocy w trybie c.w.u.", "varmegrense", "limite chaleur", "sıcak kullanım suyu ısınma sınırı", "limite calore", "tepelný limit")
MAKE_TRANSLATION(auxHeaterOff, "auxheateroff", "disable aux heater", "Verbiete Zusatzheizer", "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č")
MAKE_TRANSLATION(auxHeaterStatus, "auxheaterstatus", "aux heater status", "Status Zusatzheizer", "Bijverwarming", "Eltillskott Status", "status dogrzewacza", "status el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "stato riscaldatori addizionali", "stav pomocného ohrievača")
@@ -457,7 +457,7 @@ MAKE_TRANSLATION(auxMaxLimit, "auxmaxlimit", "aux heater max limit", "Zusatzheiz
MAKE_TRANSLATION(auxLimitStart, "auxlimitstart", "aux heater limit start", "Zusatzheizer Grenze Start", "Bijverwarmer grens voor start", "", "dogrzewacz, początek ograniczenia", "tillegsvarme startgrense", "", "ilave ısıtıcı limir başlangıcı", "avvio limite massimo riscaldatore addizionale", "spustenie limitu pomocného ohrievača") // TODO translate
MAKE_TRANSLATION(manDefrost, "mandefrost", "manual defrost", "Manuelle Enteisung", "Handmatige ontdooicyclus", "", "ręczne odladzanie", "manuell avisning", "", "manuel buz çözme", "sbrinamento manuale", "manuálne odmrazovanie") // TODO translate
MAKE_TRANSLATION(pvCooling, "pvcooling", "cooling only with PV", "Kühlen nur mit PV", "Koelen alleen met solar PV", "", "chłodzenie tylko z PV", "kjøling med solpanel", "", "sadece PV ile soğutma", "solo raffrescamento con solare", "Chladenie len s FV") // TODO translate
MAKE_TRANSLATION(hpCircPumpWw, "hpcircpumpdhw", "circulation pump available during dhw", "Zirkulation möglich bei WW-Bereitung", "Circulatiepomp WP beschikbaar tijdens ww", "", "pompa cyrkulacji dostępna w trakcie c.w.u.", "sirkulasjonspumpe tilgjengelig under varmtvann", "", "SKS esnasında sirkülasyon pompasu uygun", "pompa di circolazione disponibile durante ACS", "obehové čerpadlo k dispozícii počas TÚV") // TODO translate
MAKE_TRANSLATION(hpCircPumpWw, "hpcircpump", "circulation pump available during dhw", "Zirkulation möglich bei WW-Bereitung", "Circulatiepomp WP beschikbaar tijdens ww", "", "pompa cyrkulacji dostępna w trakcie c.w.u.", "sirkulasjonspumpe tilgjengelig under varmtvann", "", "SKS esnasında sirkülasyon pompasu uygun", "pompa di circolazione disponibile durante ACS", "obehové čerpadlo k dispozícii počas TÚV") // TODO translate
MAKE_TRANSLATION(vp_cooling, "vpcooling", "valve/pump cooling", "Ventil/Pumpe für Kühlen", "Klep koeling", "", "zawór/pompa chłodzenia", "varmepumpe kjøling", "", "vana/pompa soğuyor", "valvola/pompa raffrescamento", "chladenie ventilu/čerpadla") // TODO translate
MAKE_TRANSLATION(VC0valve, "vc0valve", "VC0 valve", "VC0 Ventil", "Klep VC0", "", "zawór VC0", "vc0 ventil", "", "VC0 vana", "valvola VC0", "VC0 ventil") // TODO translate
MAKE_TRANSLATION(primePump, "primepump", "primary heatpump", "Hauptpumpe", "Hoofdpomp", "", "główna pompa ciepła", "primærpumpe", "", "ana ısı pompası", "pompa principale riscaldamento", "primárne tepelné čerpadlo") // TODO translate
@@ -469,7 +469,7 @@ MAKE_TRANSLATION(elHeatStep2, "elheatstep2", "el. heater step 2", "El. Heizer St
MAKE_TRANSLATION(elHeatStep3, "elheatstep3", "el. heater step 3", "El. Heizer Stufe 3", "Electrische bijverwarmer niveau 3", "", "dogrzewacz poziom 3", "el-kolbe steg 3", "", "el.ısıtıcı adım 3", "riscaldatore elettrico livello 3", "krok 3 elektrického ohrievača") // TODO translate
MAKE_TRANSLATION(wwAlternatingOper, "alternatingop", "alternating operation", "Wechselbetrieb", "Wisselbedrijf ww", "", "praca naprzemienna", "alternativ drift", "", "sıcak kullanım suyu alternatif işletim", "funzionamento alternato", "striedavá prevádzka") // TODO translate
MAKE_TRANSLATION(wwAltOpPrioHeat, "altopprioheat", "prioritise heating during dhw", "Heizen bevorzugt vor WW", "Proriteit verwarming boven ww", "", "czas na ogrzewanie w trakcie c.w.u", "prioritert oppvarmning", "", "sıcak kullanım suyu esnasında ısıtmayı öne al", "dare la priorità al riscaldamento durante l'ACS", "Uprednostniť ohrev počas TÚV") // TODO translate
MAKE_TRANSLATION(wwAltOpPrioWw, "altoppriodhw", "prioritise dhw during heating", "WW bevorzugt vor Heizen", "Prioriteit ww boven verwarming", "", "czas na c.w.u w trakcie ogrzewania", "prioritert varmtvann", "", "ısıtma esnasında sıcak kullanım suyunu öne al", "dare priorità all'acqua calda durante il riscaldamento", "uprednostniť TÚV počas ohrevu") // TODO translate
MAKE_TRANSLATION(wwAltOpPrioWw, "altopprio", "prioritise dhw during heating", "bevorzugt vor Heizen", "Prioriteit ww boven verwarming", "", "czas na c.w.u w trakcie ogrzewania", "prioritert varmtvann", "", "ısıtma esnasında sıcak kullanım suyunu öne al", "dare priorità all'acqua calda durante il riscaldamento", "uprednostniť TÚV počas ohrevu") // TODO translate
MAKE_TRANSLATION(hpEA0, "hpea0", "condensate reservoir heating (EA0)", "Heizung Kondensatwanne (EA0)", "", "", "ogrzewanie zbiornika kondensatu (EA0)", "", "", "", "", "ohrievanie zásobníka kondenzátu (EA0)") // TODO translate
MAKE_TRANSLATION(boost, "boost", "boost mode", "Boost", "", "", "tryb wzmocnienia (boost)", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(boosttime, "boosttime", "boost time", "Boost Dauer", "", "", "czas trwania wzmocnienia", "", "", "", "", "") // TODO translate
@@ -542,17 +542,17 @@ MAKE_TRANSLATION(releaseWait, "releasewait", "boiler release wait time", "Wartez
// energy
MAKE_TRANSLATION(nrgTotal, "nrgtotal", "total energy", "Energie gesamt", "", "", "całkowita energia", "", "", "", "", "celková energia") // TODO translate
MAKE_TRANSLATION(nrgHeat, "nrgheat", "energy heating", "Energie Heizen", "", "", "energia na ogrzewanie", "", "", "ısıtma enerjisi", "", "energetické vykurovanie") // TODO translate
MAKE_TRANSLATION(nrgWw, "nrgdhw", "energy", "Energie", "", "", "energia", "", "", "sıcak kullanım suyu enerjisi", "", "energia") // TODO translate
MAKE_TRANSLATION(nrgWw, "nrg", "energy", "Energie", "", "", "energia", "", "", "sıcak kullanım suyu enerjisi", "", "energia") // TODO translate
MAKE_TRANSLATION(nrgHeat2, "nrgheat2", "energy heating 2", "Energie Heizen 2", "", "", "energia na ogrzewanie 2", "", "", "ısıtma enerjisi 2", "", "energetické vykurovanie") // TODO translate
MAKE_TRANSLATION(nrgWw2, "nrgdhw2", "energy 2", "Energie 2", "", "", "energia 2", "", "", "sıcak kullanım suyu enerjisi 2", "", "energia 2") // TODO translate
MAKE_TRANSLATION(nrgWw2, "nrg2", "energy 2", "Energie 2", "", "", "energia 2", "", "", "sıcak kullanım suyu enerjisi 2", "", "energia 2") // TODO translate
MAKE_TRANSLATION(nomPower, "nompower", "nominal Power", "Brennerleistung", "", "", "moc nominalna", "", "", "nominal güç", "", "nominálny výkon") // TODO translate
MAKE_TRANSLATION(meterTotal, "metertotal", "meter total", "Messung gesamt", "", "", "licznik całkowity", "", "", "", "", "meter celkom") // TODO translate
MAKE_TRANSLATION(meterComp, "metercomp", "meter compressor", "Messung Kompressor", "", "", "licznik sprężarki", "", "", "", "", "meter kompresor") // TODO translate
MAKE_TRANSLATION(meterEHeat, "metereheat", "meter e-heater", "Messung E-Heizer", "", "", "licznik dogrzewacza", "", "", "", "", "elektrický ohrievač") // TODO translate
MAKE_TRANSLATION(meterHeat, "meterheat", "meter heating", "Messung Heizen", "", "", "licznik ogrzewania", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(meterWw, "meterdhw", "meter", "Messung", "", "", "licznik", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(meterWw, "meter", "meter", "Messung", "", "", "licznik", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(gasMeterHeat, "gasmeterheat", "gas meter heating", "Gas Messung Heizen", "", "", "licznik gazu na ogrzewanie", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(gasMeterWw, "gasmeterdhw", "gas meter", "Gas Messung", "", "", "licznik gazu", "", "", "", "", "") // TODO translate
MAKE_TRANSLATION(gasMeterWw, "gasmeter", "gas meter", "Gas Messung", "", "", "licznik gazu", "", "", "", "", "") // TODO translate
// HIU
MAKE_TRANSLATION(netFlowTemp, "netflowtemp", "heat network flow temp", "System Vorlauftemperatur", "Netto aanvoertemperatuur", "", "temp. zasilania sieci cieplnej", "", "", "ısıtma şebekesi akış derecesi", "temperatura di mandata della rete di riscaldamento", "teplota prívodu tepelnej siete") // TODO translate
@@ -710,6 +710,14 @@ MAKE_TRANSLATION(nofrostmode1, "nofrostmode1", "nofrost mode", "Frostschutz", "V
MAKE_TRANSLATION(reducehours, "reducehours", "duration for nighttemp", "Dauer Nachttemp.", "Duur nachtverlaging", "Timmar Nattsänkning", "czas trwania trybu nocnego", "timer nattsenkning", "durée température nuit", "gece sıcaklığı süresi", "durata temperatura notturna", "trvanie nočnej teploty")
MAKE_TRANSLATION(reduceminutes, "reduceminutes", "remaining time for nightmode", "Restzeit Nachttemp.", "Resterende tijd nachtverlaging", "Återstående Tid Nattläge", "czas do końca trybu nocnego", "gjenværende tid i nattstilling", "temps restant mode nuit", "gece modu için kalan süre", "temperatura notturna residua", "zostávajúci čas pre nočný režim")
MAKE_TRANSLATION(switchonoptimization, "switchonoptimization", "switch-on optimization", "Einschaltoptimierung", "Inschakeloptimalisering", "Växlingsoptimering", "optymalizacja załączania", "slå på optimalisering", "optimisation mise en marche", "optimizasyonu aç", "ottimizzazione all'accensione", "optimalizácia pri zapnutí")
MAKE_TRANSLATION(vacations1, "vacations1", "vacation dates 1", "Urlaubstage 1", "Vakantiedagen 1", "Semesterdatum 1", "urlop 1", "feriedager 1", "dates vacances 1", "izin günleri 1", "date vacanze 1", "termíny dovolenky 1")
MAKE_TRANSLATION(vacations2, "vacations2", "vacation dates 2", "Urlaubstage 2", "Vakantiedagen 2", "Semesterdatum 2", "urlop 2", "feriedager 2", "dates vacances 2", "izin günleri 2", "date vacanze 2", "termíny dovolenky 2")
MAKE_TRANSLATION(vacations3, "vacations3", "vacation dates 3", "Urlaubstage 3", "Vakantiedagen 3", "Semesterdatum 3", "urlop 3", "feriedager 3", "dates vacances 3", "izin günleri 3", "date vacanze 3", "termíny dovolenky 3")
MAKE_TRANSLATION(vacations4, "vacations4", "vacation dates 4", "Urlaubstage 4", "Vakantiedagen 4", "Semesterdatum 4", "urlop 4", "feriedager 4", "dates vacances 4", "izin günleri 4", "date vacanze 4", "termíny dovolenky 4")
MAKE_TRANSLATION(vacations5, "vacations5", "vacation dates 5", "Urlaubstage 5", "Vakantiedagen 5", "Semesterdatum 5", "urlop 5", "feriedager 5", "dates vacances 5", "izin günleri 5", "date vacanze 5", "termíny dovolenky 5")
MAKE_TRANSLATION(vacations6, "vacations6", "vacation dates 6", "Urlaubstage 6", "Vakantiedagen 6", "Semesterdatum 6", "urlop 6", "feriedager 6", "dates vacances 6", "izin günleri 6", "date vacanze 6", "termíny dovolenky 6")
MAKE_TRANSLATION(vacations7, "vacations7", "vacation dates 7", "Urlaubstage 7", "Vakantiedagen 7", "Semesterdatum 7", "urlop 7", "feriedager 7", "dates vacances 7", "izin günleri 7", "date vacanze 7", "termíny dovolenky 7")
MAKE_TRANSLATION(vacations8, "vacations8", "vacation dates 8", "Urlaubstage 8", "Vakantiedagen 8", "Semesterdatum 8", "urlop 8", "feriedager 8", "dates vacances 8", "izin günleri 8", "date vacanze 8", "termíny dovolenky 8")
MAKE_TRANSLATION(hpmode, "hpmode", "HP Mode", "WP Modus", "Modus warmtepomp", "", "tryb pracy pompy ciepła", "", "", "yüksek güç modu", "Modalità Termopompa", "Režim HP") // TODO translate
MAKE_TRANSLATION(dewoffset, "dewoffset", "dew point offset", "Taupunkt Differenz", "Offset dauwpunt", "", "przesunięcie punktu rosy", "", "", "çiğ noktası göreli", "differenza del punto di rugiada", "posun rosného bodu") // TODO translate

View File

@@ -800,14 +800,14 @@ bool Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, cons
ids.add(Mqtt::basename());
return publish_ha_sensor_config(
type, DeviceValueTAG::TAG_HEARTBEAT, name, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, 0, dev_json);
type, DeviceValueTAG::TAG_DEVICE_DATA, name, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, 0, dev_json);
}
// MQTT discovery configs
// entity must match the key/value pair in the *_data topic
// note: some extra string copying done here, it looks messy but does help with heap fragmentation issues
bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType
uint8_t tag, // EMSdevice::DeviceValueTAG
int8_t tag, // EMSdevice::DeviceValueTAG
const char * const fullname, // fullname, already translated
const char * const en_name, // original name in english
const uint8_t device_type, // EMSdevice::DeviceType
@@ -841,6 +841,10 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
// build unique identifier also used as object_id which also becomes the Entity ID in HA
char uniq_id[80];
// list of boiler entities that need conversion for 3.6 compatibility, add ww suffix
const char * dhw_old[] =
{FL_(nrgWw)[0], FL_(upTimeCompWw)[0], FL_(nrgConsCompWw)[0], FL_(auxElecHeatNrgConsWw)[0], FL_(nrgSuppWw)[0], FL_(wwAltOpPrioWw)[0], FL_(hpCircPumpWw)[0]};
uint8_t num_dhw_old = sizeof(dhw_old) / sizeof(dhw_old[0]);
if (Mqtt::entity_format() == entityFormat::MULTI_SHORT) {
// prefix base name to each uniq_id and use the shortname
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag);
@@ -852,11 +856,12 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
if (has_tag && (device_type == EMSdevice::DeviceType::BOILER || device_type == EMSdevice::DeviceType::THERMOSTAT)
&& tag == DeviceValue::DeviceValueTAG::TAG_DHW1) {
snprintf(entity_with_tag, sizeof(entity_with_tag), "ww%s", entity);
snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, entity_with_tag);
if (strcmp(entity, "nrgdhw") == 0) { // special case for tp1de #1714
strcpy(uniq_id, "boiler_nrgww");
strcpy(entity_with_tag, "nrgww");
for (uint8_t i = 0; i < num_dhw_old; i++) {
if (strcmp(entity, dhw_old[i]) == 0) { // special case for tp1de #1714
snprintf(entity_with_tag, sizeof(entity_with_tag), "%sww", dhw_old[i]);
}
}
snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, entity_with_tag);
} else if (has_tag && device_type == EMSdevice::DeviceType::WATER && tag >= DeviceValue::DeviceValueTAG::TAG_DHW3) {
snprintf(entity_with_tag, sizeof(entity_with_tag), "wwc%d_%s", tag - DeviceValue::DeviceValueTAG::TAG_DHW1 + 1, entity);
snprintf(uniq_id, sizeof(uniq_id), "solar_%s", entity_with_tag);
@@ -871,10 +876,12 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
if (has_tag && (device_type == EMSdevice::DeviceType::BOILER || device_type == EMSdevice::DeviceType::THERMOSTAT)
&& tag == DeviceValue::DeviceValueTAG::TAG_DHW1) {
snprintf(entity_with_tag, sizeof(entity_with_tag), "ww%s", entity);
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag);
if (strcmp(entity, "nrgdhw") == 0) { // special case for tp1de #1714
snprintf(uniq_id, sizeof(uniq_id), "%s_boiler_nrgww", mqtt_basename_.c_str());
for (uint8_t i = 0; i < num_dhw_old; i++) {
if (strcmp(entity, dhw_old[i]) == 0) { // special case for tp1de #1714
snprintf(entity_with_tag, sizeof(entity_with_tag), "%sww", dhw_old[i]);
}
}
snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag);
} else if (has_tag && device_type == EMSdevice::DeviceType::WATER && tag >= DeviceValue::DeviceValueTAG::TAG_DHW3) {
snprintf(entity_with_tag, sizeof(entity_with_tag), "wwc%d_%s", tag - DeviceValue::DeviceValueTAG::TAG_DHW1 + 1, entity);
snprintf(uniq_id, sizeof(uniq_id), "%s_solar_%s", mqtt_basename_.c_str(), entity_with_tag);
@@ -895,6 +902,11 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
&& tag == DeviceValue::DeviceValueTAG::TAG_DHW1) {
snprintf(entity_with_tag, sizeof(entity_with_tag), "ww%s", entity);
snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, Helpers::toLower(uniq_s).c_str());
for (uint8_t i = 0; i < num_dhw_old; i++) {
if (strcmp(entity, dhw_old[i]) == 0) { // special case for tp1de #1714
snprintf(entity_with_tag, sizeof(entity_with_tag), "%sww", dhw_old[i]);
}
}
} else if (has_tag && device_type == EMSdevice::DeviceType::WATER && tag >= DeviceValue::DeviceValueTAG::TAG_DHW3) {
snprintf(entity_with_tag, sizeof(entity_with_tag), "wwc%d_%s", tag - DeviceValue::DeviceValueTAG::TAG_DHW1 + 1, entity);
snprintf(uniq_id, sizeof(uniq_id), "solar_wwc%d_%s", tag - DeviceValue::DeviceValueTAG::TAG_DHW1 + 1, Helpers::toLower(uniq_s).c_str());
@@ -1217,8 +1229,8 @@ void Mqtt::add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, con
}
}
bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint32_t max) {
uint8_t hc_num = tag - DeviceValueTAG::TAG_HC1 + 1;
bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove, const int16_t min, const uint32_t max) {
uint8_t hc_num = tag;
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
char topic_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
@@ -1329,10 +1341,10 @@ bool Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
// based on the device and tag, create the MQTT topic name (without the basename)
// differs based on whether MQTT nested is enabled
// tag = EMSdevice::DeviceValueTAG
std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
std::string Mqtt::tag_to_topic(uint8_t device_type, int8_t tag) {
// the system device is treated differently. The topic is 'heartbeat' and doesn't follow the usual convention
if (device_type == EMSdevice::DeviceType::SYSTEM) {
return EMSdevice::tag_to_mqtt(tag);
return F_(heartbeat);
}
std::string topic = EMSdevice::device_type_2_device_name(device_type);

View File

@@ -79,7 +79,7 @@ class Mqtt {
static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
static bool publish_ha_sensor_config(uint8_t type,
uint8_t tag,
int8_t tag,
const char * const fullname,
const char * const en_name,
const uint8_t device_type,
@@ -95,7 +95,7 @@ class Mqtt {
const JsonObjectConst dev_json);
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
static bool publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
static void show_mqtt(uuid::console::Shell & shell);
@@ -220,7 +220,7 @@ class Mqtt {
mqtt_retain_ = mqtt_retain;
}
static std::string tag_to_topic(uint8_t device_type, uint8_t tag);
static std::string tag_to_topic(uint8_t device_type, int8_t tag);
static void add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, const char * entity = nullptr);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.0-dev.7"
#define EMSESP_APP_VERSION "3.7.0-dev.8"