added ems gateway & switch. moved all code to devices folder

This commit is contained in:
proddy
2020-06-06 20:55:42 +02:00
parent 80bae3bf33
commit 92a565151f
17 changed files with 343 additions and 309 deletions

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

@@ -0,0 +1,932 @@
/*
* 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, nullptr);
register_telegram_type(0x11, F("UBAErrorMessage2"), false, nullptr);
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(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));
register_telegram_type(0xE3, F("HeatPumpMonitor1"), false, std::bind(&Boiler::process_HPMonitor1, this, _1));
register_telegram_type(0xE5, F("HeatPumpMonitor2"), false, std::bind(&Boiler::process_HPMonitor2, 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;
}
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 (wWComfort_ == 0x00) {
doc["wWComfort"] = "Hot";
} else if (wWComfort_ == 0xD8) {
doc["wWComfort"] = "Eco";
} else if (wWComfort_ == 0xEC) {
doc["wWComfort"] = "Intelligent";
}
if (wWSelTemp_ != EMS_VALUE_UINT_NOTSET) {
doc["wWSelTemp"] = wWSelTemp_;
}
if (wWDisinfectTemp_ != EMS_VALUE_UINT_NOTSET) {
doc["wWDisinfectionTemp"] = wWDisinfectTemp_;
}
if (selFlowTemp_ != EMS_VALUE_UINT_NOTSET) {
doc["selFlowTemp"] = selFlowTemp_;
}
if (selBurnPow_ != EMS_VALUE_UINT_NOTSET) {
doc["selBurnPow"] = selBurnPow_;
}
if (curBurnPow_ != EMS_VALUE_UINT_NOTSET) {
doc["curBurnPow"] = curBurnPow_;
}
if (pumpMod_ != EMS_VALUE_UINT_NOTSET) {
doc["pumpMod"] = pumpMod_;
}
if (wWCircPump_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL);
}
if (wWCircPumpType_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCiPuType"] = wWCircPumpType_ ? "valve" : "pump";
}
if (wWCircPumpMode_ != EMS_VALUE_UINT_NOTSET) {
doc["wWCiPuMode"] = wWCircPumpMode_;
}
if (wWCirc_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (extTemp_ != EMS_VALUE_SHORT_NOTSET) {
doc["outdoorTemp"] = (float)extTemp_ / 10;
}
if (wWCurTmp_ != EMS_VALUE_USHORT_NOTSET) {
doc["wWCurTmp"] = (float)wWCurTmp_ / 10;
}
if (wWCurFlow_ != EMS_VALUE_UINT_NOTSET) {
doc["wWCurFlow"] = (float)wWCurFlow_ / 10;
}
if (curFlowTemp_ != EMS_VALUE_USHORT_NOTSET) {
doc["curFlowTemp"] = (float)curFlowTemp_ / 10;
}
if (retTemp_ != EMS_VALUE_USHORT_NOTSET) {
doc["retTemp"] = (float)retTemp_ / 10;
}
if (switchTemp_ != EMS_VALUE_USHORT_NOTSET) {
doc["switchTemp"] = (float)switchTemp_ / 10;
}
if (sysPress_ != EMS_VALUE_UINT_NOTSET) {
doc["sysPress"] = (float)sysPress_ / 10;
}
if (boilTemp_ != EMS_VALUE_USHORT_NOTSET) {
doc["boilTemp"] = (float)boilTemp_ / 10;
}
if (wwStorageTemp1_ != EMS_VALUE_USHORT_NOTSET) {
doc["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10;
}
if (wwStorageTemp2_ != EMS_VALUE_USHORT_NOTSET) {
doc["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10;
}
if (exhaustTemp_ != EMS_VALUE_USHORT_NOTSET) {
doc["exhaustTemp"] = (float)exhaustTemp_ / 10;
}
if (wWActivated_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
}
if (wWOneTime_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
}
if (wWDesinfecting_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWDesinfecting"] = Helpers::render_value(s, wWDesinfecting_, EMS_VALUE_BOOL);
}
if (wWReadiness_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
}
if (wWRecharging_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
}
if (wWTemperatureOK_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
}
if (wWCirc_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (burnGas_ != EMS_VALUE_BOOL_NOTSET) {
doc["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL);
}
if (flameCurr_ != EMS_VALUE_USHORT_NOTSET) {
doc["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
}
if (heatPmp_ != EMS_VALUE_BOOL_NOTSET) {
doc["heatPmp"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
}
if (fanWork_ != EMS_VALUE_BOOL_NOTSET) {
doc["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
}
if (ignWork_ != EMS_VALUE_BOOL_NOTSET) {
doc["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
}
if (wWHeat_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
}
if (heating_temp_ != EMS_VALUE_UINT_NOTSET) {
doc["heating_temp"] = heating_temp_;
}
if (pump_mod_max_ != EMS_VALUE_UINT_NOTSET) {
doc["pump_mod_max"] = pump_mod_max_;
}
if (pump_mod_min_ != EMS_VALUE_UINT_NOTSET) {
doc["pump_mod_min"] = pump_mod_min_;
}
if (wWStarts_ != EMS_VALUE_ULONG_NOTSET) {
doc["wWStarts"] = wWStarts_;
}
if (wWWorkM_ != EMS_VALUE_ULONG_NOTSET) {
doc["wWWorkM"] = wWWorkM_;
}
if (UBAuptime_ != EMS_VALUE_ULONG_NOTSET) {
doc["UBAuptime"] = UBAuptime_;
}
if (burnStarts_ != EMS_VALUE_ULONG_NOTSET) {
doc["burnStarts"] = burnStarts_;
}
if (burnWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
doc["burnWorkMin"] = burnWorkMin_;
}
if (heatWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
doc["heatWorkMin"] = heatWorkMin_;
}
if (serviceCode_ != EMS_VALUE_USHORT_NOTSET) {
doc["serviceCode"] = serviceCodeChar_;
doc["serviceCodeNumber"] = serviceCode_;
}
// heatpump specific
if (hpModulation_ != EMS_VALUE_UINT_NOTSET) {
doc["pumpmodulation"] = hpModulation_;
}
if (hpSpeed_ != EMS_VALUE_UINT_NOTSET) {
doc["pumpspeed"] = hpSpeed_;
}
#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
char buffer[10]; // used for formatting
if (tap_water_active_ != EMS_VALUE_BOOL_NOTSET) {
print_value(shell, 2, F("Hot tap water"), tap_water_active_ ? "running" : "off");
}
if (heating_active_ != EMS_VALUE_BOOL_NOTSET) {
print_value(shell, 2, F("Central heating"), heating_active_ ? "active" : "off");
}
print_value(shell, 2, F("Warm Water activated"), Helpers::render_value(buffer, wWActivated_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Warm Water charging type"), wWCircPumpType_ ? "3-way valve" : "charge pump");
print_value(shell, 2, F("Warm Water circulation pump available"), Helpers::render_value(buffer, wWCircPump_, EMS_VALUE_BOOL));
if (wWCircPumpMode_ == 7) {
print_value(shell, 2, F("Warm Water circulation pump freq"), "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"), Helpers::render_value(buffer, wWCirc_, EMS_VALUE_BOOL));
if (wWComfort_ == 0x00) {
print_value(shell, 2, F("Warm Water comfort setting"), "Hot");
} else if (wWComfort_ == 0xD8) {
print_value(shell, 2, F("Warm Water comfort setting"), "Eco");
} else if (wWComfort_ == 0xEC) {
print_value(shell, 2, F("Warm Water comfort setting"), "Intelligent");
}
print_value(shell, 2, F("Warm Water disinfection temperature"), F_(degrees), Helpers::render_value(buffer, wWDisinfectTemp_, 1));
print_value(shell, 2, F("Warm Water selected temperature"), F_(degrees), Helpers::render_value(buffer, wWSelTemp_, 1));
print_value(shell, 2, F("Warm Water set temperature"), F_(degrees), Helpers::render_value(buffer, wWSetTmp_, 1));
print_value(shell, 2, F("Warm Water current temperature (intern)"), F_(degrees), Helpers::render_value(buffer, wWCurTmp_, 10));
print_value(shell, 2, F("Warm water storage temperature (intern)"), F_(degrees), Helpers::render_value(buffer, wwStorageTemp1_, 10));
print_value(shell, 2, F("Warm Water current temperature (extern)"), F_(degrees), Helpers::render_value(buffer, wWCurTmp2_, 10));
print_value(shell, 2, F("Warm water storage temperature (extern)"), F_(degrees), Helpers::render_value(buffer, wwStorageTemp2_, 10));
print_value(shell, 2, F("Warm Water current tap water flow"), F("l/min"), Helpers::render_value(buffer, wWCurFlow_, 10));
print_value(shell, 2, F("Warm Water # starts"), Helpers::render_value(buffer, wWStarts_, 1));
if (wWWorkM_ != EMS_VALUE_ULONG_NOTSET) {
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"), Helpers::render_value(buffer, wWHeat_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Warm Water disinfecting"), Helpers::render_value(buffer, wWDesinfecting_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Selected flow temperature"), F_(degrees), Helpers::render_value(buffer, selFlowTemp_, 1));
print_value(shell, 2, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, curFlowTemp_, 10));
print_value(shell, 2, F("Max boiler temperature"), F_(degrees), Helpers::render_value(buffer, boilTemp_, 10));
print_value(shell, 2, F("Return temperature"), F_(degrees), Helpers::render_value(buffer, retTemp_, 10));
print_value(shell, 2, F("Gas"), Helpers::render_value(buffer, burnGas_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Boiler pump"), Helpers::render_value(buffer, heatPmp_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Fan"), Helpers::render_value(buffer, fanWork_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Ignition"), Helpers::render_value(buffer, ignWork_, EMS_VALUE_BOOL));
print_value(shell, 2, F("Burner selected max power"), F_(percent), Helpers::render_value(buffer, selBurnPow_, 1));
print_value(shell, 2, F("Burner current power"), F_(percent), Helpers::render_value(buffer, curBurnPow_, 1));
print_value(shell, 2, F("Flame current"), F("uA"), Helpers::render_value(buffer, flameCurr_, 10));
print_value(shell, 2, F("System pressure"), F("bar"), Helpers::render_value(buffer, sysPress_, 10));
if (serviceCode_ != EMS_VALUE_USHORT_NOTSET) {
shell.printfln(F(" System service code: %s (%d)"), serviceCodeChar_, serviceCode_);
} else if (serviceCodeChar_[0] != '\0') {
shell.printfln(F(" System service code: %s"), serviceCodeChar_);
}
// UBAParameters
print_value(shell, 2, F("Heating temperature setting on the boiler"), F_(degrees), Helpers::render_value(buffer, heating_temp_, 1));
print_value(shell, 2, F("Boiler circuit pump modulation max power"), F_(percent), Helpers::render_value(buffer, pump_mod_max_, 1));
print_value(shell, 2, F("Boiler circuit pump modulation min power"), F_(percent), Helpers::render_value(buffer, pump_mod_min_, 1));
// UBAMonitorSlow
if (extTemp_ != EMS_VALUE_SHORT_NOTSET) {
print_value(shell, 2, F("Outside temperature"), F_(degrees), Helpers::render_value(buffer, extTemp_, 10));
}
print_value(shell, 2, F("Exhaust temperature"), F_(degrees), Helpers::render_value(buffer, exhaustTemp_, 10));
print_value(shell, 2, F("Pump modulation"), F_(percent), Helpers::render_value(buffer, pumpMod_, 1));
print_value(shell, 2, F("Burner # starts"), Helpers::render_value(buffer, burnStarts_, 1));
if (burnWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
shell.printfln(F(" Total burner operating time: %d days %d hours %d minutes"), burnWorkMin_ / 1440, (burnWorkMin_ % 1440) / 60, burnWorkMin_ % 60);
}
if (heatWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
shell.printfln(F(" Total heat operating time: %d days %d hours %d minutes"), heatWorkMin_ / 1440, (heatWorkMin_ % 1440) / 60, heatWorkMin_ % 60);
}
if (UBAuptime_ != EMS_VALUE_ULONG_NOTSET) {
shell.printfln(F(" Total UBA working time: %d days %d hours %d minutes"), UBAuptime_ / 1440, (UBAuptime_ % 1440) / 60, UBAuptime_ % 60);
}
if (hpModulation_ != EMS_VALUE_UINT_NOTSET) {
print_value(shell, 2, F("Heat Pump modulation"), F_(percent), Helpers::render_value(buffer, hpModulation_, 1));
}
if (hpSpeed_ != EMS_VALUE_UINT_NOTSET) {
print_value(shell, 2, F("Heat Pump speed"), F_(percent), Helpers::render_value(buffer, hpSpeed_, 1));
}
}
/*
* 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 (wWCurFlow_ != EMS_VALUE_UINT_NOTSET && burnGas_ != EMS_VALUE_UINT_NOTSET) {
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 (selFlowTemp_ != EMS_VALUE_UINT_NOTSET && burnGas_ != EMS_VALUE_UINT_NOTSET) {
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 ((tap_water_active_ != EMS_VALUE_BOOL_NOTSET) && (heating_active_ != EMS_VALUE_BOOL_NOTSET)) {
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_value(burnGas_, 7, 0);
telegram->read_value(fanWork_, 7, 2);
telegram->read_value(ignWork_, 7, 3);
telegram->read_value(heatPmp_, 7, 5);
telegram->read_value(wWHeat_, 7, 6);
telegram->read_value(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);
}
/*
* 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);
telegram->read_value(wWStarts_, 13);
telegram->read_value(wWOneTime_, 5, 1);
telegram->read_value(wWDesinfecting_, 5, 2);
telegram->read_value(wWReadiness_, 5, 3);
telegram->read_value(wWRecharging_, 5, 4);
telegram->read_value(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_value(burnGas_, 11, 0);
telegram->read_value(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);
telegram->read_value(burnWorkMin_, 13);
telegram->read_value(heatWorkMin_, 19);
}
/*
* UBAMonitorSlowPlus - type 0xE5 - central heating monitor EMS+
*/
void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(fanWork_, 2, 2);
telegram->read_value(ignWork_, 2, 3);
telegram->read_value(heatPmp_, 2, 5);
telegram->read_value(wWCirc_, 2, 7);
telegram->read_value(burnStarts_, 10);
telegram->read_value(burnWorkMin_, 13);
telegram->read_value(heatWorkMin_, 19);
telegram->read_value(pumpMod_, 25); // or is it switchTemp ?
}
/*
* Type 0xE3 - HeatPump Monitor 1
*/
void Boiler::process_HPMonitor1(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(hpModulation_, 13);
}
/*
* Type 0xE5 - HeatPump Monitor 2
*/
void Boiler::process_HPMonitor2(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(hpSpeed_, 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);
telegram->read_value(wWStarts_, 14);
telegram->read_value(wWOneTime_, 12, 2);
telegram->read_value(wWDesinfecting_, 12, 3);
telegram->read_value(wWReadiness_, 12, 4);
telegram->read_value(wWRecharging_, 13, 4);
telegram->read_value(wWTemperatureOK_, 13, 5);
telegram->read_value(wWActivated_, 20);
telegram->read_value(wWCircPump_, 13, 2);
telegram->read_value(wWSelTemp_, 10);
telegram->read_value(wWDisinfectTemp_, 9);
}
/*
* 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) {
}
// 0x2A
// not yet implemented
void Boiler::process_MC10Status(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) {
}
#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);
}
// 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
void Boiler::set_warmwater_mode(const uint8_t comfort) {
if (comfort == 1) {
LOG_INFO(F("Setting boiler warm water to hot"));
} else if (comfort == 2) {
LOG_INFO(F("Setting boiler warm water to eco"));
} else if (comfort == 3) {
LOG_INFO(F("Setting boiler warm water to intelligent"));
}
write_command(EMS_TYPE_UBAParameterWW, 9, comfort);
}
// turn on/off warm water
void Boiler::set_warmwater_activated(const bool activated) {
LOG_INFO(F("Setting boiler warm water %s"), activated ? "on" : "off");
uint8_t value;
// 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_(change), 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_(change), 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_(change), 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_(change), 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_(change), 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_(change), 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);
}
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); });
EMSESPShell::commands->add_command(
ShellContext::BOILER,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(shower), F_(timer)},
flash_string_vector{F_(bool_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
bool value;
if (arguments[0] == read_flash_string(F_(on))) {
value = true;
} else if (arguments[0] == read_flash_string(F_(off))) {
value = false;
} else {
shell.println(F("Must be on or off"));
return;
}
Settings settings;
settings.shower_timer(value);
settings.commit();
shell.printfln(F_(shower_timer_fmt), settings.shower_timer() ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str());
},
[](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_(set), F_(shower), F_(alert)},
flash_string_vector{F_(bool_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) {
bool value;
if (arguments[0] == read_flash_string(F_(on))) {
value = true;
} else if (arguments[0] == read_flash_string(F_(off))) {
value = false;
} else {
shell.println(F("Must be on or off"));
return;
}
Settings settings;
settings.shower_alert(value);
settings.commit();
shell.printfln(F_(shower_timer_fmt), settings.shower_alert() ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str());
},
[](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::USER,
flash_string_vector{F_(set)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
Settings settings;
shell.printfln(F_(shower_timer_fmt), settings.shower_timer() ? F_(enabled) : F_(disabled));
shell.printfln(F_(shower_alert_fmt), settings.shower_alert() ? F_(enabled) : F_(disabled));
shell.println();
});
// enter the context
Console::enter_custom_context(shell, context);
}
} // namespace emsesp

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

@@ -0,0 +1,165 @@
/*
* 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
// 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
// heatpump boilers
uint8_t hpModulation_ = EMS_VALUE_UINT_NOTSET; // heatpump modulation in %
uint8_t hpSpeed_ = EMS_VALUE_UINT_NOTSET; // speed 0-100 %
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_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_UBADHWStatus(std::shared_ptr<const Telegram> telegram);
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
void process_HPMonitor2(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

60
src/devices/connect.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 "connect.h"
// MAKE_PSTR_WORD(connect)
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

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

@@ -0,0 +1,52 @@
/*
* 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 "emsesp.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,61 @@
/*
* 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

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

@@ -0,0 +1,52 @@
/*
* 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 "emsesp.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

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

@@ -0,0 +1,52 @@
/*
* 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 "emsesp.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

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

@@ -0,0 +1,55 @@
/*
* 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 "emsesp.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

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

@@ -0,0 +1,164 @@
/*
* 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 < 0x28) {
// 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, nullptr);
register_telegram_type(0x00AB, F("MMStatusMessage"), true, std::bind(&Mixing::process_MMStatusMessage, this, _1));
register_telegram_type(0x00AC, F("MMSetMessage"), false, nullptr);
}
// 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
}
char buffer[10]; // used for formatting
if (type_ == Type::WWC) {
shell.printfln(F(" Warm Water Circuit #: %d"), hc_);
} else {
shell.printfln(F(" Heating Circuit #: %d"), hc_);
}
print_value(shell, 4, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, flowTemp_, 10));
print_value(shell, 4, F("Setpoint flow temperature"), F_(degrees), Helpers::render_value(buffer, flowSetTemp_, 1));
print_value(shell, 4, F("Current pump modulation"), Helpers::render_value(buffer, pumpMod_, 1));
print_value(shell, 4, F("Current valve status"), Helpers::render_value(buffer, status_, 1));
}
// 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 (flowTemp_ != EMS_VALUE_USHORT_NOTSET) {
doc["flowTemp"] = (float)flowTemp_ / 10;
}
if (pumpMod_ != EMS_VALUE_UINT_NOTSET) {
doc["pumpMod"] = pumpMod_;
}
if (status_ != EMS_VALUE_UINT_NOTSET) {
doc["status"] = status_;
}
if (flowSetTemp_ != EMS_VALUE_UINT_NOTSET) {
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...
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(pumpMod_, 5);
telegram->read_value(status_, 2); // 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 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);
}
} // namespace emsesp

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

@@ -0,0 +1,70 @@
/*
* 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 "emsesp.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_MMStatusMessage(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