initial commit

This commit is contained in:
proddy
2020-07-05 18:29:08 +02:00
parent 26b201ea2f
commit c5933e8c14
739 changed files with 86566 additions and 20952 deletions

896
src/devices/boiler.cpp Normal file
View File

@@ -0,0 +1,896 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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(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};
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);
// the telegram handlers...
register_telegram_type(0x10, F("UBAErrorMessage1"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
register_telegram_type(0x11, F("UBAErrorMessage2"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
register_telegram_type(0x18, F("UBAMonitorFast"), false, std::bind(&Boiler::process_UBAMonitorFast, this, _1));
register_telegram_type(0x19, F("UBAMonitorSlow"), true, std::bind(&Boiler::process_UBAMonitorSlow, this, _1));
register_telegram_type(0x34, F("UBAMonitorWW"), false, std::bind(&Boiler::process_UBAMonitorWW, this, _1));
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, std::bind(&Boiler::process_UBAMaintenanceStatus, this, _1));
register_telegram_type(0x2A, F("MC10Status"), false, std::bind(&Boiler::process_MC10Status, this, _1));
register_telegram_type(0x33, F("UBAParameterWW"), true, std::bind(&Boiler::process_UBAParameterWW, this, _1));
register_telegram_type(0x14, F("UBATotalUptime"), false, std::bind(&Boiler::process_UBATotalUptime, this, _1));
register_telegram_type(0x35, F("UBAFlags"), false, std::bind(&Boiler::process_UBAFlags, this, _1));
register_telegram_type(0x15, F("UBAMaintenanceSettings"), false, std::bind(&Boiler::process_UBAMaintenanceSettings, this, _1));
register_telegram_type(0x16, F("UBAParameters"), true, std::bind(&Boiler::process_UBAParameters, this, _1));
register_telegram_type(0x1A, F("UBASetPoints"), false, std::bind(&Boiler::process_UBASetPoints, this, _1));
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, std::bind(&Boiler::process_UBAOutdoorTemp, this, _1));
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));
}
// add submenu context
void Boiler::add_context_menu() {
EMSESPShell::commands->add_command(ShellContext::MAIN,
CommandFlags::USER,
flash_string_vector{F_(boiler)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
Boiler::console_commands(shell, 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);
}
const char * command = doc["cmd"];
if (command == nullptr) {
return;
}
// boiler ww comfort setting
if (strcmp(command, "comfort") == 0) {
const char * data = doc["data"];
if (data == nullptr) {
return;
}
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"];
if (t) {
set_flow_temp(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);
}
}
// publish values via MQTT
void Boiler::publish_values() {
const size_t capacity = JSON_OBJECT_SIZE(47); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
DynamicJsonDocument doc(capacity);
char s[10]; // for formatting strings
if (Helpers::hasValue(wWComfort_)) {
if (wWComfort_ == 0x00) {
doc["wWComfort"] = "Hot";
} else if (wWComfort_ == 0xD8) {
doc["wWComfort"] = "Eco";
} else if (wWComfort_ == 0xEC) {
doc["wWComfort"] = "Intelligent";
}
}
if (Helpers::hasValue(wWSelTemp_)) {
doc["wWSelTemp"] = wWSelTemp_;
}
if (Helpers::hasValue(wWSetTmp_)) {
doc["wWSetTemp"] = wWSetTmp_;
}
if (Helpers::hasValue(wWDisinfectTemp_)) {
doc["wWDisinfectionTemp"] = wWDisinfectTemp_;
}
if (Helpers::hasValue(selFlowTemp_)) {
doc["selFlowTemp"] = selFlowTemp_;
}
if (Helpers::hasValue(selBurnPow_)) {
doc["selBurnPow"] = selBurnPow_;
}
if (Helpers::hasValue(curBurnPow_)) {
doc["curBurnPow"] = curBurnPow_;
}
if (Helpers::hasValue(pumpMod_)) {
doc["pumpMod"] = pumpMod_;
}
if (Helpers::hasValue(pumpMod2_)) {
doc["pumpMod2"] = pumpMod2_;
}
if (Helpers::hasValue(wWCircPump_, true)) {
doc["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWCircPumpType_)) {
doc["wWCiPuType"] = wWCircPumpType_ ? "valve" : "pump";
}
if (Helpers::hasValue(wWCircPumpMode_)) {
doc["wWCiPuMode"] = wWCircPumpMode_;
}
if (Helpers::hasValue(wWCirc_)) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(extTemp_)) {
doc["outdoorTemp"] = (float)extTemp_ / 10;
}
if (Helpers::hasValue(wWCurTmp_)) {
doc["wWCurTmp"] = (float)wWCurTmp_ / 10;
}
if (Helpers::hasValue(wWCurFlow_)) {
doc["wWCurFlow"] = (float)wWCurFlow_ / 10;
}
if (Helpers::hasValue(curFlowTemp_)) {
doc["curFlowTemp"] = (float)curFlowTemp_ / 10;
}
if (Helpers::hasValue(retTemp_)) {
doc["retTemp"] = (float)retTemp_ / 10;
}
if (Helpers::hasValue(switchTemp_)) {
doc["switchTemp"] = (float)switchTemp_ / 10;
}
if (Helpers::hasValue(sysPress_)) {
doc["sysPress"] = (float)sysPress_ / 10;
}
if (Helpers::hasValue(boilTemp_)) {
doc["boilTemp"] = (float)boilTemp_ / 10;
}
if (Helpers::hasValue(wwStorageTemp1_)) {
doc["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10;
}
if (Helpers::hasValue(wwStorageTemp2_)) {
doc["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10;
}
if (Helpers::hasValue(exhaustTemp_)) {
doc["exhaustTemp"] = (float)exhaustTemp_ / 10;
}
if (Helpers::hasValue(wWActivated_, true)) {
doc["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWOneTime_, true)) {
doc["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWDesinfecting_, true)) {
doc["wWDesinfecting"] = Helpers::render_value(s, wWDesinfecting_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWReadiness_, true)) {
doc["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWRecharging_, true)) {
doc["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWTemperatureOK_, true)) {
doc["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWCirc_, true)) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(burnGas_, true)) {
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_, true)) {
doc["heatPmp"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(fanWork_, true)) {
doc["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(ignWork_, true)) {
doc["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(wWHeat_, true)) {
doc["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(heating_temp_)) {
doc["heating_temp"] = heating_temp_;
}
if (Helpers::hasValue(pump_mod_max_)) {
doc["pump_mod_max"] = pump_mod_max_;
}
if (Helpers::hasValue(pump_mod_min_)) {
doc["pump_mod_min"] = pump_mod_min_;
}
if (Helpers::hasValue(wWStarts_)) {
doc["wWStarts"] = wWStarts_;
}
if (Helpers::hasValue(wWWorkM_)) {
doc["wWWorkM"] = wWWorkM_;
}
if (Helpers::hasValue(UBAuptime_)) {
doc["UBAuptime"] = UBAuptime_;
}
if (Helpers::hasValue(burnStarts_)) {
doc["burnStarts"] = burnStarts_;
}
if (Helpers::hasValue(burnWorkMin_)) {
doc["burnWorkMin"] = burnWorkMin_;
}
if (Helpers::hasValue(heatWorkMin_)) {
doc["heatWorkMin"] = heatWorkMin_;
}
if (Helpers::hasValue(serviceCode_)) {
doc["serviceCode"] = serviceCodeChar_;
doc["serviceCodeNumber"] = serviceCode_;
}
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Performing a boiler publish"));
#endif
// if we have data, publish it
if (!doc.isNull()) {
Mqtt::publish("boiler_data", doc);
}
}
// called after a process command is called, to check values and see if we need to force an MQTT publish
bool Boiler::updated_values() {
return false;
}
// print values to shell console
void Boiler::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // for showing the header
if (Helpers::hasValue(tap_water_active_, true)) {
print_value(shell, 2, F("Hot tap water"), tap_water_active_ ? F("running") : F("off"));
}
if (Helpers::hasValue(heating_active_, true)) {
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_, true)) {
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);
if (Helpers::hasValue(wWCircPumpMode_)) {
if (wWCircPumpMode_ == 7) {
print_value(shell, 2, F("Warm Water circulation pump freq"), F("continuous"));
} else {
char s[7];
char buffer[2];
buffer[0] = (wWCircPumpMode_ % 10) + '0';
buffer[1] = '\0';
strlcpy(s, buffer, 7);
strlcat(s, "x3min", 7);
print_value(shell, 2, F("Warm Water circulation pump freq"), s);
}
}
print_value(shell, 2, F("Warm Water circulation active"), wWCirc_, nullptr, EMS_VALUE_BOOL);
if (wWComfort_ == 0x00) {
print_value(shell, 2, F("Warm Water comfort setting"), F("Hot"));
} else if (wWComfort_ == 0xD8) {
print_value(shell, 2, F("Warm Water comfort setting"), F("Eco"));
} else if (wWComfort_ == 0xEC) {
print_value(shell, 2, F("Warm Water comfort setting"), F("Intelligent"));
}
print_value(shell, 2, F("Warm water mix temperature"), wwMixTemperature_, F_(degrees), 10);
print_value(shell, 2, F("Warm water buffer boiler temperature"), wwBufferBoilerTemperature_, F_(degrees), 10);
print_value(shell, 2, F("Warm Water disinfection temperature"), wWDisinfectTemp_, F_(degrees));
print_value(shell, 2, F("Warm Water selected temperature"), wWSelTemp_, F_(degrees));
print_value(shell, 2, F("Warm Water set temperature"), wWSetTmp_, F_(degrees));
print_value(shell, 2, F("Warm Water current temperature (intern)"), wWCurTmp_, F_(degrees), 10);
print_value(shell, 2, F("Warm water storage temperature (intern)"), wwStorageTemp1_, F_(degrees), 10);
print_value(shell, 2, F("Warm Water current temperature (extern)"), wWCurTmp2_, F_(degrees), 10);
print_value(shell, 2, F("Warm water storage temperature (extern)"), wwStorageTemp2_, F_(degrees), 10);
print_value(shell, 2, F("Warm Water current tap water flow"), wWCurFlow_, F("l/min"), 10);
print_value(shell, 2, F("Warm Water # starts"), wWStarts_, nullptr);
if (Helpers::hasValue(wWWorkM_)) {
shell.printfln(F(" Warm Water active time: %d days %d hours %d minutes"), wWWorkM_ / 1440, (wWWorkM_ % 1440) / 60, wWWorkM_ % 60);
}
print_value(shell, 2, F("Warm Water charging"), wWHeat_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Warm Water disinfecting"), wWDesinfecting_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Selected flow temperature"), selFlowTemp_, F_(degrees));
print_value(shell, 2, F("Current flow temperature"), curFlowTemp_, F_(degrees), 10);
print_value(shell, 2, F("Max boiler temperature"), boilTemp_, F_(degrees), 10);
print_value(shell, 2, F("Return temperature"), retTemp_, F_(degrees), 10);
print_value(shell, 2, F("Gas"), burnGas_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Boiler pump"), heatPmp_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Fan"), fanWork_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Ignition"), ignWork_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Burner selected max power"), selBurnPow_, F_(percent));
print_value(shell, 2, F("Burner current power"), curBurnPow_, F_(percent));
print_value(shell, 2, F("Flame current"), flameCurr_, F("uA"), 10);
print_value(shell, 2, F("System pressure"), sysPress_, F("bar"), 10);
if (Helpers::hasValue(serviceCode_)) {
shell.printfln(F(" System service code: %s (%d)"), serviceCodeChar_, serviceCode_);
} else if (serviceCodeChar_[0] != '\0') {
print_value(shell, 2, F("System service code"), serviceCodeChar_);
}
// UBAParameters
print_value(shell, 2, F("Heating temperature setting on the boiler"), heating_temp_, F_(degrees));
print_value(shell, 2, F("Boiler circuit pump modulation max power"), pump_mod_max_, F_(percent));
print_value(shell, 2, F("Boiler circuit pump modulation min power"), pump_mod_min_, F_(percent));
// UBAMonitorSlow
if (Helpers::hasValue(extTemp_)) {
print_value(shell, 2, F("Outside temperature"), extTemp_, F_(degrees), 10);
}
print_value(shell, 2, F("Exhaust temperature"), exhaustTemp_, F_(degrees), 10);
print_value(shell, 2, F("Pump modulation"), pumpMod_, F_(percent));
print_value(shell, 2, F("Pump modulation2"), pumpMod2_, F_(percent));
print_value(shell, 2, F("Burner # starts"), burnStarts_, nullptr);
if (Helpers::hasValue(burnWorkMin_)) {
shell.printfln(F(" Total burner operating time: %d days %d hours %d minutes"), burnWorkMin_ / 1440, (burnWorkMin_ % 1440) / 60, burnWorkMin_ % 60);
}
if (Helpers::hasValue(heatWorkMin_)) {
shell.printfln(F(" Total heat operating time: %d days %d hours %d minutes"), heatWorkMin_ / 1440, (heatWorkMin_ % 1440) / 60, heatWorkMin_ % 60);
}
if (Helpers::hasValue(UBAuptime_)) {
shell.printfln(F(" Total UBA working time: %d days %d hours %d minutes"), UBAuptime_ / 1440, (UBAuptime_ % 1440) / 60, UBAuptime_ % 60);
}
}
/*
* Check if hot tap water or heating is active
* If a value has changed, post it immediately to MQTT so we get real time data
*/
void Boiler::check_active() {
// hot tap water, using flow to check instead of the burner power
// send these values back to the main EMSESP, so other classes (e.g. Shower) can use it
if (Helpers::hasValue(wWCurFlow_) && Helpers::hasValue(burnGas_)) {
tap_water_active_ = ((wWCurFlow_ != 0) && (burnGas_ != EMS_VALUE_BOOL_OFF));
EMSESP::tap_water_active(tap_water_active_);
}
// heating
// using a quick hack for checking the heating by looking at the Selected Flow Temp, but doesn't work for all boilers apparently
if (Helpers::hasValue(selFlowTemp_) && Helpers::hasValue(burnGas_)) {
heating_active_ = (!tap_water_active_ && ((selFlowTemp_ >= EMS_BOILER_SELFLOWTEMP_HEATING) && (burnGas_ != EMS_VALUE_BOOL_OFF)));
}
// 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_, true) && Helpers::hasValue(heating_active_, true)) {
uint8_t latest_boilerState = (tap_water_active_ << 1) + heating_active_;
if (latest_boilerState != last_boilerState) {
last_boilerState = latest_boilerState;
Mqtt::publish("tapwater_active", tap_water_active_);
Mqtt::publish("heating_active", heating_active_);
}
}
}
// 0x33
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(wWActivated_, 1); // 0xFF means on
telegram->read_value(wWCircPump_, 6); // 0xFF means on
telegram->read_value(wWCircPumpMode_, 7); // 1=1x3min... 6=6x3min, 7=continuous
telegram->read_value(wWCircPumpType_, 10); // 0 = charge pump, 0xff = 3-way valve
telegram->read_value(wWSelTemp_, 2);
telegram->read_value(wWDisinfectTemp_, 8);
telegram->read_value(wWComfort_, 9);
}
// 0x18
void Boiler::process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(selFlowTemp_, 0);
telegram->read_value(curFlowTemp_, 1);
telegram->read_value(selBurnPow_, 3); // burn power max setting
telegram->read_value(curBurnPow_, 4);
telegram->read_bitvalue(burnGas_, 7, 0);
telegram->read_bitvalue(fanWork_, 7, 2);
telegram->read_bitvalue(ignWork_, 7, 3);
telegram->read_bitvalue(heatPmp_, 7, 5);
telegram->read_bitvalue(wWHeat_, 7, 6);
telegram->read_bitvalue(wWCirc_, 7, 7);
// warm water storage sensors (if present)
// wwStorageTemp2 is also used by some brands as the boiler temperature - see https://github.com/proddy/EMS-ESP/issues/206
telegram->read_value(wwStorageTemp1_, 9); // 0x8300 if not available
telegram->read_value(wwStorageTemp2_, 11); // 0x8000 if not available - this is boiler temp
telegram->read_value(retTemp_, 13);
telegram->read_value(flameCurr_, 15);
telegram->read_value(serviceCode_, 20);
// system pressure. FF means missing
telegram->read_value(sysPress_, 17); // is *10
// read the service code / installation status as appears on the display
if ((telegram->message_length > 18) && (telegram->offset == 0)) {
serviceCodeChar_[0] = char(telegram->message_data[18]); // ascii character 1
serviceCodeChar_[1] = char(telegram->message_data[19]); // ascii character 2
serviceCodeChar_[2] = '\0'; // null terminate string
}
// at this point do a quick check to see if the hot water or heating is active
check_active();
}
/*
* UBATotalUptime - type 0x14 - total uptime
* received only after requested (not broadcasted)
*/
void Boiler::process_UBATotalUptime(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(UBAuptime_, 0, 3); // force to 3 bytes
}
/*
* UBAParameters - type 0x16
*/
void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(heating_temp_, 1);
telegram->read_value(pump_mod_max_, 9);
telegram->read_value(pump_mod_min_, 10);
}
/*
* UBAMonitorWW - type 0x34 - warm water monitor. 19 bytes long
* received every 10 seconds
*/
void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(wWSetTmp_, 0);
telegram->read_value(wWCurTmp_, 1);
telegram->read_value(wWCurTmp2_, 3);
telegram->read_value(wWCurFlow_, 9);
telegram->read_value(wWWorkM_, 10, 3); // force to 3 bytes
telegram->read_value(wWStarts_, 13, 3); // force to 3 bytes
telegram->read_bitvalue(wWOneTime_, 5, 1);
telegram->read_bitvalue(wWDesinfecting_, 5, 2);
telegram->read_bitvalue(wWReadiness_, 5, 3);
telegram->read_bitvalue(wWRecharging_, 5, 4);
telegram->read_bitvalue(wWTemperatureOK_, 5, 5);
}
/*
* UBAMonitorFastPlus - type 0xE4 - central heating monitor EMS+
* Still to figure out are: serviceCode, retTemp, sysPress
*/
void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(selFlowTemp_, 6);
telegram->read_bitvalue(burnGas_, 11, 0);
telegram->read_bitvalue(wWHeat_, 11, 2);
telegram->read_value(curBurnPow_, 10);
telegram->read_value(selBurnPow_, 9);
telegram->read_value(curFlowTemp_, 7);
telegram->read_value(flameCurr_, 19);
// read the service code / installation status as appears on the display
if ((telegram->message_length > 4) && (telegram->offset == 0)) {
serviceCodeChar_[0] = char(telegram->message_data[4]); // ascii character 1
serviceCodeChar_[1] = char(telegram->message_data[5]); // ascii character 2
serviceCodeChar_[2] = '\0';
}
// at this point do a quick check to see if the hot water or heating is active
check_active();
}
/*
* UBAMonitorSlow - type 0x19 - central heating monitor part 2 (27 bytes long)
* received every 60 seconds
* e.g. 08 00 19 00 80 00 02 41 80 00 00 00 00 00 03 91 7B 05 B8 40 00 00 00 04 92 AD 00 5E EE 80 00
* 08 0B 19 00 FF EA 02 47 80 00 00 00 00 62 03 CA 24 2C D6 23 00 00 00 27 4A B6 03 6E 43
* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 17 19 20 21 22 23 24
*/
void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(extTemp_, 0);
telegram->read_value(boilTemp_, 2);
telegram->read_value(exhaustTemp_, 4);
telegram->read_value(switchTemp_, 25); // only if there is a mixing module present
telegram->read_value(pumpMod_, 9);
telegram->read_value(burnStarts_, 10, 3); // force to 3 bytes
telegram->read_value(burnWorkMin_, 13, 3); // force to 3 bytes
telegram->read_value(heatWorkMin_, 19, 3); // force to 3 bytes
}
/*
* UBAMonitorSlowPlus2 - type 0xE3
*/
void Boiler::process_UBAMonitorSlowPlus2(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(pumpMod2_, 13);
}
/*
* UBAMonitorSlowPlus - type 0xE5 - central heating monitor EMS+
*/
void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram) {
telegram->read_bitvalue(fanWork_, 2, 2);
telegram->read_bitvalue(ignWork_, 2, 3);
telegram->read_bitvalue(heatPmp_, 2, 5);
telegram->read_bitvalue(wWCirc_, 2, 7);
telegram->read_value(burnStarts_, 10, 3); // force to 3 bytes
telegram->read_value(burnWorkMin_, 13, 3); // force to 3 bytes
telegram->read_value(heatWorkMin_, 19, 3); // force to 3 bytes
telegram->read_value(pumpMod_, 25);
}
// 0xE9 - DHW Status
// e.g. 08 00 E9 00 37 01 F6 01 ED 00 00 00 00 41 3C 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 (CRC=77) #data=27
void Boiler::process_UBADHWStatus(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(wWSetTmp_, 0);
telegram->read_value(wWCurTmp_, 1);
telegram->read_value(wWCurTmp2_, 3);
telegram->read_value(wWWorkM_, 17, 3); // force to 3 bytes
telegram->read_value(wWStarts_, 14, 3); // force to 3 bytes
telegram->read_bitvalue(wWOneTime_, 12, 2);
telegram->read_bitvalue(wWDesinfecting_, 12, 3);
telegram->read_bitvalue(wWReadiness_, 12, 4);
telegram->read_bitvalue(wWRecharging_, 13, 4);
telegram->read_bitvalue(wWTemperatureOK_, 13, 5);
telegram->read_bitvalue(wWCircPump_, 13, 2);
telegram->read_value(wWActivated_, 20);
telegram->read_value(wWSelTemp_, 10);
telegram->read_value(wWDisinfectTemp_, 9);
}
// 0x2A - MC10Status
// e.g. 88 00 2A 00 00 00 00 00 00 00 00 00 D2 00 00 80 00 00 01 08 80 00 02 47 00
// see https://github.com/proddy/EMS-ESP/issues/397
void Boiler::process_MC10Status(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(wwMixTemperature_, 14);
telegram->read_value(wwBufferBoilerTemperature_, 18);
}
/*
* UBAOutdoorTemp - type 0xD1 - external temperature EMS+
*/
void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(extTemp_, 0);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
// UBASetPoint 0x1A
// not yet implemented
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
// uint8_t setpoint = telegram->message_data[0]; // boiler flow temp
// uint8_t ww_power = telegram->message_data[2]; // power in %
}
// 0x35
// not yet implemented
void Boiler::process_UBAFlags(std::shared_ptr<const Telegram> telegram) {
}
// 0x1C
// not yet implemented
void Boiler::process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram) {
}
// 0x15
// not yet implemented
void Boiler::process_UBAMaintenanceSettings(std::shared_ptr<const Telegram> telegram) {
}
// 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
}
#pragma GCC diagnostic pop
// 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);
// for i9000, see #397
write_command(EMS_TYPE_UBAFlags, 3, temperature);
}
// 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);
}
// 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) {
uint8_t set;
if (comfort == 1) {
LOG_INFO(F("Setting boiler warm water to Hot"));
set = 0x00;
} else if (comfort == 2) {
LOG_INFO(F("Setting boiler warm water to Eco"));
set = 0xD8;
} else if (comfort == 3) {
LOG_INFO(F("Setting boiler warm water to Intelligent"));
set = 0xEC;
} else {
return; // do nothing
}
write_command(EMS_TYPE_UBAParameterWW, 9, set);
}
// 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;
// https://github.com/proddy/EMS-ESP/issues/268
if (EMSbus::is_ht3()) {
value = (activated ? 0x08 : 0x00); // 0x08 is on, 0x00 is off
} else {
value = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
}
write_command(EMS_TYPE_UBAParameterWW, 1, value);
}
// 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");
uint8_t message_data[EMS_MAX_TELEGRAM_MESSAGE_LENGTH];
for (uint8_t i = 0; i < sizeof(message_data); i++) {
message_data[i] = 0x00;
}
// 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) {
// on
message_data[0] = 0x5A; // test mode on
message_data[1] = 0x00; // burner output 0%
message_data[3] = 0x64; // boiler pump capacity 100%
message_data[4] = 0xFF; // 3-way valve hot water only
} else {
// get out of test mode. Send all zeros.
// telegram: 0B 08 1D 00 00
message_data[4] = 0x00; // test mode off
}
write_command(EMS_TYPE_UBAFunctionTest, 0, message_data, sizeof(message_data), 0);
}
// 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));
}
// 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));
}
// add console commands
void Boiler::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::BOILER,
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::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_(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)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { show_values(shell); });
// enter the context
Console::enter_custom_context(shell, context);
}
} // namespace emsesp

168
src/devices/boiler.h Normal file
View File

@@ -0,0 +1,168 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_BOILER_H
#define EMSESP_BOILER_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include <string>
#include "emsdevice.h"
#include "telegram.h"
#include "emsesp.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Boiler : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands(Shell & shell, unsigned int context);
uint8_t last_boilerState = 0xFF; // remember last state of heating and warm water on/off
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35;
static constexpr uint8_t EMS_TYPE_UBASetPoints = 0x1A;
static constexpr uint8_t EMS_BOILER_SELFLOWTEMP_HEATING = 20; // was originally 70, changed to 30 for issue #193, then to 20 with issue #344
// UBAParameterWW
uint8_t wWActivated_ = EMS_VALUE_BOOL_NOTSET; // Warm Water activated
uint8_t wWSelTemp_ = EMS_VALUE_UINT_NOTSET; // Warm Water selected temperature
uint8_t wWCircPump_ = EMS_VALUE_BOOL_NOTSET; // Warm Water circulation pump available
uint8_t wWCircPumpMode_ = EMS_VALUE_UINT_NOTSET; // Warm Water circulation pump mode
uint8_t wWCircPumpType_ = EMS_VALUE_BOOL_NOTSET; // Warm Water circulation pump type
uint8_t wWDisinfectTemp_ = EMS_VALUE_UINT_NOTSET; // Warm Water disinfection temperature to prevent infection
uint8_t wWComfort_ = EMS_VALUE_UINT_NOTSET; // WW comfort mode
// MC10Status
uint16_t wwMixTemperature_ = EMS_VALUE_USHORT_NOTSET; // mengertemperatuur
uint16_t wwBufferBoilerTemperature_ = EMS_VALUE_USHORT_NOTSET; // bufferboilertemperatuur
// UBAMonitorFast - 0x18 on EMS1
uint8_t selFlowTemp_ = EMS_VALUE_UINT_NOTSET; // Selected flow temperature
uint16_t curFlowTemp_ = EMS_VALUE_USHORT_NOTSET; // Current flow temperature
uint16_t wwStorageTemp1_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 1
uint16_t wwStorageTemp2_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 2
uint16_t retTemp_ = EMS_VALUE_USHORT_NOTSET; // Return temperature
uint8_t burnGas_ = EMS_VALUE_BOOL_NOTSET; // Gas on/off
uint8_t fanWork_ = EMS_VALUE_BOOL_NOTSET; // Fan on/off
uint8_t ignWork_ = EMS_VALUE_BOOL_NOTSET; // Ignition on/off
uint8_t heatPmp_ = EMS_VALUE_BOOL_NOTSET; // Boiler pump on/off
uint8_t wWHeat_ = EMS_VALUE_BOOL_NOTSET; // 3-way valve on WW
uint8_t wWCirc_ = EMS_VALUE_BOOL_NOTSET; // Circulation on/off
uint8_t selBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner max power %
uint8_t curBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner current power %
uint16_t flameCurr_ = EMS_VALUE_USHORT_NOTSET; // Flame current in micro amps
uint8_t sysPress_ = EMS_VALUE_UINT_NOTSET; // System pressure
char serviceCodeChar_[3] = {'\0'}; // 2 character status/service code
uint16_t serviceCode_ = EMS_VALUE_USHORT_NOTSET; // error/service code
// UBAMonitorSlow - 0x19 on EMS1
int16_t extTemp_ = EMS_VALUE_SHORT_NOTSET; // Outside temperature
uint16_t boilTemp_ = EMS_VALUE_USHORT_NOTSET; // Boiler temperature
uint16_t exhaustTemp_ = EMS_VALUE_USHORT_NOTSET; // Exhaust temperature
uint8_t pumpMod_ = EMS_VALUE_UINT_NOTSET; // Pump modulation %
uint32_t burnStarts_ = EMS_VALUE_ULONG_NOTSET; // # burner restarts
uint32_t burnWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total burner operating time
uint32_t heatWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total heat operating time
uint16_t switchTemp_ = EMS_VALUE_USHORT_NOTSET; // Switch temperature
// UBAMonitorWW
uint8_t wWSetTmp_ = EMS_VALUE_UINT_NOTSET; // Warm Water set temperature
uint16_t wWCurTmp_ = EMS_VALUE_USHORT_NOTSET; // Warm Water current temperature
uint16_t wWCurTmp2_ = EMS_VALUE_USHORT_NOTSET; // Warm Water current temperature storage
uint32_t wWStarts_ = EMS_VALUE_ULONG_NOTSET; // Warm Water # starts
uint32_t wWWorkM_ = EMS_VALUE_ULONG_NOTSET; // Warm Water # minutes
uint8_t wWOneTime_ = EMS_VALUE_BOOL_NOTSET; // Warm Water one time function on/off
uint8_t wWDesinfecting_ = EMS_VALUE_BOOL_NOTSET; // Warm Water disinfection on/off
uint8_t wWReadiness_ = EMS_VALUE_BOOL_NOTSET; // Warm Water readiness on/off
uint8_t wWRecharging_ = EMS_VALUE_BOOL_NOTSET; // Warm Water recharge on/off
uint8_t wWTemperatureOK_ = EMS_VALUE_BOOL_NOTSET; // Warm Water temperature ok on/off
uint8_t wWCurFlow_ = EMS_VALUE_UINT_NOTSET; // Warm Water current flow temp in l/min
// UBATotalUptime
uint32_t UBAuptime_ = EMS_VALUE_ULONG_NOTSET; // Total UBA working hours
// UBAParameters
uint8_t heating_temp_ = EMS_VALUE_UINT_NOTSET; // Heating temperature setting on the boiler
uint8_t pump_mod_max_ = EMS_VALUE_UINT_NOTSET; // Boiler circuit pump modulation max. power %
uint8_t pump_mod_min_ = EMS_VALUE_UINT_NOTSET; // Boiler circuit pump modulation min. power
uint8_t tap_water_active_ = EMS_VALUE_BOOL_NOTSET; // Hot tap water is on/off
uint8_t heating_active_ = EMS_VALUE_BOOL_NOTSET; // Central heating is on/off
uint8_t pumpMod2_ = EMS_VALUE_UINT_NOTSET; // heatpump modulation from 0xE3 (heatpumps)
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
void process_UBATotalUptime(std::shared_ptr<const Telegram> telegram);
void process_UBAParameters(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram);
void process_UBAMonitorSlowPlus2(std::shared_ptr<const Telegram> telegram);
void process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram);
void process_UBASetPoints(std::shared_ptr<const Telegram> telegram);
void process_UBAFlags(std::shared_ptr<const Telegram> telegram);
void process_MC10Status(std::shared_ptr<const Telegram> telegram);
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
void process_UBAMaintenanceSettings(std::shared_ptr<const Telegram> telegram);
void process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram);
void process_UBADHWStatus(std::shared_ptr<const Telegram> telegram);
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);
// 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);
};
} // namespace emsesp
#endif

58
src/devices/connect.cpp Normal file
View File

@@ -0,0 +1,58 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "connect.h"
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};
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("cmd", std::bind(&Controller::cmd, this, _1));
}
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
}
// publish values via MQTT
void Connect::publish_values() {
}
// check to see if values have been updated
bool Connect::updated_values() {
return false;
}
// add console commands
void Connect::console_commands() {
}
} // namespace emsesp

51
src/devices/connect.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_CONNECT_H
#define EMSESP_CONNECT_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Connect : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
};
} // namespace emsesp
#endif

View File

@@ -0,0 +1,60 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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};
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("cmd", std::bind(&Controller::cmd, this, _1));
}
void Controller::add_context_menu() {
}
// 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
}
// publish values via MQTT
void Controller::publish_values() {
}
// check to see if values have been updated
bool Controller::updated_values() {
return false;
}
// add console commands
void Controller::console_commands() {
}
} // namespace emsesp

51
src/devices/controller.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_CONTROLLER_H
#define EMSESP_CONTROLLER_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Controller : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
};
} // namespace emsesp
#endif

60
src/devices/gateway.cpp Normal file
View File

@@ -0,0 +1,60 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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};
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("cmd", std::bind(&Controller::cmd, this, _1));
}
void Gateway::add_context_menu() {
}
// display all values into the shell console
void Gateway::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
}
// publish values via MQTT
void Gateway::publish_values() {
}
// check to see if values have been updated
bool Gateway::updated_values() {
return false;
}
// add console commands
void Gateway::console_commands() {
}
} // namespace emsesp

51
src/devices/gateway.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_GATEWAY_H
#define EMSESP_GATEWAY_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Gateway : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
};
} // namespace emsesp
#endif

92
src/devices/heatpump.cpp Normal file
View File

@@ -0,0 +1,92 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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};
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);
// 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("cmd", std::bind(&Heatpump::cmd, this, _1));
}
// context submenu
void Heatpump::add_context_menu() {
}
// 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
}
// publish values via MQTT
void Heatpump::publish_values() {
}
// check to see if values have been updated
bool Heatpump::updated_values() {
return false;
}
// add console commands
void Heatpump::console_commands() {
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
/*
* Type 0x42B- HeatPump Monitor 1
* e.g. "38 10 FF 00 03 2B 00 D1 08 2A 01"
*/
void Heatpump::process_HPMonitor1(std::shared_ptr<const Telegram> telegram) {
// still to implement
}
/*
* Type 0x47B - HeatPump Monitor 2
* e.g. "38 10 FF 00 03 7B 08 24 00 4B"
*/
void Heatpump::process_HPMonitor2(std::shared_ptr<const Telegram> telegram) {
// still to implement
}
#pragma GCC diagnostic pop
} // namespace emsesp

54
src/devices/heatpump.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_HEATPUMP_H
#define EMSESP_HEATPUMP_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Heatpump : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);
};
} // namespace emsesp
#endif

213
src/devices/mixing.cpp Normal file
View File

@@ -0,0 +1,213 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mixing.h"
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};
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);
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
if (device_id <= 0x27) {
// telegram handlers 0x20 - 0x27 for HC
register_telegram_type(device_id - 0x20 + 0x02D7, F("MMPLUSStatusMessage_HC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_HC, this, _1));
} else {
// telegram handlers for warm water/DHW 0x28, 0x29
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_WWC, this, _1));
}
}
// EMS 1.0
if (flags == EMSdevice::EMS_DEVICE_FLAG_MM10) {
register_telegram_type(0x00AA, F("MMConfigMessage"), false, std::bind(&Mixing::process_MMConfigMessage, this, _1));
register_telegram_type(0x00AB, F("MMStatusMessage"), true, std::bind(&Mixing::process_MMStatusMessage, this, _1));
register_telegram_type(0x00AC, F("MMSetMessage"), false, std::bind(&Mixing::process_MMSetMessage, this, _1));
}
// HT3
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("cmd", std::bind(&Mixing::cmd, this, _1));
}
// add context submenu
void Mixing::add_context_menu() {
}
// check to see if values have been updated
bool Mixing::updated_values() {
return false;
}
// add console commands
void Mixing::console_commands() {
}
// display all values into the shell console
void Mixing::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
if (type_ == Type::NONE) {
return; // don't have any values yet
}
if (type_ == Type::WWC) {
print_value(shell, 2, F("Warm Water Circuit"), hc_, nullptr);
} else {
print_value(shell, 2, F("Heating Circuit"), hc_, nullptr);
}
print_value(shell, 4, F("Current flow temperature"), flowTemp_, F_(degrees), 10);
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);
}
// publish values via MQTT
// ideally we should group up all the mixing units together into a nested JSON but for now we'll send them individually
void Mixing::publish_values() {
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_SMALL);
switch (type_) {
case Type::HC:
doc["type"] = "hc";
break;
case Type::WWC:
doc["type"] = "wwc";
break;
case Type::NONE:
default:
return;
}
if (Helpers::hasValue(flowTemp_)) {
doc["flowTemp"] = (float)flowTemp_ / 10;
}
if (Helpers::hasValue(pumpMod_)) {
doc["pumpMod"] = pumpMod_;
}
if (Helpers::hasValue(status_)) {
doc["status"] = status_;
}
if (Helpers::hasValue(flowSetTemp_)) {
doc["flowSetTemp"] = flowSetTemp_;
}
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Performing a mixing module publish"));
#endif
char topic[30];
char s[3]; // for formatting strings
strlcpy(topic, "mixing_data", 30);
strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic
Mqtt::publish(topic, doc);
}
// heating circuits 0x02D7, 0x02D8 etc...
// e.g. A0 00 FF 00 01 D7 00 00 00 80 00 00 00 00 03 C5
// A0 0B FF 00 01 D7 00 00 00 80 00 00 00 00 03 80
void Mixing::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram) {
type_ = Type::HC;
hc_ = telegram->type_id - 0x02D7 + 1; // determine which circuit this is
telegram->read_value(flowTemp_, 3); // is * 10
telegram->read_value(flowSetTemp_, 5);
telegram->read_value(pumpMod_, 2);
telegram->read_value(status_, 1); // valve status
}
// Mixing module warm water loading/DHW - 0x0331, 0x0332
// e.g. A9 00 FF 00 02 32 02 6C 00 3C 00 3C 3C 46 02 03 03 00 3C // on 0x28
// A8 00 FF 00 02 31 02 35 00 3C 00 3C 3C 46 02 03 03 00 3C // in 0x29
void Mixing::process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram) {
type_ = Type::WWC;
hc_ = telegram->type_id - 0x0331 + 1; // determine which circuit this is. There are max 2.
telegram->read_value(flowTemp_, 0); // is * 10
telegram->read_value(pumpMod_, 2);
telegram->read_value(status_, 11); // temp status
}
// Mixing IMP - 0x010C
// e.g. A0 00 FF 00 00 0C 01 00 00 00 00 00 54
// A1 00 FF 00 00 0C 02 04 00 01 1D 00 82
void Mixing::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
type_ = Type::HC;
hc_ = device_id() - 0x20 + 1;
uint8_t ismixed = 0;
telegram->read_value(ismixed, 0); // check if circuit is active, 0-off, 1-unmixed, 2-mixed
if (ismixed == 0) {
return;
}
if (ismixed == 2) { // we have a mixed circuit
telegram->read_value(flowTemp_, 3); // is * 10
telegram->read_value(flowSetTemp_, 5);
telegram->read_value(status_, 2); // valve status
}
uint8_t pump = 0xFF;
telegram->read_bitvalue(pump, 1, 0); // pump is also in unmixed circuits
if (pump != 0xFF) {
pumpMod_ = 100 * pump;
}
}
// Mixing on a MM10 - 0xAB
// e.g. Mixing Module -> All, type 0xAB, telegram: 21 00 AB 00 2D 01 BE 64 04 01 00 (CRC=15) #data=7
// see also https://github.com/proddy/EMS-ESP/issues/386
void Mixing::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
type_ = Type::HC;
// the heating circuit is determine by which device_id it is, 0x20 - 0x23
// 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module
// see https://github.com/proddy/EMS-ESP/issues/270 and https://github.com/proddy/EMS-ESP/issues/386#issuecomment-629610918
hc_ = device_id() - 0x20 + 1;
telegram->read_value(flowTemp_, 1); // is * 10
telegram->read_value(pumpMod_, 3);
telegram->read_value(flowSetTemp_, 0);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
// Mixing on a MM10 - 0xAA
// e.g. Thermostat -> Mixing Module, type 0xAA, telegram: 10 21 AA 00 FF 0C 0A 11 0A 32 xx
void Mixing::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
hc_ = device_id() - 0x20 + 1;
// pos 0: active FF = on
// pos 1: valve runtime 0C = 120 sec in units of 10 sec
}
// Mixing on a MM10 - 0xAC
// e.g. Thermostat -> Mixing Module, type 0xAC, telegram: 10 21 AC 00 1E 64 01 AB
void Mixing::process_MMSetMessage(std::shared_ptr<const Telegram> telegram) {
hc_ = device_id() - 0x20 + 1;
// pos 0: flowtemp setpoint 1E = 30°C
// pos 1: position in %
}
#pragma GCC diagnostic pop
} // namespace emsesp

72
src/devices/mixing.h Normal file
View File

@@ -0,0 +1,72 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_MIXING_H
#define EMSESP_MIXING_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Mixing : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
void process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram);
void process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram);
void process_MMStatusMessage(std::shared_ptr<const Telegram> telegram);
void process_MMConfigMessage(std::shared_ptr<const Telegram> telegram);
void process_MMSetMessage(std::shared_ptr<const Telegram> telegram);
enum class Type {
NONE,
HC, // heating circuit
WWC // warm water circuit
};
private:
uint16_t hc_ = EMS_VALUE_USHORT_NOTSET;
uint16_t flowTemp_ = EMS_VALUE_USHORT_NOTSET;
uint8_t pumpMod_ = EMS_VALUE_UINT_NOTSET;
uint8_t status_ = EMS_VALUE_UINT_NOTSET;
uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET;
Type type_ = Type::NONE;
};
} // namespace emsesp
#endif

236
src/devices/solar.cpp Normal file
View File

@@ -0,0 +1,236 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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};
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);
// telegram handlers
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
register_telegram_type(0x0097, F("SM10Monitor"), true, std::bind(&Solar::process_SM10Monitor, this, _1));
}
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
register_telegram_type(0x0362, F("SM100Monitor"), true, std::bind(&Solar::process_SM100Monitor, this, _1));
register_telegram_type(0x0363, F("SM100Monitor2"), true, std::bind(&Solar::process_SM100Monitor2, this, _1));
register_telegram_type(0x0366, F("SM100Config"), true, std::bind(&Solar::process_SM100Config, this, _1));
register_telegram_type(0x0364, F("SM100Status"), false, std::bind(&Solar::process_SM100Status, this, _1));
register_telegram_type(0x036A, F("SM100Status2"), false, std::bind(&Solar::process_SM100Status2, this, _1));
register_telegram_type(0x038E, F("SM100Energy"), true, std::bind(&Solar::process_SM100Energy, this, _1));
}
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
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("cmd", std::bind(&Solar::cmd, this, _1));
}
// context submenu
void Solar::add_context_menu() {
}
// display all values into the shell console
void Solar::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
print_value(shell, 2, F("Collector temperature (TS1)"), collectorTemp_, F_(degrees), 10);
print_value(shell, 2, F("Bottom temperature (TS2)"), bottomTemp_, F_(degrees), 10);
print_value(shell, 2, F("Bottom temperature (TS5)"), bottomTemp2_, F_(degrees), 10);
print_value(shell, 2, F("Pump modulation"), pumpModulation_, F_(percent));
print_value(shell, 2, F("Valve (VS2) status"), valveStatus_, nullptr, EMS_VALUE_BOOL);
print_value(shell, 2, F("Pump (PS1) active"), pump_, nullptr, EMS_VALUE_BOOL);
if (Helpers::hasValue(pumpWorkMin_)) {
shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60);
}
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);
}
// publish values via MQTT
void Solar::publish_values() {
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_MEDIUM);
char s[10]; // for formatting strings
if (Helpers::hasValue(collectorTemp_)) {
doc["collectortemp"] = (float)collectorTemp_ / 10;
}
if (Helpers::hasValue(bottomTemp_)) {
doc["bottomtemp"] = (float)bottomTemp_ / 10;
}
if (Helpers::hasValue(bottomTemp2_)) {
doc["bottomtemp2"] = (float)bottomTemp2_ / 10;
}
if (Helpers::hasValue(pumpModulation_)) {
doc["pumpmodulation"] = pumpModulation_;
}
if (Helpers::hasValue(pump_, true)) {
doc["pump"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(valveStatus_, true)) {
doc["valvestatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
}
if (Helpers::hasValue(pumpWorkMin_)) {
doc["pumpWorkMin"] = (float)pumpWorkMin_;
}
if (Helpers::hasValue(energyLastHour_)) {
doc["energylasthour"] = (float)energyLastHour_ / 10;
}
if (Helpers::hasValue(energyToday_)) {
doc["energytoday"] = energyToday_;
}
if (Helpers::hasValue(energyTotal_)) {
doc["energytotal"] = (float)energyTotal_ / 10;
}
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Performing a solar module publish"));
#endif
Mqtt::publish("sm_data", doc);
}
// check to see if values have been updated
bool Solar::updated_values() {
return false;
}
// add console commands
void Solar::console_commands() {
}
// SM10Monitor - type 0x97
void Solar::process_SM10Monitor(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(collectorTemp_, 2); // collector temp from SM10, is *10
telegram->read_value(bottomTemp_, 5); // bottom temp from SM10, is *10
telegram->read_value(pumpModulation_, 4); // modulation solar pump
telegram->read_bitvalue(pump_, 7, 1);
telegram->read_value(pumpWorkMin_, 8);
}
/*
* SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
* e.g. B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
* e.g, 30 00 FF 00 02 62 01 AC
* 30 00 FF 18 02 62 80 00
* 30 00 FF 00 02 62 01 A1 - for bottom temps
* bytes 0+1 = TS1 Temperature sensor for collector
* bytes 2+3 = TS2 Temperature sensor bottom cylinder 1
* bytes 16+17 = TS5 Temperature sensor bottom cylinder 2
*/
void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(collectorTemp_, 0); // is *10
telegram->read_value(bottomTemp_, 2); // is *10
telegram->read_value(bottomTemp2_, 16); // is *10
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
// SM100Monitor2 - 0x0363
// e.g. B0 00 FF 00 02 63 80 00 80 00 00 00 80 00 80 00 80 00 00 80 00 5A
void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
// not implemented yet
}
#pragma GCC diagnostic pop
// SM100Config - 0x0366
// e.g. B0 00 FF 00 02 66 01 62 00 13 40 14
void Solar::process_SM100Config(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(availabilityFlag_, 0);
telegram->read_value(configFlag_, 1);
telegram->read_value(userFlag_, 2);
}
/*
* SM100Status - type 0x0364 EMS+ for pump modulation - for SM100 and SM200
* e.g. 30 00 FF 09 02 64 64 = 100%
* 30 00 FF 09 02 64 1E = 30%
*/
void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
uint8_t pumpmod = pumpModulation_;
telegram->read_value(pumpModulation_, 9);
if (pumpmod == 0 && pumpModulation_ == 100) { // mask out boosts
pumpModulation_ = 15; // set to minimum
}
}
/*
* SM100Status2 - type 0x036A EMS+ for pump on/off at offset 0x0A - for SM100 and SM200
* e.g. B0 00 FF 00 02 6A 03 03 03 03 01 03 03 03 03 03 01 03
* byte 4 = VS2 3-way valve for cylinder 2 : test=01, on=04 and off=03
* byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3)
*/
void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
telegram->read_bitvalue(valveStatus_, 4, 2); // on if bit 2 set
telegram->read_bitvalue(pump_, 10, 2); // on if bit 2 set
}
/*
* SM100Energy - type 0x038E EMS+ for energy readings
* e.g. 30 00 FF 00 02 8E 00 00 00 00 00 00 06 C5 00 00 76 35
*/
void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(energyLastHour_, 0); // last hour / 10 in Wh
telegram->read_value(energyToday_, 4); // todays in Wh
telegram->read_value(energyTotal_, 8); // total / 10 in kWh
}
/*
* Junkers ISM1 Solar Module - type 0x0103 EMS+ for energy readings
* e.g. B0 00 FF 00 00 03 32 00 00 00 00 13 00 D6 00 00 00 FB D0 F0
*/
void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(collectorTemp_, 4); // Collector Temperature
telegram->read_value(bottomTemp_, 6); // Temperature Bottom of Solar Boiler
uint16_t Wh = 0xFFFF;
telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
if (Wh != 0xFFFF) {
energyLastHour_ = Wh * 10; // set to *10
}
telegram->read_bitvalue(pump_, 8, 0); // Solar pump on (1) or off (0)
telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes
}
/*
* Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values
* e.g. 90 30 FF 06 00 01 50
*/
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(setpoint_maxBottomTemp_, 6);
}
} // namespace emsesp

80
src/devices/solar.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_SOLAR_H
#define EMSESP_SOLAR_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Solar : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
int16_t collectorTemp_ = EMS_VALUE_SHORT_NOTSET; // collector temp (TS1)
int16_t bottomTemp_ = EMS_VALUE_SHORT_NOTSET; // bottom temp (TS2)
int16_t bottomTemp2_ = EMS_VALUE_SHORT_NOTSET; // bottom temp cylinder 2 (TS5)
uint8_t pumpModulation_ = EMS_VALUE_UINT_NOTSET; // modulation solar pump
uint8_t pump_ = EMS_VALUE_BOOL_NOTSET; // pump active
uint8_t valveStatus_ = EMS_VALUE_BOOL_NOTSET; // valve status (VS2)
int16_t setpoint_maxBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // setpoint for maximum collector temp
uint32_t energyLastHour_ = EMS_VALUE_ULONG_NOTSET;
uint32_t energyToday_ = EMS_VALUE_ULONG_NOTSET;
uint32_t energyTotal_ = EMS_VALUE_ULONG_NOTSET;
uint32_t pumpWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total solar pump operating time
uint8_t availabilityFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t configFlag_ = EMS_VALUE_BOOL_NOTSET;
uint8_t userFlag_ = EMS_VALUE_BOOL_NOTSET;
void process_SM10Monitor(std::shared_ptr<const Telegram> telegram);
void process_SM100Monitor(std::shared_ptr<const Telegram> telegram);
void process_SM100Monitor2(std::shared_ptr<const Telegram> telegram);
void process_SM100Config(std::shared_ptr<const Telegram> telegram);
void process_SM100Status(std::shared_ptr<const Telegram> telegram);
void process_SM100Status2(std::shared_ptr<const Telegram> telegram);
void process_SM100Energy(std::shared_ptr<const Telegram> telegram);
void process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram);
void process_ISM1Set(std::shared_ptr<const Telegram> telegram);
};
} // namespace emsesp
#endif

60
src/devices/switch.cpp Normal file
View File

@@ -0,0 +1,60 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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};
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("cmd", std::bind(&Controller::cmd, this, _1));
}
void Switch::add_context_menu() {
}
// 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
}
// publish values via MQTT
void Switch::publish_values() {
}
// check to see if values have been updated
bool Switch::updated_values() {
return false;
}
// add console commands
void Switch::console_commands() {
}
} // namespace emsesp

51
src/devices/switch.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_SWITCH_H
#define EMSESP_SWITCH_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "helpers.h"
#include "mqtt.h"
namespace emsesp {
class Switch : public EMSdevice {
public:
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);
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
private:
static uuid::log::Logger logger_;
void console_commands();
};
} // namespace emsesp
#endif

1888
src/devices/thermostat.cpp Normal file

File diff suppressed because it is too large Load Diff

272
src/devices/thermostat.h Normal file
View File

@@ -0,0 +1,272 @@
/*
* EMS-ESP - https://github.com/proddy/EMS-ESP
* Copyright 2019 Paul Derbyshire
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMSESP_THERMOSTAT_H
#define EMSESP_THERMOSTAT_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include <uuid/log.h>
#include "emsdevice.h"
#include "telegram.h"
#include "emsesp.h"
#include "helpers.h"
#include "mqtt.h"
#include <vector>
namespace emsesp {
class Thermostat : public EMSdevice {
public:
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);
class HeatingCircuit {
public:
HeatingCircuit(const uint8_t hc_num, const uint16_t monitor_typeid, const uint16_t set_typeid)
: hc_num_(hc_num)
, monitor_typeid_(monitor_typeid)
, set_typeid_(set_typeid) {
}
~HeatingCircuit() = default;
int16_t setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET;
int16_t curr_roomTemp = EMS_VALUE_SHORT_NOTSET;
uint8_t mode = EMS_VALUE_UINT_NOTSET;
uint8_t mode_type = EMS_VALUE_UINT_NOTSET;
uint8_t summer_mode = EMS_VALUE_UINT_NOTSET;
uint8_t holiday_mode = EMS_VALUE_UINT_NOTSET;
uint8_t daytemp = EMS_VALUE_UINT_NOTSET;
uint8_t nighttemp = EMS_VALUE_UINT_NOTSET;
uint8_t holidaytemp = EMS_VALUE_UINT_NOTSET;
uint8_t heatingtype = EMS_VALUE_UINT_NOTSET; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
uint8_t targetflowtemp = EMS_VALUE_UINT_NOTSET;
uint8_t summertemp = EMS_VALUE_UINT_NOTSET;
uint8_t nofrosttemp = EMS_VALUE_UINT_NOTSET;
uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heatingcurve design temp at MinExtTemp
int8_t offsettemp = EMS_VALUE_INT_NOTSET; // heatingcurve offest temp at roomtemp signed!
uint8_t hc_num() const {
return hc_num_; // 1..10
}
uint8_t get_mode(uint8_t flags) const;
uint8_t get_mode_type(uint8_t flags) const;
uint16_t monitor_typeid() const {
return monitor_typeid_;
}
uint16_t set_typeid() const {
return set_typeid_;
}
enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN, SUMMER };
// for sorting
friend inline bool operator<(const std::shared_ptr<HeatingCircuit> & lhs, const std::shared_ptr<HeatingCircuit> & rhs) {
return (lhs->hc_num_ < rhs->hc_num_);
}
private:
uint8_t hc_num_; // 1..10
uint16_t monitor_typeid_;
uint16_t set_typeid_;
};
std::string mode_tostring(uint8_t mode) const;
virtual void show_values(uuid::console::Shell & shell);
virtual void publish_values();
virtual bool updated_values();
virtual void add_context_menu();
bool can_write() const {
return ((flags() & EMSdevice::EMS_DEVICE_FLAG_NO_WRITE) == EMSdevice::EMS_DEVICE_FLAG_NO_WRITE);
}
// each thermostat has a list of heating controller type IDs for reading and writing
std::vector<uint16_t> monitor_typeids;
std::vector<uint16_t> set_typeids;
std::vector<uint16_t> timer_typeids;
private:
static uuid::log::Logger logger_;
void console_commands(Shell & shell, unsigned int context);
void init_mqtt();
std::string datetime_; // date and time stamp
uint8_t mqtt_format_; // single, nested or ha
// Installation parameters
uint8_t ibaMainDisplay_ =
EMS_VALUE_UINT_NOTSET; // display on Thermostat: 0 int. temp, 1 int. setpoint, 2 ext. temp., 3 burner temp., 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
uint8_t ibaLanguage_ = EMS_VALUE_UINT_NOTSET; // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian
int8_t ibaCalIntTemperature_ = EMS_VALUE_INT_NOTSET; // offset int. temperature sensor, by * 0.1 Kelvin (-5.0 to 5.0K)
int8_t ibaMinExtTemperature_ = EMS_VALUE_INT_NOTSET; // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1
uint8_t ibaBuildingType_ = EMS_VALUE_UINT_NOTSET; // building type: 0 = light, 1 = medium, 2 = heavy
uint8_t ibaClockOffset_ = EMS_VALUE_UINT_NOTSET; // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
int8_t dampedoutdoortemp_ = EMS_VALUE_INT_NOTSET;
uint16_t tempsensor1_ = EMS_VALUE_USHORT_NOTSET;
uint16_t tempsensor2_ = EMS_VALUE_USHORT_NOTSET;
uint8_t wwSystem_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwExtra_ = EMS_VALUE_UINT_NOTSET;
uint8_t wwMode_ = EMS_VALUE_UINT_NOTSET;
std::vector<std::shared_ptr<HeatingCircuit>> heating_circuits_; // each thermostat can have multiple heating circuits
// Generic Types
static constexpr uint16_t EMS_TYPE_RCTime = 0x06; // time
static constexpr uint16_t EMS_TYPE_RCOutdoorTemp = 0xA3; // is an automatic thermostat broadcast, outdoor external temp
// Type offsets
static constexpr uint8_t EMS_OFFSET_RC10StatusMessage_setpoint = 1; // setpoint temp
static constexpr uint8_t EMS_OFFSET_RC10StatusMessage_curr = 2; // current temp
static constexpr uint8_t EMS_OFFSET_RC10Set_temp = 4; // position of thermostat setpoint temperature
static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_setpoint = 1; // setpoint temp
static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_curr = 2; // current temp
static constexpr uint8_t EMS_OFFSET_RC20Set_mode = 23; // position of thermostat mode
static constexpr uint8_t EMS_OFFSET_RC20Set_temp = 28; // position of thermostat setpoint temperature
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_mode = 3; // ES72 - see https://github.com/proddy/EMS-ESP/issues/334
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_temp_night = 1; // ES72
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_temp_day = 2; // ES72
static constexpr uint8_t EMS_OFFSET_RC30StatusMessage_setpoint = 1; // setpoint temp
static constexpr uint8_t EMS_OFFSET_RC30StatusMessage_curr = 2; // current temp
static constexpr uint8_t EMS_OFFSET_RC30Set_mode = 23; // position of thermostat mode
static constexpr uint8_t EMS_OFFSET_RC30Set_temp = 28; // position of thermostat setpoint temperature
static constexpr uint8_t EMS_OFFSET_RC35StatusMessage_setpoint = 2; // desired temp
static constexpr uint8_t EMS_OFFSET_RC35StatusMessage_curr = 3; // current temp
static constexpr uint8_t EMS_OFFSET_RC35StatusMessage_mode = 1; // day mode, also summer on RC3's
static constexpr uint8_t EMS_OFFSET_RC35StatusMessage_mode1 = 0; // for holiday mode
static constexpr uint8_t EMS_OFFSET_RC35Set_mode = 7; // position of thermostat mode
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_day = 2; // position of thermostat setpoint temperature for day time
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_night = 1; // position of thermostat setpoint temperature for night time
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_holiday = 3; // temp during holiday mode
static constexpr uint8_t EMS_OFFSET_RC35Set_heatingtype = 0; // e.g. floor heating = 3
static constexpr uint8_t EMS_OFFSET_RC35Set_targetflowtemp = 14; // target flow temperature
static constexpr uint8_t EMS_OFFSET_RC35Set_seltemp = 37; // selected temp
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_offset = 6;
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_design = 17;
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_summer = 22;
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_nofrost = 23;
static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_setpoint = 10; // setpoint temp
static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_curr = 8; // current temp
static constexpr uint8_t EMS_OFFSET_RCPLUSStatusMessage_mode = 10; // thermostat mode (auto, manual)
static constexpr uint8_t EMS_OFFSET_RCPLUSStatusMessage_setpoint = 3; // setpoint temp
static constexpr uint8_t EMS_OFFSET_RCPLUSStatusMessage_curr = 0; // current temp
static constexpr uint8_t EMS_OFFSET_RCPLUSStatusMessage_currsetpoint = 6; // target setpoint temp
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_mode = 0; // operation mode(Auto=0xFF, Manual=0x00)
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_temp_comfort3 = 1; // comfort3 level
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_temp_comfort2 = 2; // comfort2 level
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_temp_comfort1 = 3; // comfort1 level
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_temp_eco = 4; // eco level
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_temp_setpoint = 8; // temp setpoint, when changing of templevel (in auto) value is reset to FF
static constexpr uint8_t EMS_OFFSET_RCPLUSSet_manual_setpoint = 10; // manual setpoint
static constexpr uint8_t EMS_OFFSET_JunkersStatusMessage_daymode = 0; // 3 = day, 2 = night, 1 = nofrost
static constexpr uint8_t EMS_OFFSET_JunkersStatusMessage_mode = 1; // current mode, 1 = manual, 2 = auto
static constexpr uint8_t EMS_OFFSET_JunkersStatusMessage_setpoint = 2; // setpoint temp
static constexpr uint8_t EMS_OFFSET_JunkersStatusMessage_curr = 4; // current temp
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage_day_temp = 17; // EMS offset to set temperature on thermostat for day mode
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage_night_temp = 16; // EMS offset to set temperature on thermostat for night mode
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage_no_frost_temp = 15; // EMS offset to set temperature on thermostat for no frost mode
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage_set_mode = 14; // EMS offset to set mode on thermostat
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage2_set_mode = 4; // EMS offset to set mode on thermostat
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage2_no_frost_temp = 5;
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage2_eco_temp = 6;
static constexpr uint8_t EMS_OFFSET_JunkersSetMessage3_heat = 7;
#define AUTO_HEATING_CIRCUIT 0
#define DEFAULT_HEATING_CIRCUIT 1
// Installation settings
static constexpr uint8_t EMS_TYPE_IBASettings = 0xA5; // installation settings
static constexpr uint8_t EMS_TYPE_wwSettings = 0x37; // ww settings
std::shared_ptr<Thermostat::HeatingCircuit> heating_circuit(std::shared_ptr<const Telegram> telegram);
std::shared_ptr<Thermostat::HeatingCircuit> heating_circuit(const uint8_t hc_num);
void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram);
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);
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);
void thermostat_cmd_temp(const char * message);
void thermostat_cmd_mode(const char * message);
};
} // namespace emsesp
#endif