initial commit with refactored mqtt commands

This commit is contained in:
proddy
2020-08-03 23:14:43 +02:00
parent 56c4e043fe
commit 6154ff38f2
41 changed files with 1208 additions and 1351 deletions

View File

@@ -18,37 +18,15 @@
#include "boiler.h"
MAKE_PSTR_WORD(boiler)
MAKE_PSTR_WORD(wwtemp)
MAKE_PSTR_WORD(flowtemp)
MAKE_PSTR_WORD(wwactive)
MAKE_PSTR_WORD(wwonetime)
MAKE_PSTR_WORD(wwcirculation)
MAKE_PSTR_WORD(comfort)
MAKE_PSTR_WORD(eco)
MAKE_PSTR_WORD(intelligent)
MAKE_PSTR_WORD(hot)
MAKE_PSTR_WORD(maxpower)
MAKE_PSTR_WORD(minpower)
MAKE_PSTR(comfort_mandatory, "<hot | eco | intelligent>")
// shower
MAKE_PSTR_WORD(shower)
MAKE_PSTR_WORD(timer)
MAKE_PSTR_WORD(alert)
MAKE_PSTR(shower_timer_fmt, "Shower Timer is %s")
MAKE_PSTR(shower_alert_fmt, "Shower Alert is %s")
namespace emsesp {
REGISTER_FACTORY(Boiler, EMSdevice::DeviceType::BOILER)
MAKE_PSTR(logger_name, "boiler")
uuid::log::Logger Boiler::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Boiler::logger_{F_(boiler), uuid::log::Facility::CONSOLE};
Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
LOG_DEBUG(F("Registering new Boiler with device ID 0x%02X"), device_id);
LOG_DEBUG(F("Adding new Boiler with device ID 0x%02X"), device_id);
// the telegram handlers...
register_telegram_type(0x10, F("UBAErrorMessage1"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
@@ -68,15 +46,22 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus2, this, _1));
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus, this, _1));
register_telegram_type(0xE9, F("UBADHWStatus"), false, std::bind(&Boiler::process_UBADHWStatus, this, _1));
// MQTT callbacks
register_mqtt_topic("boiler_cmd", std::bind(&Boiler::boiler_cmd, this, _1));
register_mqtt_topic("boiler_cmd_wwactivated", std::bind(&Boiler::boiler_cmd_wwactivated, this, _1));
register_mqtt_topic("boiler_cmd_wwonetime", std::bind(&Boiler::boiler_cmd_wwonetime, this, _1));
register_mqtt_topic("boiler_cmd_wwcirculation", std::bind(&Boiler::boiler_cmd_wwcirculation, this, _1));
register_mqtt_topic("boiler_cmd_wwtemp", std::bind(&Boiler::boiler_cmd_wwtemp, this, _1));
// MQTT commands for boiler_cmd topic
register_mqtt_cmd(F("comfort"), std::bind(&Boiler::set_warmwater_mode, this, _1, _2));
register_mqtt_cmd(F("wwactivated"), std::bind(&Boiler::set_warmwater_activated, this, _1, _2));
register_mqtt_cmd(F("wwtapactivated"), std::bind(&Boiler::set_tapwarmwater_activated, this, _1, _2));
register_mqtt_cmd(F("wwonetime"), std::bind(&Boiler::set_warmwater_onetime, this, _1, _2));
register_mqtt_cmd(F("wwcirculation"), std::bind(&Boiler::set_warmwater_circulation, this, _1, _2));
register_mqtt_cmd(F("flowtemp"), std::bind(&Boiler::set_flow_temp, this, _1, _2));
register_mqtt_cmd(F("wwtemp"), std::bind(&Boiler::set_warmwater_temp, this, _1, _2));
register_mqtt_cmd(F("burnmaxpower"), std::bind(&Boiler::set_max_power, this, _1, _2));
register_mqtt_cmd(F("burnminpower"), std::bind(&Boiler::set_min_power, this, _1, _2));
register_mqtt_cmd(F("boilhyston"), std::bind(&Boiler::set_hyst_on, this, _1, _2));
register_mqtt_cmd(F("boilhystoff"), std::bind(&Boiler::set_hyst_off, this, _1, _2));
register_mqtt_cmd(F("burnperiod"), std::bind(&Boiler::set_burn_period, this, _1, _2));
register_mqtt_cmd(F("pumpdelay"), std::bind(&Boiler::set_pump_delay, this, _1, _2));
}
// add submenu context
@@ -86,156 +71,10 @@ void Boiler::add_context_menu() {
flash_string_vector{F_(boiler)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
Boiler::console_commands(shell, ShellContext::BOILER);
add_context_commands(ShellContext::BOILER);
});
}
// boiler_cmd topic
void Boiler::boiler_cmd(const char * message) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
DeserializationError error = deserializeJson(doc, message);
if (error) {
LOG_DEBUG(F("MQTT error: payload %s, error %s"), message, error.c_str());
return;
}
if (nullptr != doc["flowtemp"]) {
uint8_t t = doc["flowtemp"];
set_flow_temp(t);
}
if (nullptr != doc["wwtemp"]) {
uint8_t t = doc["wwtemp"];
set_warmwater_temp(t);
}
if (nullptr != doc["boilhyston"]) {
int8_t t = doc["boilhyston"];
set_hyst_on(t);
}
if (nullptr != doc["boilhystoff"]) {
uint8_t t = doc["boilhystoff"];
set_hyst_off(t);
}
if (nullptr != doc["burnperiod"]) {
uint8_t t = doc["burnperiod"];
set_burn_period(t);
}
if (nullptr != doc["burnminpower"]) {
uint8_t p = doc["burnminpower"];
set_min_power(p);
}
if (nullptr != doc["burnmaxpower"]) {
uint8_t p = doc["burnmaxpower"];
set_max_power(p);
}
if (nullptr != doc["pumpdelay"]) {
uint8_t t = doc["pumpdelay"];
set_pump_delay(t);
}
if (nullptr != doc["comfort"]) {
const char * data = doc["comfort"];
if (strcmp((char *)data, "hot") == 0) {
set_warmwater_mode(1);
} else if (strcmp((char *)data, "eco") == 0) {
set_warmwater_mode(2);
} else if (strcmp((char *)data, "intelligent") == 0) {
set_warmwater_mode(3);
}
}
const char * command = doc["cmd"];
if (command == nullptr || doc["data"] == nullptr) {
return;
}
// boiler ww comfort setting
if (strcmp(command, "comfort") == 0) {
const char * data = doc["data"];
if (strcmp((char *)data, "hot") == 0) {
set_warmwater_mode(1);
} else if (strcmp((char *)data, "eco") == 0) {
set_warmwater_mode(2);
} else if (strcmp((char *)data, "intelligent") == 0) {
set_warmwater_mode(3);
}
return;
}
// boiler flowtemp setting
if (strcmp(command, "flowtemp") == 0) {
uint8_t t = doc["data"];
set_flow_temp(t);
return;
}
if (strcmp(command, "wwtemp") == 0) {
uint8_t t = doc["data"];
set_warmwater_temp(t);
return;
}
// boiler max power setting
if (strcmp(command, "burnmaxpower") == 0) {
uint8_t p = doc["data"];
set_max_power(p);
return;
}
// boiler min power setting
if (strcmp(command, "burnminpower") == 0) {
uint8_t p = doc["data"];
set_min_power(p);
return;
}
if (strcmp(command, "boilhyston") == 0) {
int8_t t = doc["data"];
set_hyst_on(t);
return;
}
if (strcmp(command, "boilhystoff") == 0) {
uint8_t t = doc["data"];
set_hyst_off(t);
return;
}
if (strcmp(command, "burnperiod") == 0) {
uint8_t t = doc["data"];
set_burn_period(t);
return;
}
if (strcmp(command, "pumpdelay") == 0) {
uint8_t t = doc["data"];
set_pump_delay(t);
return;
}
}
void Boiler::boiler_cmd_wwactivated(const char * message) {
if ((message[0] == '1' || strcmp(message, "on") == 0) || (strcmp(message, "auto") == 0)) {
set_warmwater_activated(true);
} else if (message[0] == '0' || strcmp(message, "off") == 0) {
set_warmwater_activated(false);
}
}
void Boiler::boiler_cmd_wwonetime(const char * message) {
if (message[0] == '1' || strcmp(message, "on") == 0) {
set_warmwater_onetime(true);
} else if (message[0] == '0' || strcmp(message, "off") == 0) {
set_warmwater_onetime(false);
}
}
void Boiler::boiler_cmd_wwcirculation(const char * message) {
if (message[0] == '1' || strcmp(message, "on") == 0) {
set_warmwater_circulation(true);
} else if (message[0] == '0' || strcmp(message, "off") == 0) {
set_warmwater_circulation(false);
}
}
void Boiler::boiler_cmd_wwtemp(const char * message) {
uint8_t t = atoi((char *)message);
if (t) {
set_warmwater_temp(t);
}
}
void Boiler::device_info(JsonArray & root) {
JsonObject dataElement;
@@ -295,16 +134,16 @@ void Boiler::publish_values() {
if (Helpers::hasValue(pumpMod2_)) {
doc["pumpMod2"] = pumpMod2_;
}
if (Helpers::hasValue(wWCircPump_, VALUE_BOOL)) {
if (Helpers::hasValue(wWCircPump_, EMS_VALUE_BOOL)) {
doc["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWCircPumpType_, VALUE_BOOL)) {
if (Helpers::hasValue(wWCircPumpType_, EMS_VALUE_BOOL)) {
doc["wWCiPuType"] = wWCircPumpType_ ? "valve" : "pump";
}
if (Helpers::hasValue(wWCircPumpMode_)) {
doc["wWCiPuMode"] = wWCircPumpMode_;
}
if (Helpers::hasValue(wWCirc_, VALUE_BOOL)) {
if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(extTemp_)) {
@@ -340,43 +179,43 @@ void Boiler::publish_values() {
if (Helpers::hasValue(exhaustTemp_)) {
doc["exhaustTemp"] = (float)exhaustTemp_ / 10;
}
if (Helpers::hasValue(wWActivated_, VALUE_BOOL)) {
if (Helpers::hasValue(wWActivated_, EMS_VALUE_BOOL)) {
doc["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWOneTime_, VALUE_BOOL)) {
if (Helpers::hasValue(wWOneTime_, EMS_VALUE_BOOL)) {
doc["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWDesinfecting_, VALUE_BOOL)) {
if (Helpers::hasValue(wWDesinfecting_, EMS_VALUE_BOOL)) {
doc["wWDesinfecting"] = Helpers::render_value(s, wWDesinfecting_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWReadiness_, VALUE_BOOL)) {
if (Helpers::hasValue(wWReadiness_, EMS_VALUE_BOOL)) {
doc["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWRecharging_, VALUE_BOOL)) {
if (Helpers::hasValue(wWRecharging_, EMS_VALUE_BOOL)) {
doc["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWTemperatureOK_, VALUE_BOOL)) {
if (Helpers::hasValue(wWTemperatureOK_, EMS_VALUE_BOOL)) {
doc["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWCirc_, VALUE_BOOL)) {
if (Helpers::hasValue(wWCirc_, EMS_VALUE_BOOL)) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(burnGas_, VALUE_BOOL)) {
if (Helpers::hasValue(burnGas_, EMS_VALUE_BOOL)) {
doc["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(flameCurr_)) {
doc["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
}
if (Helpers::hasValue(heatPmp_, VALUE_BOOL)) {
if (Helpers::hasValue(heatPmp_, EMS_VALUE_BOOL)) {
doc["heatPump"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(fanWork_, VALUE_BOOL)) {
if (Helpers::hasValue(fanWork_, EMS_VALUE_BOOL)) {
doc["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(ignWork_, VALUE_BOOL)) {
if (Helpers::hasValue(ignWork_, EMS_VALUE_BOOL)) {
doc["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWHeat_, VALUE_BOOL)) {
if (Helpers::hasValue(wWHeat_, EMS_VALUE_BOOL)) {
doc["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(heating_temp_)) {
@@ -450,16 +289,16 @@ bool Boiler::updated_values() {
void Boiler::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // for showing the header
if (Helpers::hasValue(tap_water_active_, VALUE_BOOL)) {
if (Helpers::hasValue(tap_water_active_, EMS_VALUE_BOOL)) {
print_value(shell, 2, F("Hot tap water"), tap_water_active_ ? F("running") : F("off"));
}
if (Helpers::hasValue(heating_active_, VALUE_BOOL)) {
if (Helpers::hasValue(heating_active_, EMS_VALUE_BOOL)) {
print_value(shell, 2, F("Central heating"), heating_active_ ? F("active") : F("off"));
}
print_value(shell, 2, F("Warm Water activated"), wWActivated_, nullptr, EMS_VALUE_BOOL);
if (Helpers::hasValue(wWCircPumpType_, VALUE_BOOL)) {
if (Helpers::hasValue(wWCircPumpType_, EMS_VALUE_BOOL)) {
print_value(shell, 2, F("Warm Water charging type"), wWCircPumpType_ ? F("3-way valve") : F("charge pump"));
}
print_value(shell, 2, F("Warm Water circulation pump available"), wWCircPump_, nullptr, EMS_VALUE_BOOL);
@@ -557,6 +396,8 @@ void Boiler::show_values(uuid::console::Shell & shell) {
if (Helpers::hasValue(UBAuptime_)) {
shell.printfln(F(" Total UBA working time: %d days %d hours %d minutes"), UBAuptime_ / 1440, (UBAuptime_ % 1440) / 60, UBAuptime_ % 60);
}
shell.println();
}
/*
@@ -579,7 +420,7 @@ void Boiler::check_active() {
// see if the heating or hot tap water has changed, if so send
// last_boilerActive stores heating in bit 1 and tap water in bit 2
if (Helpers::hasValue(tap_water_active_, VALUE_BOOL) && Helpers::hasValue(heating_active_, VALUE_BOOL)) {
if (Helpers::hasValue(tap_water_active_, EMS_VALUE_BOOL) && Helpers::hasValue(heating_active_, EMS_VALUE_BOOL)) {
uint8_t latest_boilerState = (tap_water_active_ << 1) + heating_active_;
if (latest_boilerState != last_boilerState) {
last_boilerState = latest_boilerState;
@@ -650,12 +491,12 @@ void Boiler::process_UBATotalUptime(std::shared_ptr<const Telegram> telegram) {
*/
void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(heating_temp_, 1);
telegram->read_value(burnPowermax_,2);
telegram->read_value(burnPowermin_,3);
telegram->read_value(boilTemp_off_,4);
telegram->read_value(boilTemp_on_,5);
telegram->read_value(burnPeriod_,6);
telegram->read_value(pumpDelay_,8);
telegram->read_value(burnPowermax_, 2);
telegram->read_value(burnPowermin_, 3);
telegram->read_value(boilTemp_off_, 4);
telegram->read_value(boilTemp_on_, 5);
telegram->read_value(burnPeriod_, 6);
telegram->read_value(pumpDelay_, 8);
telegram->read_value(pump_mod_max_, 9);
telegram->read_value(pump_mod_min_, 10);
}
@@ -804,7 +645,7 @@ void Boiler::process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegr
// 0x10, 0x11, 0x12
// not yet implemented
void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
// data: displaycode(2), errornumner(2), year, month, hour, day, minute, duration(2), src-addr
// data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr
}
#pragma GCC diagnostic pop
@@ -818,67 +659,113 @@ void Boiler::process_UBAMaintenanceData(std::shared_ptr<const Telegram> telegram
}
}
/*
* Commands
*/
// Set the warm water temperature 0x33
void Boiler::set_warmwater_temp(const uint8_t temperature) {
LOG_INFO(F("Setting boiler warm water temperature to %d C"), temperature);
write_command(EMS_TYPE_UBAParameterWW, 2, temperature);
write_command(EMS_TYPE_UBAFlags, 3, temperature); // for i9000, see #397
void Boiler::set_warmwater_temp(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler warm water temperature to %d C"), v);
write_command(EMS_TYPE_UBAParameterWW, 2, v);
write_command(EMS_TYPE_UBAFlags, 3, v); // for i9000, see #397
}
// flow temp
void Boiler::set_flow_temp(const uint8_t temperature) {
LOG_INFO(F("Setting boiler flow temperature to %d C"), temperature);
write_command(EMS_TYPE_UBASetPoints, 0, temperature);
void Boiler::set_flow_temp(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler flow temperature to %d C"), v);
write_command(EMS_TYPE_UBASetPoints, 0, v);
}
// set min boiler output
void Boiler::set_min_power(const uint8_t power) {
LOG_INFO(F("Setting boiler min power to "), power);
write_command(EMS_TYPE_UBAParameters, 3, power);
void Boiler::set_min_power(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler min power to "), v);
write_command(EMS_TYPE_UBAParameters, 3, v);
}
// set max temp
void Boiler::set_max_power(const uint8_t power) {
LOG_INFO(F("Setting boiler max power to %d C"), power);
write_command(EMS_TYPE_UBAParameters, 2, power);
void Boiler::set_max_power(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler max power to %d C"), v);
write_command(EMS_TYPE_UBAParameters, 2, v);
}
// set oiler on hysteresis
void Boiler::set_hyst_on(const uint8_t temp) {
LOG_INFO(F("Setting boiler hysteresis on to %d C"), temp);
write_command(EMS_TYPE_UBAParameters, 5, temp);
void Boiler::set_hyst_on(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler hysteresis on to %d C"), v);
write_command(EMS_TYPE_UBAParameters, 5, v);
}
// set boiler off hysteresis
void Boiler::set_hyst_off(const uint8_t temp) {
LOG_INFO(F("Setting boiler hysteresis off to %d C"), temp);
write_command(EMS_TYPE_UBAParameters, 4, temp);
void Boiler::set_hyst_off(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler hysteresis off to %d C"), v);
write_command(EMS_TYPE_UBAParameters, 4, v);
}
// set min burner period
void Boiler::set_burn_period(const uint8_t t) {
LOG_INFO(F("Setting burner min. period to %d min"), t);
write_command(EMS_TYPE_UBAParameters, 6, t);
void Boiler::set_burn_period(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting burner min. period to %d min"), v);
write_command(EMS_TYPE_UBAParameters, 6, v);
}
// set pump delay
void Boiler::set_pump_delay(const uint8_t t) {
LOG_INFO(F("Setting boiler pump delay to %d min"), t);
write_command(EMS_TYPE_UBAParameters, 8, t);
void Boiler::set_pump_delay(const char * value, const int8_t id) {
uint8_t v = 0;
if (!Helpers::value2number(value, v)) {
return;
}
LOG_INFO(F("Setting boiler pump delay to %d min"), v);
write_command(EMS_TYPE_UBAParameters, 8, v);
}
// 1=hot, 2=eco, 3=intelligent
// note some boilers do not have this setting, than it's done by thermostat
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3)
void Boiler::set_warmwater_mode(const uint8_t comfort) {
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3) 1=hot, 2=eco, 3=intelligent
void Boiler::set_warmwater_mode(const char * value, const int8_t id) {
if (value == nullptr) {
return;
}
uint8_t set;
if (comfort == 1) {
if (strcmp(value, "hot") == 0) {
LOG_INFO(F("Setting boiler warm water to Hot"));
set = 0x00;
} else if (comfort == 2) {
} else if (strcmp(value, "eco") == 0) {
LOG_INFO(F("Setting boiler warm water to Eco"));
set = 0xD8;
} else if (comfort == 3) {
} else if (strcmp(value, "intelligent") == 0) {
LOG_INFO(F("Setting boiler warm water to Intelligent"));
set = 0xEC;
} else {
@@ -888,24 +775,33 @@ void Boiler::set_warmwater_mode(const uint8_t comfort) {
}
// turn on/off warm water
void Boiler::set_warmwater_activated(const bool activated) {
LOG_INFO(F("Setting boiler warm water %s"), activated ? "on" : "off");
uint8_t value;
void Boiler::set_warmwater_activated(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
return;
}
LOG_INFO(F("Setting boiler warm water %s"), v ? "on" : "off");
// https://github.com/proddy/EMS-ESP/issues/268
uint8_t n;
if (EMSbus::is_ht3()) {
value = (activated ? 0x08 : 0x00); // 0x08 is on, 0x00 is off
n = (v ? 0x08 : 0x00); // 0x08 is on, 0x00 is off
} else {
value = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
n = (v ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
}
write_command(EMS_TYPE_UBAParameterWW, 1, value);
write_command(EMS_TYPE_UBAParameterWW, 1, n);
}
// Activate / De-activate the Warm Tap Water
// true = on, false = off
// Note: Using the type 0x1D to put the boiler into Test mode. This may be shown on the boiler with a flashing 'T'
void Boiler::set_tapwarmwater_activated(const bool activated) {
LOG_INFO(F("Setting boiler warm tap water %s"), activated ? "on" : "off");
void Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
return;
}
LOG_INFO(F("Setting tap warm tap water %s"), v ? "on" : "off");
uint8_t message_data[EMS_MAX_TELEGRAM_MESSAGE_LENGTH];
for (uint8_t i = 0; i < sizeof(message_data); i++) {
message_data[i] = 0x00;
@@ -914,7 +810,7 @@ void Boiler::set_tapwarmwater_activated(const bool activated) {
// we use the special test mode 0x1D for this. Setting the first data to 5A puts the system into test mode and
// a setting of 0x00 puts it back into normal operating mode
// when in test mode we're able to mess around with the 3-way valve settings
if (!activated) {
if (!v) {
// on
message_data[0] = 0x5A; // test mode on
message_data[1] = 0x00; // burner output 0%
@@ -932,16 +828,26 @@ void Boiler::set_tapwarmwater_activated(const bool activated) {
// Activate / De-activate One Time warm water 0x35
// true = on, false = off
// See also https://github.com/proddy/EMS-ESP/issues/341#issuecomment-596245458 for Junkers
void Boiler::set_warmwater_onetime(const bool activated) {
LOG_INFO(F("Setting boiler warm water OneTime loading %s"), activated ? "on" : "off");
write_command(EMS_TYPE_UBAFlags, 0, (activated ? 0x22 : 0x02));
void Boiler::set_warmwater_onetime(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
return;
}
LOG_INFO(F("Setting boiler warm water OneTime loading %s"), v ? "on" : "off");
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x22 : 0x02));
}
// Activate / De-activate circulation of warm water 0x35
// true = on, false = off
void Boiler::set_warmwater_circulation(const bool activated) {
LOG_INFO(F("Setting boiler warm water circulation %s"), activated ? "on" : "off");
write_command(EMS_TYPE_UBAFlags, 1, (activated ? 0x22 : 0x02));
void Boiler::set_warmwater_circulation(const char * value, const int8_t id) {
bool v = false;
if (!Helpers::value2bool(value, v)) {
return;
}
LOG_INFO(F("Setting boiler warm water circulation %s"), v ? "on" : "off");
write_command(EMS_TYPE_UBAFlags, 1, (v ? 0x22 : 0x02));
}
// add console commands
@@ -955,115 +861,6 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
EMSESP::send_read_request(type_id, device_id());
});
EMSESPShell::commands->add_command(ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(wwtemp)},
flash_string_vector{F_(degrees_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
set_warmwater_temp(Helpers::atoint(arguments.front().c_str()));
});
EMSESPShell::commands->add_command(ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(flowtemp)},
flash_string_vector{F_(degrees_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
set_flow_temp(Helpers::atoint(arguments.front().c_str()));
});
EMSESPShell::commands->add_command(ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(maxpower)},
flash_string_vector{F_(n_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
set_max_power(Helpers::atoint(arguments.front().c_str()));
});
EMSESPShell::commands->add_command(ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(minpower)},
flash_string_vector{F_(n_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
set_min_power(Helpers::atoint(arguments.front().c_str()));
});
EMSESPShell::commands->add_command(
ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(wwactive)},
flash_string_vector{F_(bool_mandatory)},
[=](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments[0] == read_flash_string(F_(on))) {
set_warmwater_activated(true);
} else if (arguments[0] == read_flash_string(F_(off))) {
set_warmwater_activated(false);
} else {
shell.println(F("Must be on or off"));
return;
}
},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
});
EMSESPShell::commands->add_command(
ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(wwonetime)},
flash_string_vector{F_(bool_mandatory)},
[=](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments[0] == read_flash_string(F_(on))) {
set_warmwater_onetime(true);
} else if (arguments[0] == read_flash_string(F_(off))) {
set_warmwater_onetime(false);
} else {
shell.println(F("Must be on or off"));
return;
}
},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
});
EMSESPShell::commands->add_command(
ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(wwcirculation)},
flash_string_vector{F_(bool_mandatory)},
[=](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments[0] == read_flash_string(F_(on))) {
set_warmwater_circulation(true);
} else if (arguments[0] == read_flash_string(F_(off))) {
set_warmwater_circulation(false);
} else {
shell.println(F("Must be on or off"));
return;
}
},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F_(on)), read_flash_string(F_(off))};
});
EMSESPShell::commands->add_command(
ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(comfort)},
flash_string_vector{F_(comfort_mandatory)},
[=](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments[0] == read_flash_string(F_(hot))) {
set_warmwater_mode(1);
} else if (arguments[0] == read_flash_string(F_(eco))) {
set_warmwater_mode(2);
} else if (arguments[0] == read_flash_string(F_(intelligent))) {
set_warmwater_mode(3);
} else {
shell.println(F("Invalid value. Must be hot, eco or intelligent"));
}
},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F_(hot)), read_flash_string(F_(eco)), read_flash_string(F_(intelligent))};
});
EMSESPShell::commands->add_command(ShellContext::BOILER,
CommandFlags::USER,
flash_string_vector{F_(show)},

View File

@@ -124,7 +124,7 @@ class Boiler : public EMSdevice {
uint8_t burnPowermin_ = EMS_VALUE_UINT_NOTSET;
uint8_t burnPowermax_ = EMS_VALUE_UINT_NOTSET;
int8_t boilTemp_off_ = EMS_VALUE_INT_NOTSET;
int8_t boilTemp_on_ = EMS_VALUE_UINT_NOTSET;
int8_t boilTemp_on_ = EMS_VALUE_INT_NOTSET;
uint8_t burnPeriod_ = EMS_VALUE_UINT_NOTSET;
uint8_t pumpDelay_ = EMS_VALUE_UINT_NOTSET;
@@ -160,27 +160,20 @@ class Boiler : public EMSdevice {
void check_active();
void set_warmwater_temp(const uint8_t temperature);
void set_flow_temp(const uint8_t temperature);
void set_warmwater_mode(const uint8_t comfort);
void set_warmwater_activated(const bool activated);
void set_tapwarmwater_activated(const bool activated);
void set_warmwater_onetime(const bool activated);
void set_warmwater_circulation(const bool activated);
void set_min_power(const uint8_t power);
void set_max_power(const uint8_t power);
void set_hyst_on(const uint8_t temp);
void set_hyst_off(const uint8_t temp);
void set_burn_period(const uint8_t t);
void set_pump_delay(const uint8_t t);
// mqtt callbacks
void boiler_cmd(const char * message);
void boiler_cmd_wwactivated(const char * message);
void boiler_cmd_wwonetime(const char * message);
void boiler_cmd_wwcirculation(const char * message);
void boiler_cmd_wwtemp(const char * message);
// commands
void set_warmwater_mode(const char * value, const int8_t id);
void set_warmwater_activated(const char * value, const int8_t id);
void set_tapwarmwater_activated(const char * value, const int8_t id);
void set_warmwater_onetime(const char * value, const int8_t id);
void set_warmwater_circulation(const char * value, const int8_t id);
void set_warmwater_temp(const char * value, const int8_t id);
void set_flow_temp(const char * value, const int8_t id);
void set_min_power(const char * value, const int8_t id);
void set_max_power(const char * value, const int8_t id);
void set_hyst_on(const char * value, const int8_t id);
void set_hyst_off(const char * value, const int8_t id);
void set_burn_period(const char * value, const int8_t id);
void set_pump_delay(const char * value, const int8_t id);
};
} // namespace emsesp

View File

@@ -22,16 +22,10 @@ namespace emsesp {
REGISTER_FACTORY(Connect, EMSdevice::DeviceType::CONNECT);
MAKE_PSTR(logger_name, "connect")
uuid::log::Logger Connect::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Connect::logger_{F_(connect), uuid::log::Facility::CONSOLE};
Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// telegram handlers
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Connect::cmd, this, _1));
}
void Connect::device_info(JsonArray & root) {
@@ -42,7 +36,7 @@ void Connect::add_context_menu() {
// display all values into the shell console
void Connect::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
// EMSdevice::show_values(shell); // always call this to show header
}
// publish values via MQTT

View File

@@ -18,22 +18,14 @@
#include "controller.h"
// MAKE_PSTR_WORD(controller)
namespace emsesp {
REGISTER_FACTORY(Controller, EMSdevice::DeviceType::CONTROLLER);
MAKE_PSTR(logger_name, "controller")
uuid::log::Logger Controller::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Controller::logger_{F_(controller), uuid::log::Facility::CONSOLE};
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// telegram handlers
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Controller::cmd, this, _1));
}
void Controller::add_context_menu() {
@@ -44,7 +36,7 @@ void Controller::device_info(JsonArray & root) {
// display all values into the shell console
void Controller::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
// EMSdevice::show_values(shell); // always call this to show header
}
// publish values via MQTT

View File

@@ -18,22 +18,14 @@
#include "gateway.h"
// MAKE_PSTR_WORD(gateway)
namespace emsesp {
REGISTER_FACTORY(Gateway, EMSdevice::DeviceType::GATEWAY);
MAKE_PSTR(logger_name, "gateway")
uuid::log::Logger Gateway::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Gateway::logger_{F_(gateway), uuid::log::Facility::CONSOLE};
Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// telegram handlers
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Gateway::cmd, this, _1));
}
void Gateway::add_context_menu() {

View File

@@ -18,32 +18,19 @@
#include "heatpump.h"
// MAKE_PSTR_WORD(heatpump)
/*
example telegrams 0x32B, 0x37B
"38 10 FF 00 03 7B 08 24 00 4B",
"38 10 FF 00 03 2B 00 C7 07 C3 01",
"38 10 FF 00 03 2B 00 D1 08 2A 01",
*/
namespace emsesp {
REGISTER_FACTORY(Heatpump, EMSdevice::DeviceType::HEATPUMP);
MAKE_PSTR(logger_name, "heatpump")
uuid::log::Logger Heatpump::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Heatpump::logger_{F_(heatpump), uuid::log::Facility::CONSOLE};
Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
LOG_DEBUG(F("Registering new Heat Pump module with device ID 0x%02X"), device_id);
LOG_DEBUG(F("Adding new Heat Pump module with device ID 0x%02X"), device_id);
// telegram handlers
register_telegram_type(0x047B, F("HP1"), true, std::bind(&Heatpump::process_HPMonitor1, this, _1));
register_telegram_type(0x042B, F("HP2"), true, std::bind(&Heatpump::process_HPMonitor2, this, _1));
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Heatpump::cmd, this, _1));
}
// context submenu
@@ -55,7 +42,7 @@ void Heatpump::device_info(JsonArray & root) {
// display all values into the shell console
void Heatpump::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
// EMSdevice::show_values(shell); // always call this to show header
}
// publish values via MQTT

View File

@@ -22,12 +22,11 @@ namespace emsesp {
REGISTER_FACTORY(Mixing, EMSdevice::DeviceType::MIXING);
MAKE_PSTR(logger_name, "mixing")
uuid::log::Logger Mixing::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Mixing::logger_{F_(mixing), uuid::log::Facility::CONSOLE};
Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
LOG_DEBUG(F("Registering new Mixing module with device ID 0x%02X"), device_id);
LOG_DEBUG(F("Adding new Mixing module with device ID 0x%02X"), device_id);
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
if (device_id <= 0x27) {
@@ -48,9 +47,6 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
register_telegram_type(0x010C, F("IPMSetMessage"), false, std::bind(&Mixing::process_IPMStatusMessage, this, _1));
}
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Mixing::cmd, this, _1));
}
// add context submenu
@@ -72,7 +68,6 @@ void Mixing::device_info(JsonArray & root) {
render_value_json(root, "", F("Setpoint flow temperature"), flowSetTemp_, F_(degrees));
render_value_json(root, "", F("Current pump modulation"), pumpMod_, F_(percent));
render_value_json(root, "", F("Current valve status"), status_, nullptr);
}
// check to see if values have been updated
@@ -101,6 +96,8 @@ void Mixing::show_values(uuid::console::Shell & shell) {
print_value(shell, 4, F("Setpoint flow temperature"), flowSetTemp_, F_(degrees));
print_value(shell, 4, F("Current pump modulation"), pumpMod_, F_(percent));
print_value(shell, 4, F("Current valve status"), status_, nullptr);
shell.println();
}
// publish values via MQTT

View File

@@ -18,19 +18,15 @@
#include "solar.h"
MAKE_PSTR(kwh, "kWh")
MAKE_PSTR(wh, "Wh")
namespace emsesp {
REGISTER_FACTORY(Solar, EMSdevice::DeviceType::SOLAR);
MAKE_PSTR(logger_name, "solar")
uuid::log::Logger Solar::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Solar::logger_{F_(solar), uuid::log::Facility::CONSOLE};
Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
LOG_DEBUG(F("Registering new Solar module with device ID 0x%02X"), device_id);
LOG_DEBUG(F("Adding new Solar module with device ID 0x%02X"), device_id);
// telegram handlers
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
@@ -49,9 +45,6 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, std::bind(&Solar::process_ISM1StatusMessage, this, _1));
register_telegram_type(0x0101, F("ISM1Set"), false, std::bind(&Solar::process_ISM1Set, this, _1));
}
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Solar::cmd, this, _1));
}
// context submenu
@@ -105,6 +98,8 @@ void Solar::show_values(uuid::console::Shell & shell) {
print_value(shell, 2, F("Energy last hour"), energyLastHour_, F_(wh), 10);
print_value(shell, 2, F("Energy today"), energyToday_, F_(wh));
print_value(shell, 2, F("Energy total"), energyTotal_, F_(kwh), 10);
shell.println();
}
// publish values via MQTT
@@ -129,11 +124,11 @@ void Solar::publish_values() {
doc["pumpmodulation"] = pumpModulation_;
}
if (Helpers::hasValue(pump_, VALUE_BOOL)) {
if (Helpers::hasValue(pump_, EMS_VALUE_BOOL)) {
doc["pump"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(valveStatus_, VALUE_BOOL)) {
if (Helpers::hasValue(valveStatus_, EMS_VALUE_BOOL)) {
doc["valvestatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
}
@@ -141,11 +136,11 @@ void Solar::publish_values() {
doc["pumpWorkMin"] = (float)pumpWorkMin_;
}
if (Helpers::hasValue(tankHeated_, VALUE_BOOL)) {
if (Helpers::hasValue(tankHeated_, EMS_VALUE_BOOL)) {
doc["tankHeated"] = Helpers::render_value(s, tankHeated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(collectorOnOff_, VALUE_BOOL)) {
if (Helpers::hasValue(collectorOnOff_, EMS_VALUE_BOOL)) {
doc["collectorOnOff"] = Helpers::render_value(s, collectorOnOff_, EMS_VALUE_BOOL);
}

View File

@@ -18,22 +18,14 @@
#include "switch.h"
// MAKE_PSTR_WORD(switch)
namespace emsesp {
REGISTER_FACTORY(Switch, EMSdevice::DeviceType::SWITCH);
MAKE_PSTR(logger_name, "switch")
uuid::log::Logger Switch::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Switch::logger_{F_(switch), uuid::log::Facility::CONSOLE};
Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
// telegram handlers
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
// MQTT callbacks
// register_mqtt_topic("topic", std::bind(&Switch::cmd, this, _1));
}
void Switch::add_context_menu() {
@@ -44,7 +36,7 @@ void Switch::device_info(JsonArray & root) {
// display all values into the shell console
void Switch::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
// EMSdevice::show_values(shell); // always call this to show header
}
// publish values via MQTT

View File

@@ -18,22 +18,11 @@
#include "thermostat.h"
MAKE_PSTR_WORD(thermostat)
MAKE_PSTR_WORD(master)
MAKE_PSTR_WORD(temp)
MAKE_PSTR_WORD(mode)
MAKE_PSTR_WORD(wwmode)
MAKE_PSTR(hc_optional, "[heating circuit]")
MAKE_PSTR(mode_mandatory, "<mode>")
MAKE_PSTR(mode_optional, "[mode]")
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat device ID = %s")
namespace emsesp {
REGISTER_FACTORY(Thermostat, EMSdevice::DeviceType::THERMOSTAT);
MAKE_PSTR(logger_name, "thermostat")
uuid::log::Logger Thermostat::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
uuid::log::Logger Thermostat::logger_{F_(thermostat), uuid::log::Facility::CONSOLE};
Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
@@ -138,18 +127,17 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
uint8_t actual_master_thermostat = EMSESP::actual_master_thermostat(); // what we're actually using
uint8_t num_devices = EMSESP::count_devices(EMSdevice::DeviceType::THERMOSTAT) + 1; // including this thermostat
// if we're on auto mode, register this thermostat if it has a device id of 0x10 or 0x17
// if we're on auto mode, register this thermostat if it has a device id of 0x10, 0x17 or 0x18
// or if its the master thermostat we defined
// see https://github.com/proddy/EMS-ESP/issues/362#issuecomment-629628161
if (((num_devices == 1) && (actual_master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT)) || (master_thermostat == device_id)) {
EMSESP::actual_master_thermostat(device_id);
LOG_DEBUG(F("Registering new thermostat with device ID 0x%02X (as master)"), device_id);
LOG_DEBUG(F("Adding new thermostat with device ID 0x%02X (as master)"), device_id);
init_mqtt();
} else {
LOG_DEBUG(F("Registering new thermostat with device ID 0x%02X"), device_id);
LOG_DEBUG(F("Adding new thermostat with device ID 0x%02X"), device_id);
}
// for the thermostat, go a query all the heating circuits. This is only done once. The automatic fetch will from now on
// only update the active heating circuits
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
@@ -160,14 +148,6 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
}
}
// for the master thermostat initialize the MQTT subscribes
// these will be prefixed with hostname
void Thermostat::init_mqtt() {
register_mqtt_topic("thermostat_cmd", std::bind(&Thermostat::thermostat_cmd, this, _1)); // generic commands
register_mqtt_topic("thermostat_cmd_temp", std::bind(&Thermostat::thermostat_cmd_temp, this, _1));
register_mqtt_topic("thermostat_cmd_mode", std::bind(&Thermostat::thermostat_cmd_mode, this, _1));
}
// prepare data for Web UI
void Thermostat::device_info(JsonArray & root) {
uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits
@@ -205,14 +185,18 @@ void Thermostat::device_info(JsonArray & root) {
dataElement = root.createNestedObject();
std::string mode_str(15, '\0');
snprintf_P(&mode_str[0], mode_str.capacity() + 1, PSTR("%sMode"), hc_str.c_str());
dataElement["name"] = mode_str;
dataElement["name"] = mode_str;
std::string modetype_str(20, '\0');
if (Helpers::hasValue(hc->summer_mode) && hc->summer_mode) {
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - summer"), mode_tostring(hc->get_mode(flags)).c_str());
} else if (Helpers::hasValue(hc->holiday_mode) && hc->holiday_mode) {
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - holiday"), mode_tostring(hc->get_mode(flags)).c_str());
} else if (Helpers::hasValue(hc->mode_type)) {
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - %s"), mode_tostring(hc->get_mode(flags)).c_str(), mode_tostring(hc->get_mode_type(flags)).c_str());
snprintf_P(&modetype_str[0],
modetype_str.capacity() + 1,
PSTR("%s - %s"),
mode_tostring(hc->get_mode(flags)).c_str(),
mode_tostring(hc->get_mode_type(flags)).c_str());
} else {
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, mode_tostring(hc->get_mode(flags)).c_str());
}
@@ -221,8 +205,9 @@ void Thermostat::device_info(JsonArray & root) {
}
}
// only add the menu for the master thermostat
// context menu "thermostat"
void Thermostat::add_context_menu() {
// only add it once, to prevent conflicts when there are multiple thermostats
if (device_id() != EMSESP::actual_master_thermostat()) {
return;
}
@@ -232,318 +217,10 @@ void Thermostat::add_context_menu() {
flash_string_vector{F_(thermostat)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
Thermostat::console_commands(shell, ShellContext::THERMOSTAT);
add_context_commands(ShellContext::THERMOSTAT);
});
}
// general MQTT command for controlling thermostat
// e.g. { "hc": 1, "cmd":"daytemp", "data": 20 }
// or { "hc": 1, "daytemp": 20 } or { "hc2": { "daytemp":20 }}
void Thermostat::thermostat_cmd(const char * message) {
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
DeserializationError error = deserializeJson(doc, message);
if (error) {
LOG_DEBUG(F("MQTT error: payload %s, error %s"), message, error.c_str());
return;
}
// check for nested commands like {"hc2":{"temp":21}}
for (const auto & hc : heating_circuits_) {
char hc_name[6], s[3]; // hc{1-4}
strlcpy(hc_name, "hc", 6);
uint8_t hc_num = hc->hc_num();
strlcat(hc_name, Helpers::itoa(s, hc_num), 6);
if (nullptr != doc[hc_name]["mode"]) {
std::string mode = doc[hc_name]["mode"];
set_mode(mode, hc_num);
}
if (float f = doc[hc_name]["temp"]) {
set_temperature(f, HeatingCircuit::Mode::AUTO, hc_num);
}
if (float f = doc[hc_name]["nighttemp"]) {
set_temperature(f, HeatingCircuit::Mode::NIGHT, hc_num);
}
if (float f = doc[hc_name]["daytemp"]) {
set_temperature(f, HeatingCircuit::Mode::DAY, hc_num);
}
if (float f = doc[hc_name]["nofrosttemp"]) {
set_temperature(f, HeatingCircuit::Mode::NOFROST, hc_num);
}
if (float f = doc[hc_name]["ecotemp"]) {
set_temperature(f, HeatingCircuit::Mode::ECO, hc_num);
}
if (float f = doc[hc_name]["heattemp"]) {
set_temperature(f, HeatingCircuit::Mode::HEAT, hc_num);
}
if (float f = doc[hc_name]["summertemp"]) {
set_temperature(f, HeatingCircuit::Mode::SUMMER, hc_num);
}
if (float f = doc[hc_name]["designtemp"]) {
set_temperature(f, HeatingCircuit::Mode::DESIGN, hc_num);
}
if (float f = doc[hc_name]["offsettemp"]) {
set_temperature(f, HeatingCircuit::Mode::OFFSET, hc_num);
}
if (float f = doc[hc_name]["holidaytemp"]) { //
set_temperature(f, HeatingCircuit::Mode::HOLIDAY, hc_num);
}
if (float f = doc[hc_name]["remotetemp"]) {
if (f > 100 || f < 0) {
Roomctrl::set_remotetemp(hc_num - 1, EMS_VALUE_SHORT_NOTSET);
} else {
Roomctrl::set_remotetemp(hc_num - 1, (int16_t)(f * 10));
}
}
if (nullptr != doc[hc_name]["control"]) {
uint8_t ctrl = doc[hc_name]["control"];
set_control(ctrl, hc_num);
}
if (nullptr != doc[hc_name]["pause"]) {
uint8_t p = doc[hc_name]["pause"];
set_pause(p, hc_num);
}
if (nullptr != doc[hc_name]["party"]) {
uint8_t p = doc[hc_name]["party"];
set_party(p, hc_num);
}
if (nullptr != doc[hc_name]["holiday"]) {
std::string holiday = doc[hc_name]["holiday"];
set_holiday(holiday.c_str(), hc_num);
}
}
// commands without heatingcircuit
if (nullptr != doc["wwmode"]) {
std::string mode = doc["wwmode"];
set_ww_mode(mode);
}
if (float ct = doc["calinttemp"]) {
set_settings_calinttemp((int8_t)(ct * 10));
}
if (nullptr != doc["minexttemp"]) {
int8_t mt = doc["minexttemp"];
set_settings_minexttemp(mt);
}
if (nullptr != doc["building"]) {
std::string bds = doc["building"];
uint8_t bd = doc["building"];
if (strcmp(bds.c_str(), "light") == 0) {
bd = 0;
} else if (strcmp(bds.c_str(), "medium") == 0) {
bd = 1;
} else if (strcmp(bds.c_str(), "heavy") == 0) {
bd = 2;
}
set_settings_building(bd);
}
if (nullptr != doc["language"]) {
uint8_t lg = doc["language"];
set_settings_language(lg);
}
if (nullptr != doc["display"]) {
uint8_t dp = doc["display"];
set_settings_display(dp);
}
if (nullptr != doc["clockoffset"]) {
int8_t co = doc["clockoffset"];
set_settings_clockoffset(co);
}
// get heating circuit if it exists
uint8_t hc_num = doc["hc"] | AUTO_HEATING_CIRCUIT;
// check for unnested commands like {"temp":21} or {"hc":2,"temp":21,"mode":"auto"}
if (nullptr != doc["mode"]) {
std::string mode = doc["mode"];
set_mode(mode, hc_num);
}
if (float f = doc["temp"]) {
set_temperature(f, HeatingCircuit::Mode::AUTO, hc_num);
}
if (float f = doc["nighttemp"]) {
set_temperature(f, HeatingCircuit::Mode::NIGHT, hc_num);
}
if (float f = doc["daytemp"]) {
set_temperature(f, HeatingCircuit::Mode::DAY, hc_num);
}
if (float f = doc["nofrosttemp"]) {
set_temperature(f, HeatingCircuit::Mode::NOFROST, hc_num);
}
if (float f = doc["ecotemp"]) {
set_temperature(f, HeatingCircuit::Mode::ECO, hc_num);
}
if (float f = doc["heattemp"]) {
set_temperature(f, HeatingCircuit::Mode::HEAT, hc_num);
}
if (float f = doc["summertemp"]) {
set_temperature(f, HeatingCircuit::Mode::SUMMER, hc_num);
}
if (float f = doc["designtemp"]) {
set_temperature(f, HeatingCircuit::Mode::DESIGN, hc_num);
}
if (float f = doc["offsettemp"]) {
set_temperature(f, HeatingCircuit::Mode::OFFSET, hc_num);
}
if (float f = doc["holidaytemp"]) { //
set_temperature(f, HeatingCircuit::Mode::HOLIDAY, hc_num);
}
if (float f = doc["remotetemp"]) {
if (f > 100 || f < 0) {
Roomctrl::set_remotetemp(hc_num - 1, EMS_VALUE_SHORT_NOTSET);
} else {
Roomctrl::set_remotetemp(hc_num - 1, (int16_t)(f * 10));
}
}
if (nullptr != doc["control"]) {
uint8_t ctrl = doc["control"];
set_control(ctrl, hc_num);
}
if (nullptr != doc["pause"]) {
uint8_t p = doc["pause"];
set_pause(p, hc_num);
}
if (nullptr != doc["party"]) {
uint8_t p = doc["party"];
set_party(p, hc_num);
}
if (nullptr != doc["holiday"]) {
std::string holiday = doc["holiday"];
set_holiday(holiday.c_str(), hc_num);
}
if (nullptr != doc["date"]) {
std::string date = doc["date"];
set_datetime(date.c_str());
}
// check for commands like {"hc":2,"cmd":"temp","data":21}
const char * command = doc["cmd"];
if (command == nullptr || doc["data"] == nullptr) {
return;
}
// ok, we have command and data
if (strcmp(command, "temp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::AUTO, hc_num);
}
return;
}
if (strcmp(command, "mode") == 0) {
std::string mode = doc["data"];
if (mode.empty()) {
return;
}
set_mode(mode, hc_num);
return;
}
if (strcmp(command, "nighttemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::NIGHT, hc_num);
}
return;
}
if (strcmp(command, "daytemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::DAY, hc_num);
}
return;
}
if (strcmp(command, "holidaytemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::HOLIDAY, hc_num);
}
return;
}
if (strcmp(command, "ecotemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::ECO, hc_num);
}
return;
}
if (strcmp(command, "heattemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::HEAT, hc_num);
}
return;
}
if (strcmp(command, "nofrosttemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::NOFROST, hc_num);
}
return;
}
if (strcmp(command, "summertemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::SUMMER, hc_num);
}
return;
}
if (strcmp(command, "designtemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::DESIGN, hc_num);
}
return;
}
if (strcmp(command, "offsettemp") == 0) {
float f = doc["data"];
if (f) {
set_temperature(f, HeatingCircuit::Mode::OFFSET, hc_num);
}
return;
}
if (strcmp(command, "remotetemp") == 0) {
float f = doc["data"];
if (f > 100 || f < 0) {
Roomctrl::set_remotetemp(hc_num - 1, EMS_VALUE_SHORT_NOTSET);
} else {
Roomctrl::set_remotetemp(hc_num - 1, (int16_t)(f * 10));
}
return;
}
if (strcmp(command, "control") == 0) {
uint8_t ctrl = doc["data"];
set_control(ctrl, hc_num);
return;
}
if (strcmp(command, "pause") == 0) {
uint8_t p = doc["data"];
set_pause(p, hc_num);
return;
}
if (strcmp(command, "party") == 0) {
uint8_t p = doc["data"];
set_party(p, hc_num);
return;
}
if (strcmp(command, "holiday") == 0) {
std::string holiday = doc["data"];
set_holiday(holiday.c_str(), hc_num);
return;
}
if (strcmp(command, "date") == 0) {
std::string date = doc["data"];
set_datetime(date.c_str());
return;
}
}
void Thermostat::thermostat_cmd_temp(const char * message) {
float f = strtof((char *)message, 0);
set_temperature(f, HeatingCircuit::Mode::AUTO, AUTO_HEATING_CIRCUIT);
}
// message payload holds the text name of the mode e.g. "auto"
void Thermostat::thermostat_cmd_mode(const char * message) {
std::string s(message);
set_mode(s, AUTO_HEATING_CIRCUIT);
}
// this function is called post the telegram handler function has been executed
// we check if any of the thermostat values have changed and then republish if necessary
bool Thermostat::updated_values() {
@@ -761,12 +438,14 @@ void Thermostat::publish_values() {
// returns the heating circuit object based on the hc number
// of nullptr if it doesn't exist yet
std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const uint8_t hc_num) {
// if hc_num is 0 then return the first existing hc in the list
if (hc_num == 0) {
// return first existing hc
for (const auto & heating_circuit : heating_circuits_) {
return heating_circuit;
}
}
// otherwise find a match
for (const auto & heating_circuit : heating_circuits_) {
if (heating_circuit->hc_num() == hc_num) {
return heating_circuit;
@@ -1161,6 +840,8 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
print_value(shell, 4, F("Target flow temperature"), hc->targetflowtemp, F_(degrees));
}
}
shell.println();
}
// 0xA8 - for reading the mode from the RC20 thermostat (0x17)
@@ -1360,8 +1041,7 @@ void Thermostat::process_RC35Monitor(std::shared_ptr<const Telegram> telegram) {
// type 0x3D (HC1), 0x47 (HC2), 0x51 (HC3), 0x5B (HC4) - Working Mode Heating - for reading the mode from the RC35 thermostat (0x10)
void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
// check to see we have a valid type
// heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
// check to see we have a valid type. heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
if (telegram->message_data[0] == 0x00) {
return;
}
@@ -1389,7 +1069,7 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
return;
}
if (telegram->message_data[7] & 0x0C) { // date and time not valid
set_datetime("NTP"); // set from NTP
set_datetime("ntp", -1); // set from NTP
return;
}
if (datetime_.empty()) {
@@ -1415,8 +1095,69 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
);
}
// add console commands
void Thermostat::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(master)},
flash_string_vector{F_(deviceid_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint8_t value;
if (arguments.empty()) {
value = EMSESP_DEFAULT_MASTER_THERMOSTAT;
} else {
value = Helpers::hextoint(arguments.front().c_str());
}
EMSESP::emsespSettingsService.update(
[&](EMSESPSettings & settings) {
settings.master_thermostat = value;
EMSESP::actual_master_thermostat(value); // set the internal value too
char buffer[5];
shell.printfln(F_(master_thermostat_fmt),
!value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
return StateUpdateResult::CHANGED;
},
"local");
});
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(read)},
flash_string_vector{F_(typeid_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint16_t type_id = Helpers::hextoint(arguments.front().c_str());
EMSESP::send_read_request(type_id, device_id());
});
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::USER,
flash_string_vector{F_(show)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_values(shell); });
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::USER,
flash_string_vector{F_(set)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
char buffer[4];
shell.printfln(F_(master_thermostat_fmt),
settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str()
: Helpers::hextoa(buffer, settings.master_thermostat));
shell.println();
});
});
// enter the context
Console::enter_custom_context(shell, context);
}
// 0xA5 - Set minimum external temperature
void Thermostat::set_settings_minexttemp(const int8_t mt) {
void Thermostat::set_settings_minexttemp(const char * value, const int8_t id) {
int8_t mt = 0;
if (!Helpers::value2number(value, mt)) {
return;
}
if (((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) || ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35)) {
LOG_INFO(F("Setting min external temperature to %d"), mt);
write_command(EMS_TYPE_IBASettings, 5, mt);
@@ -1424,14 +1165,25 @@ void Thermostat::set_settings_minexttemp(const int8_t mt) {
}
// 0xA5 - Clock offset
void Thermostat::set_settings_clockoffset(const int8_t co) {
void Thermostat::set_settings_clockoffset(const char * value, const int8_t id) {
int8_t co = 0;
if (!Helpers::value2number(value, co)) {
return;
}
if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) {
LOG_INFO(F("Setting clock offset to %d"), co);
write_command(EMS_TYPE_IBASettings, 12, co);
}
}
// 0xA5 - Calibrate internal temperature
void Thermostat::set_settings_calinttemp(const int8_t ct) {
void Thermostat::set_settings_calinttemp(const char * value, const int8_t id) {
int8_t ct = 0;
if (!Helpers::value2number(value, ct)) {
return;
}
// TODO: Michael - does this value need to be multiple by 10?
if (((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) || ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35)) {
LOG_INFO(F("Calibrating internal temperature to %d.%d"), ct / 10, ct < 0 ? -ct % 10 : ct % 10);
write_command(EMS_TYPE_IBASettings, 2, ct);
@@ -1439,15 +1191,50 @@ void Thermostat::set_settings_calinttemp(const int8_t ct) {
}
// 0xA5 - Set the display settings
void Thermostat::set_settings_display(const uint8_t ds) {
void Thermostat::set_settings_display(const char * value, const int8_t id) {
uint8_t ds = 0;
if (!Helpers::value2number(value, ds)) {
return;
}
if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) {
LOG_INFO(F("Setting display to %d"), ds);
write_command(EMS_TYPE_IBASettings, 0, ds);
}
}
void Thermostat::set_remotetemp(const char * value, const int8_t id) {
float f = 0;
if (!Helpers::value2float(value, f)) {
return;
}
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
if (f > 100 || f < 0) {
Roomctrl::set_remotetemp(hc_num - 1, EMS_VALUE_SHORT_NOTSET);
} else {
Roomctrl::set_remotetemp(hc_num - 1, (int16_t)(f * 10));
}
}
// 0xA5 - Set the building settings
void Thermostat::set_settings_building(const uint8_t bg) {
void Thermostat::set_settings_building(const char * value, const int8_t id) {
std::string bd;
if (!Helpers::value2string(value, bd)) {
return;
}
uint8_t bg = 0;
if (bd == "light") {
bg = 0;
} else if (bd == "medium") {
bg = 1;
} else if (bd == "heavy") {
bg = 2;
} else {
return; // invalid
}
if (((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) || ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35)) {
LOG_INFO(F("Setting building to %d"), bg);
write_command(EMS_TYPE_IBASettings, 6, bg);
@@ -1455,7 +1242,11 @@ void Thermostat::set_settings_building(const uint8_t bg) {
}
// 0xA5 Set the language settings
void Thermostat::set_settings_language(const uint8_t lg) {
void Thermostat::set_settings_language(const char * value, const int8_t id) {
uint8_t lg = 0;
if (!Helpers::value2number(value, lg)) {
return;
}
if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) {
LOG_INFO(F("Setting language to %d"), lg);
write_command(EMS_TYPE_IBASettings, 1, lg);
@@ -1463,16 +1254,25 @@ void Thermostat::set_settings_language(const uint8_t lg) {
}
// Set the control-mode for hc 0-off, 1-RC20, 2-RC3x
void Thermostat::set_control(const uint8_t ctrl, const uint8_t hc_num) {
void Thermostat::set_control(const char * value, const int8_t id) {
uint8_t ctrl = 0;
if (!Helpers::value2number(value, ctrl)) {
return;
}
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("Set control: Heating Circuit %d not found or activated"), hc_num);
return;
}
if (ctrl > 2) {
LOG_WARNING(F("Set control: Invalid control mode: %d"), ctrl);
return;
}
if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35 || (flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) {
LOG_INFO(F("Setting circuit-control for hc%d to %d"), hc_num, ctrl);
write_command(set_typeids[hc->hc_num() - 1], 26, ctrl);
@@ -1482,23 +1282,37 @@ void Thermostat::set_control(const uint8_t ctrl, const uint8_t hc_num) {
}
// sets the thermostat ww working mode, where mode is a string
void Thermostat::set_ww_mode(const std::string & mode) {
if (strcasecmp("off", mode.c_str()) == 0) {
LOG_INFO(F("Setting thermostat warm water mode to %s"), mode.c_str());
write_command(EMS_TYPE_wwSettings, 2, 0);
} else if (strcasecmp("on", mode.c_str()) == 0) {
LOG_INFO(F("Setting thermostat warm water mode to %s"), mode.c_str());
write_command(EMS_TYPE_wwSettings, 2, 1);
} else if (strcasecmp("auto", mode.c_str()) == 0) {
LOG_INFO(F("Setting thermostat warm water mode to %s"), mode.c_str());
write_command(EMS_TYPE_wwSettings, 2, 2);
void Thermostat::set_wwmode(const char * value, const int8_t id) {
std::string v;
if (!Helpers::value2string(value, v)) {
return;
}
uint8_t set = 0xFF; // some dummy value
if (v == "off") {
set = 0;
} else if (v == "on") {
set = 1;
} else if (v == "auto") {
set = 2;
}
if (set != 0xFF) {
LOG_INFO(F("Setting thermostat warm water mode to %s"), v.c_str());
write_command(EMS_TYPE_wwSettings, 2, set);
} else {
LOG_WARNING(F("Set thermostat warm water mode: Invalid mode: %s"), mode.c_str());
LOG_WARNING(F("Set thermostat warm water mode: Invalid mode: %s"), v.c_str());
}
}
// set the holiday as string dd.mm.yyyy-dd.mm.yyyy
void Thermostat::set_holiday(const char * hd, const uint8_t hc_num) {
void Thermostat::set_holiday(const char * value, const int8_t id) {
std::string hd;
if (!Helpers::value2string(value, hd)) {
return;
}
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("Set holiday: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
@@ -1520,7 +1334,13 @@ void Thermostat::set_holiday(const char * hd, const uint8_t hc_num) {
}
// set pause in hours
void Thermostat::set_pause(const uint8_t hrs, const uint8_t hc_num) {
void Thermostat::set_pause(const char * value, const int8_t id) {
uint8_t hrs = 0;
if (!Helpers::value2number(value, hrs)) {
return;
}
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("Set pause: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
@@ -1535,7 +1355,13 @@ void Thermostat::set_pause(const uint8_t hrs, const uint8_t hc_num) {
}
// set partymode in hours
void Thermostat::set_party(const uint8_t hrs, const uint8_t hc_num) {
void Thermostat::set_party(const char * value, const int8_t id) {
uint8_t hrs = 0;
if (!Helpers::value2number(value, hrs)) {
return;
}
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
LOG_WARNING(F("Set party: Heating Circuit %d not found or activated for device ID 0x%02X"), hc_num, device_id());
@@ -1551,11 +1377,17 @@ void Thermostat::set_party(const uint8_t hrs, const uint8_t hc_num) {
// set date&time as string hh:mm:ss-dd.mm.yyyy-dw-dst or "NTP" for setting to internet-time
// dw - day of week (0..6), dst- summertime (0/1)
void Thermostat::set_datetime(const char * dt) {
// id is ignored
void Thermostat::set_datetime(const char * value, const int8_t id) {
std::string dt;
if (!Helpers::value2string(value, dt)) {
return;
}
uint8_t data[9];
if (strcmp(dt,"NTP") == 0) {
time_t now = time(nullptr);
tm * tm_ = localtime(&now);
if (dt == "ntp") {
time_t now = time(nullptr);
tm * tm_ = localtime(&now);
if (tm_->tm_year < 110) { // no NTP time
LOG_WARNING(F("No NTP time. Cannot set RCtime"));
return;
@@ -1590,34 +1422,43 @@ void Thermostat::set_datetime(const char * dt) {
}
// sets the thermostat working mode, where mode is a string
void Thermostat::set_mode(const std::string & mode, const uint8_t hc_num) {
// converts string mode to HeatingCircuit::Mode
void Thermostat::set_mode(const char * value, const int8_t id) {
std::string mode;
if (!Helpers::value2string(value, mode)) {
return;
}
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
if (mode_tostring(HeatingCircuit::Mode::OFF) == mode) {
set_mode(HeatingCircuit::Mode::OFF, hc_num);
set_mode_n(HeatingCircuit::Mode::OFF, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::MANUAL) == mode) {
set_mode(HeatingCircuit::Mode::MANUAL, hc_num);
set_mode_n(HeatingCircuit::Mode::MANUAL, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::AUTO) == mode) {
set_mode(HeatingCircuit::Mode::AUTO, hc_num);
set_mode_n(HeatingCircuit::Mode::AUTO, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::DAY) == mode) {
set_mode(HeatingCircuit::Mode::DAY, hc_num);
set_mode_n(HeatingCircuit::Mode::DAY, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::NIGHT) == mode) {
set_mode(HeatingCircuit::Mode::NIGHT, hc_num);
set_mode_n(HeatingCircuit::Mode::NIGHT, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::HEAT) == mode) {
set_mode(HeatingCircuit::Mode::HEAT, hc_num);
set_mode_n(HeatingCircuit::Mode::HEAT, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::NOFROST) == mode) {
set_mode(HeatingCircuit::Mode::NOFROST, hc_num);
set_mode_n(HeatingCircuit::Mode::NOFROST, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::ECO) == mode) {
set_mode(HeatingCircuit::Mode::ECO, hc_num);
set_mode_n(HeatingCircuit::Mode::ECO, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::HOLIDAY) == mode) {
set_mode(HeatingCircuit::Mode::HOLIDAY, hc_num);
set_mode_n(HeatingCircuit::Mode::HOLIDAY, hc_num);
} else if (mode_tostring(HeatingCircuit::Mode::COMFORT) == mode) {
set_mode(HeatingCircuit::Mode::COMFORT, hc_num);
set_mode_n(HeatingCircuit::Mode::COMFORT, hc_num);
} else {
LOG_WARNING(F("Invalid mode %s. Cannot set"), mode.c_str());
}
}
// Set the thermostat working mode
void Thermostat::set_mode(const uint8_t mode, const uint8_t hc_num) {
// mode is HeatingCircuit::Mode
void Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
if (can_write()) {
LOG_WARNING(F("Write not supported for this model Thermostat"));
return;
@@ -1893,109 +1734,93 @@ void Thermostat::set_temperature(const float temperature, const uint8_t mode, co
}
}
// add console commands
void Thermostat::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(master)},
flash_string_vector{F_(deviceid_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
uint8_t value;
if (arguments.empty()) {
value = EMSESP_DEFAULT_MASTER_THERMOSTAT;
} else {
value = Helpers::hextoint(arguments.front().c_str());
}
EMSESP::emsespSettingsService.update(
[&](EMSESPSettings & settings) {
settings.master_thermostat = value;
EMSESP::actual_master_thermostat(value); // set the internal value too
char buffer[5];
shell.printfln(F_(master_thermostat_fmt),
!value ? uuid::read_flash_string(F_(auto)).c_str() : Helpers::hextoa(buffer, value));
return StateUpdateResult::CHANGED;
},
"local");
});
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(read)},
flash_string_vector{F_(typeid_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint16_t type_id = Helpers::hextoint(arguments.front().c_str());
EMSESP::send_read_request(type_id, device_id());
});
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(temp)},
flash_string_vector{F_(degrees_mandatory), F_(hc_optional), F_(mode_optional)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint8_t hc = (arguments.size() >= 2) ? arguments[1].at(0) - '0' : AUTO_HEATING_CIRCUIT;
if ((arguments.size() == 3)) {
set_temperature(atof(arguments.front().c_str()), arguments.back().c_str(), hc);
} else if (arguments[1].at(0) >= 'A') {
set_temperature(atof(arguments.front().c_str()), arguments.back().c_str(), AUTO_HEATING_CIRCUIT);
} else {
set_temperature(atof(arguments.front().c_str()), HeatingCircuit::Mode::AUTO, hc);
}
});
EMSESPShell::commands->add_command(
ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(mode)},
flash_string_vector{F_(mode_mandatory), F_(hc_optional)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
uint8_t hc = (arguments.size() == 2) ? arguments[1].at(0) - '0' : AUTO_HEATING_CIRCUIT;
set_mode(arguments.front(), hc);
},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F("off")),
read_flash_string(F("manual")),
read_flash_string(F("day")),
read_flash_string(F("night")),
read_flash_string(F("eco")),
read_flash_string(F("comfort")),
read_flash_string(F("heat")),
read_flash_string(F("nofrost")),
read_flash_string(F("auto"))
};
});
EMSESPShell::commands->add_command(
ShellContext::THERMOSTAT,
CommandFlags::ADMIN,
flash_string_vector{F_(wwmode)},
flash_string_vector{F_(mode_mandatory)},
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) { set_ww_mode(arguments.front()); },
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F("off")), read_flash_string(F("on")), read_flash_string(F("auto"))};
});
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::USER,
flash_string_vector{F_(show)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_values(shell); });
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::USER,
flash_string_vector{F_(set)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) {
char buffer[4];
shell.printfln(F_(master_thermostat_fmt),
settings.master_thermostat == 0 ? uuid::read_flash_string(F_(auto)).c_str()
: Helpers::hextoa(buffer, settings.master_thermostat));
shell.println();
});
});
// enter the context
Console::enter_custom_context(shell, context);
// for HA specifically when receiving over MQTT
void Thermostat::thermostat_cmd_temp(const char * message) {
float f = strtof((char *)message, 0);
set_temperature(f, HeatingCircuit::Mode::AUTO, AUTO_HEATING_CIRCUIT);
}
// for HA specifically when receiving over MQTT
// message payload holds the text name of the mode e.g. "auto"
void Thermostat::thermostat_cmd_mode(const char * message) {
set_mode(message, AUTO_HEATING_CIRCUIT);
}
void Thermostat::set_temperature_value(const char * value, const uint8_t id, const uint8_t mode) {
float f = 0;
uint8_t hc_num = (id == -1) ? DEFAULT_HEATING_CIRCUIT : id;
if (Helpers::value2float(value, f)) {
set_temperature(f, mode, hc_num);
}
}
void Thermostat::set_temp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::AUTO);
}
void Thermostat::set_nighttemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::NIGHT);
}
void Thermostat::set_daytemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::DAY);
}
void Thermostat::set_nofrosttemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::NOFROST);
}
void Thermostat::set_ecotemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::ECO);
}
void Thermostat::set_heattemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::HEAT);
}
void Thermostat::set_summertemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::SUMMER);
}
void Thermostat::set_designtemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::DESIGN);
}
void Thermostat::set_offsettemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::OFFSET);
}
void Thermostat::set_holidaytemp(const char * value, const int8_t id) {
set_temperature_value(value, id, HeatingCircuit::Mode::HOLIDAY);
}
// commands for MQTT and Console
void Thermostat::init_mqtt() {
register_mqtt_cmd(F("wwmode"), std::bind(&Thermostat::set_wwmode, this, _1, _2));
register_mqtt_cmd(F("control"), std::bind(&Thermostat::set_control, this, _1, _2));
register_mqtt_cmd(F("mode"), std::bind(&Thermostat::set_mode, this, _1, _2));
register_mqtt_cmd(F("holiday"), std::bind(&Thermostat::set_holiday, this, _1, _2));
register_mqtt_cmd(F("pause"), std::bind(&Thermostat::set_pause, this, _1, _2));
register_mqtt_cmd(F("party"), std::bind(&Thermostat::set_party, this, _1, _2));
register_mqtt_cmd(F("datetime"), std::bind(&Thermostat::set_datetime, this, _1, _2));
register_mqtt_cmd(F("minexttemp"), std::bind(&Thermostat::set_settings_minexttemp, this, _1, _2));
register_mqtt_cmd(F("clockoffset"), std::bind(&Thermostat::set_settings_clockoffset, this, _1, _2));
register_mqtt_cmd(F("calinttemp"), std::bind(&Thermostat::set_settings_calinttemp, this, _1, _2));
register_mqtt_cmd(F("display"), std::bind(&Thermostat::set_settings_display, this, _1, _2));
register_mqtt_cmd(F("building"), std::bind(&Thermostat::set_settings_building, this, _1, _2));
register_mqtt_cmd(F("language"), std::bind(&Thermostat::set_settings_language, this, _1, _2));
register_mqtt_cmd(F("remotetemp"), std::bind(&Thermostat::set_remotetemp, this, _1, _2));
register_mqtt_cmd(F("temp"), std::bind(&Thermostat::set_temp, this, _1, _2));
register_mqtt_cmd(F("nighttemp"), std::bind(&Thermostat::set_nighttemp, this, _1, _2));
register_mqtt_cmd(F("daytemp"), std::bind(&Thermostat::set_daytemp, this, _1, _2));
register_mqtt_cmd(F("nofrosttemp"), std::bind(&Thermostat::set_nofrosttemp, this, _1, _2));
register_mqtt_cmd(F("ecotemp"), std::bind(&Thermostat::set_ecotemp, this, _1, _2));
register_mqtt_cmd(F("heattemp"), std::bind(&Thermostat::set_heattemp, this, _1, _2));
register_mqtt_cmd(F("summertemp"), std::bind(&Thermostat::set_summertemp, this, _1, _2));
register_mqtt_cmd(F("designtemp"), std::bind(&Thermostat::set_designtemp, this, _1, _2));
register_mqtt_cmd(F("offsettemp"), std::bind(&Thermostat::set_offsettemp, this, _1, _2));
register_mqtt_cmd(F("holidaytemp"), std::bind(&Thermostat::set_holidaytemp, this, _1, _2));
}
} // namespace emsesp

View File

@@ -220,55 +220,61 @@ class Thermostat : public EMSdevice {
void process_IBASettings(std::shared_ptr<const Telegram> telegram);
void process_RCTime(std::shared_ptr<const Telegram> telegram);
void process_RC35wwSettings(std::shared_ptr<const Telegram> telegram);
void process_RC35Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC35Set(std::shared_ptr<const Telegram> telegram);
void process_RC30Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC30Set(std::shared_ptr<const Telegram> telegram);
void process_RC20Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC20Set(std::shared_ptr<const Telegram> telegram);
void process_RC20Remote(std::shared_ptr<const Telegram> telegram);
void process_RC20Monitor_2(std::shared_ptr<const Telegram> telegram);
void process_RC20Set_2(std::shared_ptr<const Telegram> telegram);
void process_RC10Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC10Set(std::shared_ptr<const Telegram> telegram);
void process_RC300Monitor(std::shared_ptr<const Telegram> telegram);
void process_RC300Set(std::shared_ptr<const Telegram> telegram);
void process_JunkersMonitor(std::shared_ptr<const Telegram> telegram);
void process_JunkersSet(std::shared_ptr<const Telegram> telegram);
void process_EasyMonitor(std::shared_ptr<const Telegram> telegram);
void process_RC300WWmode(std::shared_ptr<const Telegram> telegram);
// set functions
void set_settings_minexttemp(const int8_t mt);
void set_settings_calinttemp(const int8_t ct);
void set_settings_clockoffset(const int8_t co);
void set_settings_display(const uint8_t ds);
void set_settings_building(const uint8_t bg);
void set_settings_language(const uint8_t lg);
void set_control(const uint8_t ctrl, const uint8_t hc_num);
void set_ww_mode(const std::string & mode);
void set_holiday(const char * hd, const uint8_t hc_num);
void set_datetime(const char * dt);
void set_pause(const uint8_t hrs, const uint8_t hc_num);
void set_party(const uint8_t hrs, const uint8_t hc_num);
void set_mode(const uint8_t mode, const uint8_t hc_num);
void set_mode(const std::string & mode, const uint8_t hc_num);
// internal helper functions
void set_mode_n(const uint8_t mode, const uint8_t hc_num);
void set_temperature_value(const char * value, const uint8_t hc, const uint8_t mode);
void set_temperature(const float temperature, const std::string & mode, const uint8_t hc_num);
void set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num);
// MQTT functions
void thermostat_cmd(const char * message);
// for HA specifically. MQTT functions.
void thermostat_cmd_temp(const char * message);
void thermostat_cmd_mode(const char * message);
// set functions - these use the id/hc
void set_mode(const char * value, const int8_t id);
void set_control(const char * value, const int8_t id);
void set_holiday(const char * value, const int8_t id);
void set_pause(const char * value, const int8_t id);
void set_party(const char * value, const int8_t id);
void set_temp(const char * value, const int8_t id);
void set_nighttemp(const char * value, const int8_t id);
void set_daytemp(const char * value, const int8_t id);
void set_nofrosttemp(const char * value, const int8_t id);
void set_ecotemp(const char * value, const int8_t id);
void set_heattemp(const char * value, const int8_t id);
void set_summertemp(const char * value, const int8_t id);
void set_designtemp(const char * value, const int8_t id);
void set_offsettemp(const char * value, const int8_t id);
void set_holidaytemp(const char * value, const int8_t id);
void set_remotetemp(const char * value, const int8_t id);
// set functions - these don't use the id/hc
void set_wwmode(const char * value, const int8_t id);
void set_datetime(const char * value, const int8_t id);
void set_settings_minexttemp(const char * value, const int8_t id);
void set_settings_clockoffset(const char * value, const int8_t id);
void set_settings_calinttemp(const char * value, const int8_t id);
void set_settings_display(const char * value, const int8_t id);
void set_settings_building(const char * value, const int8_t id);
void set_settings_language(const char * value, const int8_t id);
};
} // namespace emsesp