mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 16:59:50 +03:00
a6
This commit is contained in:
119
src/boiler.cpp
119
src/boiler.cpp
@@ -55,14 +55,14 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
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"), true, std::bind(&Boiler::process_UBATotalUptime, 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(0xE4, F("UBAMonitorFastPlus"), true, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1));
|
||||
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), true, 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"), true, std::bind(&Boiler::process_HPMonitor1, this, _1));
|
||||
@@ -82,8 +82,7 @@ void Boiler::add_context_menu() {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(boiler)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::BOILER);
|
||||
console_commands();
|
||||
Boiler::console_commands(shell, ShellContext::BOILER);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -316,7 +315,10 @@ void Boiler::publish_values() {
|
||||
DEBUG_LOG(F("[DEBUG] Performing a boiler publish"));
|
||||
#endif
|
||||
|
||||
Mqtt::publish("boiler_data", doc);
|
||||
// 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
|
||||
@@ -330,22 +332,22 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
|
||||
char buffer[10]; // used for formatting
|
||||
|
||||
print_value(shell, F("Selected flow temperature"), F_(degrees), Helpers::render_value(buffer, selFlowTemp_, 1));
|
||||
print_value(shell, F("Warm Water selected temperature"), F_(degrees), Helpers::render_value(buffer, wWSelTemp_, 1));
|
||||
print_value(shell, 2, F("Selected flow temperature"), F_(degrees), Helpers::render_value(buffer, selFlowTemp_, 1));
|
||||
print_value(shell, 2, F("Warm Water selected temperature"), F_(degrees), Helpers::render_value(buffer, wWSelTemp_, 1));
|
||||
|
||||
if (tap_water_active_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
print_value(shell, F("Hot tap water"), tap_water_active_ ? "running" : "off");
|
||||
print_value(shell, 2, F("Hot tap water"), tap_water_active_ ? "running" : "off");
|
||||
}
|
||||
|
||||
if (heating_active_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
print_value(shell, F("Central heating"), heating_active_ ? "active" : "off");
|
||||
print_value(shell, 2, F("Central heating"), heating_active_ ? "active" : "off");
|
||||
}
|
||||
|
||||
print_value(shell, F("Warm Water activated"), Helpers::render_value(buffer, wWActivated_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Warm Water circulation pump available"), Helpers::render_value(buffer, wWCircPump_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Warm Water circulation pump type"), wWCircPumpType_ ? "3-way pump" : "charge pump");
|
||||
print_value(shell, 2, F("Warm Water activated"), Helpers::render_value(buffer, wWActivated_, EMS_VALUE_BOOL));
|
||||
print_value(shell, 2, F("Warm Water circulation pump available"), Helpers::render_value(buffer, wWCircPump_, EMS_VALUE_BOOL));
|
||||
print_value(shell, 2, F("Warm Water circulation pump type"), wWCircPumpType_ ? "3-way pump" : "charge pump");
|
||||
if (wWCircPumpMode_ == 7) {
|
||||
print_value(shell, F("Warm Water circulation pump freq"), "continuous");
|
||||
print_value(shell, 2, F("Warm Water circulation pump freq"), "continuous");
|
||||
} else {
|
||||
char s[7];
|
||||
char buffer[2];
|
||||
@@ -353,64 +355,64 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
buffer[1] = '\0';
|
||||
strlcpy(s, buffer, 7);
|
||||
strlcat(s, "x3min", 7);
|
||||
print_value(shell, F("Warm Water circulation pump freq"), s);
|
||||
print_value(shell, 2, F("Warm Water circulation pump freq"), s);
|
||||
}
|
||||
|
||||
if (wWComfort_ == 0x00) {
|
||||
print_value(shell, F("Warm Water comfort setting"), "Hot");
|
||||
print_value(shell, 2, F("Warm Water comfort setting"), "Hot");
|
||||
} else if (wWComfort_ == 0xD8) {
|
||||
print_value(shell, F("Warm Water comfort setting"), "Eco");
|
||||
print_value(shell, 2, F("Warm Water comfort setting"), "Eco");
|
||||
} else if (wWComfort_ == 0xEC) {
|
||||
print_value(shell, F("Warm Water comfort setting"), "Intelligent");
|
||||
print_value(shell, 2, F("Warm Water comfort setting"), "Intelligent");
|
||||
}
|
||||
|
||||
print_value(shell, F("Warm Water selected temperature"), F_(degrees), Helpers::render_value(buffer, wWSelTemp_, 1));
|
||||
print_value(shell, F("Warm Water disinfection temperature"), F_(degrees), Helpers::render_value(buffer, wWDisinfectTemp_, 1));
|
||||
print_value(shell, F("Warm Water circulation active"), F_(degrees), Helpers::render_value(buffer, wWCirc_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Warm Water set temperature"), F_(degrees), Helpers::render_value(buffer, wWSetTmp_, 1));
|
||||
print_value(shell, F("Warm Water current temperature"), F_(degrees), Helpers::render_value(buffer, wWCurTmp_, 10));
|
||||
print_value(shell, F("Warm water temperature (intern)"), F_(degrees), Helpers::render_value(buffer, wwStorageTemp1_, 10));
|
||||
print_value(shell, F("Warm water temperature (extern)"), F_(degrees), Helpers::render_value(buffer, wwStorageTemp2_, 10));
|
||||
print_value(shell, F("Warm Water current temperature (extern)"), F_(degrees), Helpers::render_value(buffer, wWCurTmp2_, 10));
|
||||
print_value(shell, F("Warm Water current tap water flow"), F("l/min"), Helpers::render_value(buffer, wWCurFlow_, 10));
|
||||
print_value(shell, F("Warm Water # starts"), Helpers::render_value(buffer, wWStarts_, 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 disinfection temperature"), F_(degrees), Helpers::render_value(buffer, wWDisinfectTemp_, 1));
|
||||
print_value(shell, 2, F("Warm Water circulation active"), F_(degrees), Helpers::render_value(buffer, wWCirc_, EMS_VALUE_BOOL));
|
||||
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"), F_(degrees), Helpers::render_value(buffer, wWCurTmp_, 10));
|
||||
print_value(shell, 2, F("Warm water temperature (intern)"), F_(degrees), Helpers::render_value(buffer, wwStorageTemp1_, 10));
|
||||
print_value(shell, 2, F("Warm water temperature (extern)"), F_(degrees), Helpers::render_value(buffer, wwStorageTemp2_, 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 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, F("Warm Water 3-way valve"), Helpers::render_value(buffer, wWHeat_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Selected flow temperature"), F_(degrees), Helpers::render_value(buffer, selFlowTemp_, 1));
|
||||
print_value(shell, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, curFlowTemp_, 10));
|
||||
print_value(shell, F("Max boiler temperature"), F_(degrees), Helpers::render_value(buffer, boilTemp_, 10));
|
||||
print_value(shell, F("Return temperature"), F_(degrees), Helpers::render_value(buffer, retTemp_, 10));
|
||||
print_value(shell, F("Gas"), Helpers::render_value(buffer, burnGas_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Boiler pump"), Helpers::render_value(buffer, heatPmp_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Fan"), Helpers::render_value(buffer, fanWork_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Ignition"), Helpers::render_value(buffer, ignWork_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Circulation pump"), Helpers::render_value(buffer, wWCirc_, EMS_VALUE_BOOL));
|
||||
print_value(shell, 2, F("Warm Water 3-way valve"), Helpers::render_value(buffer, wWHeat_, 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("Circulation pump"), Helpers::render_value(buffer, wWCirc_, EMS_VALUE_BOOL));
|
||||
|
||||
print_value(shell, F("Burner selected max power"), F_(percent), Helpers::render_value(buffer, selBurnPow_, 1));
|
||||
print_value(shell, F("Burner current power"), F_(percent), Helpers::render_value(buffer, curBurnPow_, 1));
|
||||
print_value(shell, F("Flame current"), F("uA"), Helpers::render_value(buffer, flameCurr_, 10));
|
||||
print_value(shell, F("System pressure"), F("bar"), Helpers::render_value(buffer, sysPress_, 10));
|
||||
if (serviceCode_ == EMS_VALUE_USHORT_NOTSET) {
|
||||
shell.printfln(F(" System service code: %s"), serviceCodeChar_);
|
||||
} else {
|
||||
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, F("Heating temperature setting on the boiler"), F_(degrees), Helpers::render_value(buffer, heating_temp_, 1));
|
||||
print_value(shell, F("Boiler circuit pump modulation max power"), F_(percent), Helpers::render_value(buffer, pump_mod_max_, 1));
|
||||
print_value(shell, F("Boiler circuit pump modulation min power"), F_(percent), Helpers::render_value(buffer, pump_mod_min_, 1));
|
||||
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, F("Outside temperature"), F_(degrees), Helpers::render_value(buffer, extTemp_, 10));
|
||||
print_value(shell, 2, F("Outside temperature"), F_(degrees), Helpers::render_value(buffer, extTemp_, 10));
|
||||
}
|
||||
|
||||
print_value(shell, F("Exhaust temperature"), F_(degrees), Helpers::render_value(buffer, exhaustTemp_, 10));
|
||||
print_value(shell, F("Pump modulation"), F_(percent), Helpers::render_value(buffer, pumpMod_, 1));
|
||||
print_value(shell, F("Burner # starts"), Helpers::render_value(buffer, burnStarts_, 1));
|
||||
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);
|
||||
}
|
||||
@@ -422,11 +424,11 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
if (hpModulation_ != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, F("Heat Pump modulation"), F_(percent), Helpers::render_value(buffer, hpModulation_, 1));
|
||||
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, F("Heat Pump speed"), F_(percent), Helpers::render_value(buffer, hpSpeed_, 1));
|
||||
print_value(shell, 2, F("Heat Pump speed"), F_(percent), Helpers::render_value(buffer, hpSpeed_, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,6 +547,7 @@ void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
/*
|
||||
* 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);
|
||||
@@ -562,8 +565,6 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
|
||||
serviceCodeChar_[2] = '\0';
|
||||
}
|
||||
|
||||
// still to figure out: serviceCode, retTemp, sysPress
|
||||
|
||||
// at this point do a quick check to see if the hot water or heating is active
|
||||
check_active();
|
||||
}
|
||||
@@ -754,7 +755,7 @@ void Boiler::set_warmwater_circulation(const bool activated) {
|
||||
}
|
||||
|
||||
// add console commands
|
||||
void Boiler::console_commands() {
|
||||
void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(read)},
|
||||
@@ -920,6 +921,10 @@ void Boiler::console_commands() {
|
||||
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
|
||||
|
||||
36
src/boiler.h
36
src/boiler.h
@@ -46,7 +46,7 @@ class Boiler : public EMSdevice {
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void console_commands();
|
||||
void console_commands(Shell & shell, unsigned int context);
|
||||
|
||||
uint8_t last_boilerState = 0xFF; // remember last state of heating and warm water on/off
|
||||
|
||||
@@ -67,23 +67,23 @@ class Boiler : public EMSdevice {
|
||||
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]; // 2 character status/service code
|
||||
uint16_t serviceCode_ = EMS_VALUE_USHORT_NOTSET; // error/service code
|
||||
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
|
||||
|
||||
165
src/console.cpp
165
src/console.cpp
@@ -21,7 +21,11 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
static std::shared_ptr<Commands> commands = std::make_shared<Commands>();
|
||||
std::shared_ptr<Commands> EMSESPShell::commands = [] {
|
||||
std::shared_ptr<Commands> commands = std::make_shared<Commands>();
|
||||
return commands;
|
||||
}();
|
||||
|
||||
static std::shared_ptr<EMSESPShell> shell;
|
||||
|
||||
std::vector<bool> EMSESPStreamConsole::ptys_;
|
||||
@@ -32,11 +36,6 @@ uuid::telnet::TelnetService telnet_([](Stream & stream, const IPAddress & addr,
|
||||
});
|
||||
#endif
|
||||
|
||||
std::shared_ptr<Commands> EMSESPShell::commands = [] {
|
||||
std::shared_ptr<Commands> commands = std::make_shared<Commands>();
|
||||
return commands;
|
||||
}();
|
||||
|
||||
EMSESPShell::EMSESPShell()
|
||||
: Shell() {
|
||||
}
|
||||
@@ -125,7 +124,7 @@ void EMSESPShell::add_console_commands() {
|
||||
});
|
||||
|
||||
/*
|
||||
* add the submenu contexts...
|
||||
* add all the submenu contexts...
|
||||
*/
|
||||
|
||||
// MQTT
|
||||
@@ -133,8 +132,7 @@ void EMSESPShell::add_console_commands() {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(mqtt)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::MQTT);
|
||||
Mqtt::console_commands();
|
||||
Mqtt::console_commands(shell, ShellContext::MQTT);
|
||||
});
|
||||
|
||||
// EMS
|
||||
@@ -142,8 +140,7 @@ void EMSESPShell::add_console_commands() {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(ems)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::EMS);
|
||||
EMSESP::console_commands();
|
||||
EMSESP::console_commands(shell, ShellContext::EMS);
|
||||
});
|
||||
|
||||
// System
|
||||
@@ -151,15 +148,14 @@ void EMSESPShell::add_console_commands() {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(system)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::SYSTEM);
|
||||
System::console_commands();
|
||||
System::console_commands(shell, ShellContext::SYSTEM);
|
||||
});
|
||||
|
||||
// add all the context menus for the connected devices
|
||||
// this assumes they devices have been detected and registered
|
||||
EMSESP::add_context_menu();
|
||||
// this assumes they devices have been detected and pre-registered
|
||||
EMSESP::add_context_menus();
|
||||
|
||||
enter_custom_context(ShellContext::MAIN); // add su, exit and help
|
||||
Console::load_standard_commands(ShellContext::MAIN);
|
||||
|
||||
_console_commands_loaded = true;
|
||||
}
|
||||
@@ -199,17 +195,29 @@ bool EMSESPShell::exit_context() {
|
||||
return Shell::exit_context();
|
||||
}
|
||||
|
||||
// enter a custom context (sub-menu)
|
||||
void Console::enter_custom_context(Shell & shell, unsigned int context) {
|
||||
load_standard_commands(context);
|
||||
|
||||
// don't enter context if we're already at the root
|
||||
if (context != ShellContext::MAIN) {
|
||||
shell.enter_context(context);
|
||||
}
|
||||
}
|
||||
|
||||
// each custom context has the common commands like log, help, exit, su etc
|
||||
void EMSESPShell::enter_custom_context(unsigned int context) {
|
||||
void Console::load_standard_commands(unsigned int context) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(test)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::run_test(shell, arguments.front()); });
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(test)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
EMSESP::run_test(shell, arguments.front());
|
||||
});
|
||||
#endif
|
||||
|
||||
commands->add_command(
|
||||
EMSESPShell::commands->add_command(
|
||||
context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(log)},
|
||||
@@ -229,7 +237,7 @@ void EMSESPShell::enter_custom_context(unsigned int context) {
|
||||
uint16_t watch_id = 0; // no watch ID set
|
||||
if ((arguments.size() == 2) && (level == uuid::log::Level::TRACE)) {
|
||||
watch_id = Helpers::hextoint(arguments[1].c_str());
|
||||
shell.printfln(("Tracing only telegrams that match a device ID/telegram type ID of 0x%02X"), watch_id);
|
||||
shell.printfln(("Tracing only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
|
||||
}
|
||||
emsesp::EMSESP::trace_watch_id(watch_id);
|
||||
}
|
||||
@@ -239,68 +247,61 @@ void EMSESPShell::enter_custom_context(unsigned int context) {
|
||||
return uuid::log::levels_lowercase();
|
||||
});
|
||||
|
||||
commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(help)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { shell.print_all_available_commands(); });
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(help)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.print_all_available_commands();
|
||||
});
|
||||
|
||||
commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(exit)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { shell.exit_context(); });
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(exit)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
// delete MAIN console stuff first to save memory
|
||||
EMSESPShell::commands->remove_context_commands(context);
|
||||
shell.exit_context();
|
||||
});
|
||||
|
||||
commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(su)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
auto become_admin = [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE,
|
||||
LogFacility::AUTH,
|
||||
F("Admin session opened on console %s"),
|
||||
dynamic_cast<EMSESPShell &>(shell).console_name().c_str());
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
};
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(su)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
auto become_admin = [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Admin session opened on console"));
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
};
|
||||
|
||||
if (shell.has_flags(CommandFlags::LOCAL)) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
uint64_t now = uuid::get_uptime_ms();
|
||||
if (shell.has_flags(CommandFlags::LOCAL)) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
uint64_t now = uuid::get_uptime_ms();
|
||||
|
||||
if (!password.empty() && password == Settings().admin_password()) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE,
|
||||
LogFacility::AUTH,
|
||||
F("Invalid admin password on console %s"),
|
||||
dynamic_cast<EMSESPShell &>(shell).console_name().c_str());
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!password.empty() && password == Settings().admin_password()) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid admin password on console"));
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(debug)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET);
|
||||
Settings settings;
|
||||
settings.commit();
|
||||
settings.show_settings(shell);
|
||||
shell.println();
|
||||
});
|
||||
EMSESPShell::commands->add_command(
|
||||
context, CommandFlags::ADMIN, flash_string_vector{F_(debug)}, [&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET);
|
||||
Settings settings;
|
||||
settings.commit();
|
||||
settings.show_settings(shell);
|
||||
shell.println();
|
||||
});
|
||||
#endif
|
||||
|
||||
// don't enter context if we're at the root
|
||||
if (context != ShellContext::MAIN) {
|
||||
Shell::enter_context(context);
|
||||
}
|
||||
}
|
||||
|
||||
// prompt, change per context
|
||||
@@ -339,8 +340,6 @@ std::string EMSESPShell::prompt_suffix() {
|
||||
}
|
||||
|
||||
void EMSESPShell::end_of_transmission() {
|
||||
// delete MAIN console stuff
|
||||
commands->remove_context_commands(ShellContext::MAIN);
|
||||
invoke_command(uuid::read_flash_string(F_(exit)));
|
||||
}
|
||||
|
||||
@@ -415,7 +414,7 @@ void Console::start() {
|
||||
// note, this must be started after the network/wifi for ESP32 otherwise it'll crash
|
||||
#ifndef EMSESP_STANDALONE
|
||||
telnet_.start();
|
||||
// telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second
|
||||
telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -428,8 +427,6 @@ void Console::loop() {
|
||||
#endif
|
||||
|
||||
Shell::loop_all();
|
||||
|
||||
// delay(0); // in EMS-ESP 1.9.5 this helped with stability
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -125,7 +125,13 @@ namespace emsesp {
|
||||
using LogLevel = ::uuid::log::Level;
|
||||
using LogFacility = ::uuid::log::Facility;
|
||||
|
||||
enum CommandFlags : uint8_t { USER = 0, ADMIN = (1 << 0), LOCAL = (1 << 1) };
|
||||
enum CommandFlags : uint8_t {
|
||||
|
||||
USER = 0,
|
||||
ADMIN = (1 << 0),
|
||||
LOCAL = (1 << 1)
|
||||
|
||||
};
|
||||
|
||||
enum ShellContext : uint8_t {
|
||||
|
||||
@@ -147,8 +153,6 @@ class EMSESPShell : virtual public uuid::console::Shell {
|
||||
static std::shared_ptr<uuid::console::Commands> commands;
|
||||
static std::shared_ptr<EMSESPShell> shell;
|
||||
|
||||
void enter_custom_context(unsigned int context);
|
||||
|
||||
protected:
|
||||
EMSESPShell();
|
||||
|
||||
@@ -191,6 +195,9 @@ class Console {
|
||||
|
||||
uuid::log::Level log_level();
|
||||
|
||||
static void enter_custom_context(Shell & shell, unsigned int context);
|
||||
static void load_standard_commands(unsigned int context);
|
||||
|
||||
private:
|
||||
static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200;
|
||||
static constexpr auto & serial_console_ = Serial;
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{208, DeviceType::BOILER, F("Logamax plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 64, DeviceType::BOILER, F("BK13,BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
@@ -37,6 +36,7 @@
|
||||
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 72, DeviceType::BOILER, F("MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Solar Modules - 0x30
|
||||
{ 73, DeviceType::SOLAR, F("SM10"), DeviceFlags::EMS_DEVICE_FLAG_SM10},
|
||||
@@ -56,29 +56,30 @@
|
||||
{200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Switches - 0x11
|
||||
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x11
|
||||
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x11
|
||||
|
||||
// Controllers - 0x09 / 0x10
|
||||
{ 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 89, DeviceType::CONTROLLER, F("BC"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
|
||||
{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},// 0x09
|
||||
{114, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},// 0x09
|
||||
{125, DeviceType::CONTROLLER, F("BC25"), DeviceFlags::EMS_DEVICE_FLAG_NONE},// 0x09
|
||||
{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE},// 0x09
|
||||
{152, DeviceType::CONTROLLER, F("Controller"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{114, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{125, DeviceType::CONTROLLER, F("BC25"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{152, DeviceType::CONTROLLER, F("Controller"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
|
||||
|
||||
// Connect devices - 0x02
|
||||
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
|
||||
// Gateways - 0x48 / 0x18
|
||||
{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48
|
||||
{ 94, DeviceType::GATEWAY, F("RC"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||
{ 94, DeviceType::GATEWAY, F("RC"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||
|
||||
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
|
||||
{202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
|
||||
@@ -138,7 +138,7 @@ std::string EMSdevice::to_string() const {
|
||||
}
|
||||
|
||||
if (brand_ == Brand::NO_BRAND) {
|
||||
snprintf_P(&str[0], str.capacity() + 1, PSTR("%s (DeviceID:0x%02X ProductID:%d, Version:%s)"), name_.c_str(), device_id_, product_id_, version_.c_str());
|
||||
snprintf_P(&str[0], str.capacity() + 1, PSTR("%s (DeviceID:0x%02X, ProductID:%d, Version:%s)"), name_.c_str(), device_id_, product_id_, version_.c_str());
|
||||
} else {
|
||||
snprintf_P(&str[0],
|
||||
str.capacity() + 1,
|
||||
@@ -161,6 +161,7 @@ void EMSdevice::show_values(uuid::console::Shell & shell) {
|
||||
// for each telegram that has the fetch value set (true) do a read request
|
||||
void EMSdevice::fetch_values() {
|
||||
DEBUG_LOG(F("Fetching values for device ID 0x%02X"), device_id());
|
||||
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
if (tf.fetch_) {
|
||||
read_command(tf.telegram_type_id_);
|
||||
@@ -260,13 +261,21 @@ void EMSdevice::read_command(const uint16_t type_id) {
|
||||
}
|
||||
|
||||
// prints a value to the console
|
||||
void EMSdevice::print_value(uuid::console::Shell & shell, const __FlashStringHelper * name, const __FlashStringHelper * prefix, const char * value) {
|
||||
shell.printfln(PSTR(" %s: %s%s"), uuid::read_flash_string(name).c_str(), value, uuid::read_flash_string(prefix).c_str());
|
||||
void EMSdevice::print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const __FlashStringHelper * suffix, const char * value) {
|
||||
uint8_t i = padding;
|
||||
while (i-- > 0) {
|
||||
shell.print(F(" "));
|
||||
}
|
||||
shell.printf(PSTR("%s: %s"), uuid::read_flash_string(name).c_str(), value);
|
||||
if (suffix != nullptr) {
|
||||
shell.print(uuid::read_flash_string(suffix).c_str());
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// prints a value to the console - no prefix
|
||||
void EMSdevice::print_value(uuid::console::Shell & shell, const __FlashStringHelper * name, const char * value) {
|
||||
shell.printfln(PSTR(" %s: %s"), uuid::read_flash_string(name).c_str(), value);
|
||||
// prints a value to the console - with no prefix
|
||||
void EMSdevice::print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const char * value) {
|
||||
print_value(shell, padding, name, nullptr, value);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -119,8 +119,8 @@ class EMSdevice {
|
||||
|
||||
void fetch_values();
|
||||
|
||||
void print_value(uuid::console::Shell & shell, const __FlashStringHelper * name, const __FlashStringHelper * prefix, const char * value);
|
||||
void print_value(uuid::console::Shell & shell, const __FlashStringHelper * name, const char * value);
|
||||
void print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const __FlashStringHelper * prefix, const char * value);
|
||||
void print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const char * value);
|
||||
|
||||
enum Brand : uint8_t {
|
||||
NO_BRAND, // 0
|
||||
@@ -195,9 +195,8 @@ class EMSdevice {
|
||||
|
||||
uint16_t telegram_type_id_; // it's type_id
|
||||
const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message
|
||||
// std::string telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // should this type_id be queried automatically?
|
||||
process_function_p process_function_;
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
process_function_p process_function_;
|
||||
};
|
||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
};
|
||||
|
||||
@@ -59,14 +59,15 @@ uint8_t EMSESP::actual_master_thermostat_ = EMSESP_DEFAULT_MASTER_THERMOSTAT; /
|
||||
uint16_t EMSESP::trace_watch_id_ = 0; // for when log is TRACE
|
||||
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
|
||||
bool EMSESP::ems_read_only_;
|
||||
uint32_t EMSESP::last_fetch_ = 0;
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#include "test/test_data.h" // used with the 'test' command, under su/admin
|
||||
#endif
|
||||
|
||||
// for each associated EMS device go and request data values
|
||||
// for each associated EMS device go and request its data values
|
||||
void EMSESP::fetch_device_values() {
|
||||
fetch_device_values(0); // fetch all
|
||||
fetch_device_values(0); // 0 = fetch all
|
||||
}
|
||||
|
||||
// for a specific EMS device go and request data values
|
||||
@@ -76,7 +77,9 @@ void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
if (emsdevice) {
|
||||
if ((device_id == 0) || emsdevice->is_device_id(device_id)) {
|
||||
emsdevice->fetch_values();
|
||||
return;
|
||||
if (device_id != 0) {
|
||||
return; // quit, we only want to return the selected device
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,6 +180,14 @@ void EMSESP::show_values(uuid::console::Shell & shell) {
|
||||
return;
|
||||
}
|
||||
|
||||
// show EMS device values
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
emsdevice->show_values(shell);
|
||||
shell.println();
|
||||
}
|
||||
}
|
||||
|
||||
// Dallas sensors (if available)
|
||||
char valuestr[8] = {0}; // for formatting temp
|
||||
if (!sensor_devices().empty()) {
|
||||
@@ -187,14 +198,6 @@ void EMSESP::show_values(uuid::console::Shell & shell) {
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// show EMS device values
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
emsdevice->show_values(shell);
|
||||
shell.println();
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -340,6 +343,7 @@ void EMSESP::process_UBADevices(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
// process the Version telegram (type 0x02), which is a common type
|
||||
// e.g. 09 0B 02 00 PP V1 V2
|
||||
void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
|
||||
// check for valid telegram, just in case
|
||||
if (telegram->message_length < 3) {
|
||||
@@ -447,7 +451,7 @@ bool EMSESP::device_exists(const uint8_t device_id) {
|
||||
}
|
||||
|
||||
// for each device add its context menu for the console
|
||||
void EMSESP::add_context_menu() {
|
||||
void EMSESP::add_context_menus() {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
emsdevice->add_context_menu();
|
||||
@@ -531,10 +535,10 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
|
||||
// if we don't recognize the product ID report it, but don't add it.
|
||||
if (!found) {
|
||||
DEBUG_LOG(F("Cannot add device. Unknown device ID 0x%02X, product ID %d. Ignoring it."), device_id, product_id);
|
||||
logger_.notice(F("Unrecognized EMS device with device ID 0x%02X with product ID %d. Please report on GitHub."), device_id, product_id);
|
||||
return false; // not found
|
||||
} else {
|
||||
DEBUG_LOG(F("Adding new device with device ID 0x%02X, product ID %d"), device_id, product_id);
|
||||
DEBUG_LOG(F("Adding new device with device ID 0x%02X with product ID %d"), device_id, product_id);
|
||||
// go and fetch its data, including asking for the version
|
||||
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
fetch_device_values(device_id);
|
||||
@@ -634,7 +638,7 @@ void EMSESP::set_ems_read_only() {
|
||||
}
|
||||
|
||||
// console commands to add
|
||||
void EMSESP::console_commands() {
|
||||
void EMSESP::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(show), F_(devices)},
|
||||
@@ -775,6 +779,9 @@ void EMSESP::console_commands() {
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id());
|
||||
shell.printfln(F_(read_only_fmt), settings.ems_read_only() ? F_(enabled) : F_(disabled));
|
||||
});
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
// kick off the party, start all the services
|
||||
@@ -805,6 +812,13 @@ void EMSESP::loop() {
|
||||
rxservice_.loop(); // process what ever is in the rx queue
|
||||
shower_.loop(); // check for shower on/off
|
||||
sensors_.loop(); // this will also send out via MQTT
|
||||
|
||||
// force a query on the EMS devices to fetch latest data
|
||||
uint32_t currentMillis = millis();
|
||||
if ((currentMillis - last_fetch_ > EMS_FETCH_FREQUENCY)) {
|
||||
last_fetch_ = currentMillis;
|
||||
fetch_device_values();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class EMSESP {
|
||||
static void show_devices(uuid::console::Shell & shell);
|
||||
static void show_emsbus(uuid::console::Shell & shell);
|
||||
|
||||
static void add_context_menu();
|
||||
static void add_context_menus();
|
||||
|
||||
static void incoming_telegram(uint8_t * data, const uint8_t length);
|
||||
|
||||
@@ -111,7 +111,7 @@ class EMSESP {
|
||||
return ems_read_only_;
|
||||
}
|
||||
|
||||
static void console_commands();
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
|
||||
static void fetch_device_values(const uint8_t device_id);
|
||||
static void fetch_device_values();
|
||||
@@ -136,6 +136,9 @@ class EMSESP {
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
|
||||
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
|
||||
static uint32_t last_fetch_;
|
||||
|
||||
struct Device_record {
|
||||
uint8_t product_id;
|
||||
EMSdevice::DeviceType device_type;
|
||||
|
||||
@@ -65,10 +65,10 @@ void Mixing::show_values(uuid::console::Shell & shell) {
|
||||
} else {
|
||||
shell.printfln(F(" Heating Circuit #: %d"), hc_);
|
||||
}
|
||||
print_value(shell, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, flowTemp_, 10));
|
||||
print_value(shell, F("Setpoint flow temperature"), F_(degrees), Helpers::render_value(buffer, flowSetTemp_, 1));
|
||||
print_value(shell, F("Current pump modulation"), Helpers::render_value(buffer, pumpMod_, 1));
|
||||
print_value(shell, F("Current valve status"), Helpers::render_value(buffer, status_, 1));
|
||||
print_value(shell, 2, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, flowTemp_, 10));
|
||||
print_value(shell, 2, F("Setpoint flow temperature"), F_(degrees), Helpers::render_value(buffer, flowSetTemp_, 1));
|
||||
print_value(shell, 2, F("Current pump modulation"), Helpers::render_value(buffer, pumpMod_, 1));
|
||||
print_value(shell, 2, F("Current valve status"), Helpers::render_value(buffer, status_, 1));
|
||||
}
|
||||
|
||||
// publish values via MQTT
|
||||
@@ -151,9 +151,9 @@ 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
|
||||
// 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_ = 0x22 - device_id();
|
||||
hc_ = 0x22 - device_id();
|
||||
telegram->read_value(flowTemp_, 1); // is * 10
|
||||
telegram->read_value(pumpMod_, 3);
|
||||
telegram->read_value(flowSetTemp_, 0);
|
||||
|
||||
37
src/mqtt.cpp
37
src/mqtt.cpp
@@ -40,7 +40,7 @@ MAKE_PSTR(mqtt_enabled_fmt, "MQTT is %s")
|
||||
MAKE_PSTR(mqtt_base_fmt, "Base = %s")
|
||||
MAKE_PSTR(mqtt_qos_fmt, "QOS = %ld")
|
||||
MAKE_PSTR(mqtt_retain_fmt, "Retain Flag = %s")
|
||||
MAKE_PSTR(mqtt_format_fmt, "JSON format = %s")
|
||||
MAKE_PSTR(mqtt_format_fmt, "Format for JSON = %s")
|
||||
MAKE_PSTR(mqtt_heartbeat_fmt, "Heartbeat = %s")
|
||||
MAKE_PSTR(mqtt_publish_time_fmt, "Publish time = %d seconds")
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace emsesp {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
AsyncMqttClient Mqtt::mqttClient_;
|
||||
#endif
|
||||
|
||||
std::vector<Mqtt::MQTTFunction> Mqtt::mqtt_functions_;
|
||||
bool Mqtt::mqtt_retain_;
|
||||
uint8_t Mqtt::mqtt_qos_;
|
||||
@@ -463,17 +464,20 @@ void Mqtt::send_heartbeat() {
|
||||
|
||||
// add MQTT message to queue, payload is a JSON doc.
|
||||
// NOTE this only prints first 255 chars
|
||||
void Mqtt::queue_publish_message(const char * topic, const JsonDocument & payload, const bool retain) {
|
||||
if (strlen(topic) == 0) {
|
||||
void Mqtt::queue_publish_message(const std::string & topic, const JsonDocument & payload, const bool retain) {
|
||||
// can't have bogus topics, but empty payloads are ok
|
||||
if (topic.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// check for empty JSON doc - we don't like those
|
||||
size_t capacity = measureJson(payload);
|
||||
if (capacity <= 3) {
|
||||
// DEBUG_LOG(("Empty JSON for topic %s. Skipping"), topic);
|
||||
// DEBUG_LOG(("Empty JSON payload for topic %s. Skipping"), topic);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
std::string payload_text;
|
||||
serializeJson(payload, payload_text);
|
||||
@@ -491,9 +495,9 @@ void Mqtt::queue_publish_message(const char * topic, const JsonDocument & payloa
|
||||
}
|
||||
|
||||
// add MQTT message to queue, payload is a string
|
||||
void Mqtt::queue_publish_message(const char * topic, const std::string & payload, const bool retain) {
|
||||
void Mqtt::queue_publish_message(const std::string & topic, const std::string & payload, const bool retain) {
|
||||
// can't have bogus topics, but empty payloads are ok
|
||||
if (strlen(topic) == 0) {
|
||||
if (topic.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -525,29 +529,29 @@ void Mqtt::queue_subscribe_message(const std::string & topic) {
|
||||
}
|
||||
|
||||
// MQTT Publish, using a specific retain flag
|
||||
void Mqtt::publish(const char * topic, const char * payload, bool retain) {
|
||||
void Mqtt::publish(const std::string & topic, const std::string & payload, bool retain) {
|
||||
queue_publish_message(topic, payload, retain);
|
||||
}
|
||||
|
||||
// Publish using the user's custom retain flag
|
||||
void Mqtt::publish(const char * topic, const char * payload) {
|
||||
void Mqtt::publish(const std::string & topic, const std::string & payload) {
|
||||
publish(topic, payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
void Mqtt::publish(const char * topic, const JsonDocument & payload) {
|
||||
void Mqtt::publish(const std::string & topic, const JsonDocument & payload) {
|
||||
publish(topic, payload, mqtt_retain_);
|
||||
}
|
||||
|
||||
void Mqtt::publish(const char * topic, const JsonDocument & payload, bool retain) {
|
||||
void Mqtt::publish(const std::string & topic, const JsonDocument & payload, bool retain) {
|
||||
queue_publish_message(topic, payload, retain);
|
||||
}
|
||||
|
||||
void Mqtt::publish(const char * topic, const bool value) {
|
||||
void Mqtt::publish(const std::string & topic, const bool value) {
|
||||
queue_publish_message(topic, value ? "1" : "0", mqtt_retain_);
|
||||
}
|
||||
|
||||
// no payload
|
||||
void Mqtt::publish(const char * topic) {
|
||||
void Mqtt::publish(const std::string & topic) {
|
||||
queue_publish_message(topic, "", mqtt_retain_);
|
||||
}
|
||||
|
||||
@@ -634,10 +638,10 @@ void Mqtt::process_queue() {
|
||||
}
|
||||
|
||||
mqtt_messages_.pop_front(); // remove the message from the queue
|
||||
} // namespace emsesp
|
||||
}
|
||||
|
||||
// add console commands
|
||||
void Mqtt::console_commands() {
|
||||
void Mqtt::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(
|
||||
ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
@@ -853,6 +857,9 @@ void Mqtt::console_commands() {
|
||||
shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time());
|
||||
shell.println();
|
||||
});
|
||||
} // namespace emsesp
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
20
src/mqtt.h
20
src/mqtt.h
@@ -39,6 +39,8 @@
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
#define EMSESP_MAX_JSON_SIZE_SMALL 200 // for smaller json docs
|
||||
#define EMSESP_MAX_JSON_SIZE_MEDIUM 800 // for smaller json docs from ems devices
|
||||
#define EMSESP_MAX_JSON_SIZE_LARGE 1500 // for large json docs from ems devices, like boiler or thermostat data
|
||||
@@ -69,16 +71,16 @@ class Mqtt {
|
||||
static void subscribe(const uint8_t device_id, const std::string & topic, mqtt_function_p cb);
|
||||
static void subscribe(const std::string & topic, mqtt_function_p cb);
|
||||
|
||||
static void publish(const char * topic, const char * payload);
|
||||
static void publish(const char * topic, const char * payload, bool retain);
|
||||
static void publish(const char * topic, const JsonDocument & payload);
|
||||
static void publish(const char * topic, const JsonDocument & payload, bool retain);
|
||||
static void publish(const char * topic, const bool value);
|
||||
static void publish(const char * topic);
|
||||
static void publish(const std::string & topic, const std::string & payload);
|
||||
static void publish(const std::string & topic, const std::string & payload, bool retain);
|
||||
static void publish(const std::string & topic, const JsonDocument & payload);
|
||||
static void publish(const std::string & topic, const JsonDocument & payload, bool retain);
|
||||
static void publish(const std::string & topic, const bool value);
|
||||
static void publish(const std::string & topic);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_id);
|
||||
|
||||
static void console_commands();
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
|
||||
void incoming(char * topic, char * payload); // for testing
|
||||
|
||||
@@ -130,8 +132,8 @@ class Mqtt {
|
||||
|
||||
static bool mqtt_retain_;
|
||||
|
||||
static void queue_publish_message(const char * topic, const JsonDocument & payload, const bool retain);
|
||||
static void queue_publish_message(const char * topic, const std::string & payload, const bool retain);
|
||||
static void queue_publish_message(const std::string & topic, const JsonDocument & payload, const bool retain);
|
||||
static void queue_publish_message(const std::string & topic, const std::string & payload, const bool retain);
|
||||
|
||||
static void queue_subscribe_message(const std::string & topic);
|
||||
|
||||
|
||||
@@ -346,6 +346,7 @@ void Network::show_network(uuid::console::Shell & shell) {
|
||||
shell.printfln(F("WiFi: unknown"));
|
||||
break;
|
||||
}
|
||||
|
||||
shell.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -54,20 +54,20 @@ void Solar::show_values(uuid::console::Shell & shell) {
|
||||
|
||||
char buffer[10]; // used for formatting
|
||||
|
||||
print_value(shell, F("Collector temperature (TS1)"), F_(degrees), Helpers::render_value(buffer, collectorTemp_, 10));
|
||||
print_value(shell, F("Bottom temperature (TS2)"), F_(degrees), Helpers::render_value(buffer, bottomTemp_, 10));
|
||||
print_value(shell, F("Bottom temperature (TS5)"), F_(degrees), Helpers::render_value(buffer, bottomTemp2_, 10));
|
||||
print_value(shell, F("Pump modulation"), F_(percent), Helpers::render_value(buffer, pumpModulation_, 1));
|
||||
print_value(shell, F("Valve (VS2) status"), Helpers::render_value(buffer, valveStatus_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Pump (PS1) active"), Helpers::render_value(buffer, pump_, EMS_VALUE_BOOL));
|
||||
print_value(shell, 2, F("Collector temperature (TS1)"), F_(degrees), Helpers::render_value(buffer, collectorTemp_, 10));
|
||||
print_value(shell, 2, F("Bottom temperature (TS2)"), F_(degrees), Helpers::render_value(buffer, bottomTemp_, 10));
|
||||
print_value(shell, 2, F("Bottom temperature (TS5)"), F_(degrees), Helpers::render_value(buffer, bottomTemp2_, 10));
|
||||
print_value(shell, 2, F("Pump modulation"), F_(percent), Helpers::render_value(buffer, pumpModulation_, 1));
|
||||
print_value(shell, 2, F("Valve (VS2) status"), Helpers::render_value(buffer, valveStatus_, EMS_VALUE_BOOL));
|
||||
print_value(shell, 2, F("Pump (PS1) active"), Helpers::render_value(buffer, pump_, EMS_VALUE_BOOL));
|
||||
|
||||
if (pumpWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60);
|
||||
}
|
||||
|
||||
print_value(shell, F("Energy last hour"), F_(wh), Helpers::render_value(buffer, energyLastHour_, 10));
|
||||
print_value(shell, F("Energy today"), F_(wh), Helpers::render_value(buffer, energyToday_, 0)); // no division
|
||||
print_value(shell, F("Energy total"), F_(kwh), Helpers::render_value(buffer, energyTotal_, 10));
|
||||
print_value(shell, 2, F("Energy last hour"), F_(wh), Helpers::render_value(buffer, energyLastHour_, 10));
|
||||
print_value(shell, 2, F("Energy today"), F_(wh), Helpers::render_value(buffer, energyToday_, 0)); // no division
|
||||
print_value(shell, 2, F("Energy total"), F_(kwh), Helpers::render_value(buffer, energyTotal_, 10));
|
||||
}
|
||||
|
||||
// publish values via MQTT
|
||||
|
||||
@@ -334,7 +334,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
// console commands to add
|
||||
void System::console_commands() {
|
||||
void System::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(ShellContext::SYSTEM,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(hostname)},
|
||||
@@ -509,7 +509,6 @@ void System::console_commands() {
|
||||
flash_string_vector{F_(show)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
Network::show_network(shell);
|
||||
shell.println();
|
||||
show_system(shell);
|
||||
shell.println();
|
||||
});
|
||||
@@ -522,7 +521,7 @@ void System::console_commands() {
|
||||
shell.printfln(F_(hostname_fmt),
|
||||
settings.hostname().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.hostname().c_str());
|
||||
|
||||
if (shell.has_flags(CommandFlags::ADMIN | CommandFlags::LOCAL)) {
|
||||
if (shell.has_flags(CommandFlags::ADMIN)) {
|
||||
shell.printfln("Wifi:");
|
||||
shell.print(" ");
|
||||
shell.printfln(F_(wifi_ssid_fmt),
|
||||
@@ -540,6 +539,9 @@ void System::console_commands() {
|
||||
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval());
|
||||
}
|
||||
});
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class System {
|
||||
@@ -63,7 +65,7 @@ class System {
|
||||
|
||||
static void show_mem(const char * text);
|
||||
|
||||
static void console_commands();
|
||||
static void console_commands(Shell & shell, unsigned int context);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// create some fake test data
|
||||
// used with the 'test' command, under su/admin
|
||||
void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
// A fake response - UBADevices(0x07) - only for testing offline
|
||||
if (command == "devices") {
|
||||
rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); // this is important otherwise nothing will be picked up!
|
||||
|
||||
emsdevices.push_back(EMSFactory::add(EMSdevice::DeviceType::BOILER, EMSdevice::EMS_DEVICE_ID_BOILER, 0, "", "My Boiler", 0, 0));
|
||||
|
||||
// A fake response - UBADevices(0x07)
|
||||
uint8_t t0[] = {0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47};
|
||||
rxservice_.add(t0, sizeof(t0));
|
||||
|
||||
@@ -21,6 +21,20 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == "unknown") {
|
||||
// question: do we need to set the mask?
|
||||
std::string version("1.2.3");
|
||||
add_device(0x09, 89, version, EMSdevice::Brand::BUDERUS);
|
||||
rxservice_.loop();
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
uint8_t u1[] = {0x09, 0x0B, 0x02, 0x00, 0x59, 0x01, 0x02, 0x56};
|
||||
rxservice_.add(u1, sizeof(u1));
|
||||
rxservice_.loop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == "thermostats") {
|
||||
shell.printfln(F("Testing adding devices on the EMS bus..."));
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
register_telegram_type(EMS_TYPE_RCOutdoorTemp, F("RCOutdoorTemp"), false, std::bind(&Thermostat::process_RCOutdoorTemp, this, _1));
|
||||
register_telegram_type(EMS_TYPE_RCTime, F("RCTime"), true, std::bind(&Thermostat::process_RCTime, this, _1)); // 0x06
|
||||
|
||||
// RC10
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_RC10) {
|
||||
monitor_typeids = {0xB1};
|
||||
set_typeids = {0xB0};
|
||||
@@ -47,6 +48,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
register_telegram_type(set_typeids[i], F("RC10Set"), true, std::bind(&Thermostat::process_RC10Set, this, _1));
|
||||
}
|
||||
|
||||
// RC35
|
||||
} else if ((flags == EMSdevice::EMS_DEVICE_FLAG_RC35) || (flags == EMSdevice::EMS_DEVICE_FLAG_RC30_1)) {
|
||||
monitor_typeids = {0x3E, 0x48, 0x52, 0x5C};
|
||||
set_typeids = {0x3D, 0x47, 0x51, 0x5B};
|
||||
@@ -56,6 +58,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
}
|
||||
register_telegram_type(EMS_TYPE_IBASettings, F("IBASettings"), true, std::bind(&Thermostat::process_IBASettings, this, _1));
|
||||
|
||||
// RC20
|
||||
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC20) {
|
||||
monitor_typeids = {0x91};
|
||||
set_typeids = {0xA8};
|
||||
@@ -64,6 +67,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
register_telegram_type(set_typeids[i], F("RC20Set"), true, std::bind(&Thermostat::process_RC20Set, this, _1));
|
||||
}
|
||||
|
||||
// RC20 newer
|
||||
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC20_2) {
|
||||
monitor_typeids = {0xAE};
|
||||
set_typeids = {0xAD};
|
||||
@@ -72,6 +76,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
register_telegram_type(set_typeids[i], F("RC20Set"), true, std::bind(&Thermostat::process_RC20Set_2, this, _1));
|
||||
}
|
||||
|
||||
// RC30
|
||||
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC30) {
|
||||
monitor_typeids = {0x41};
|
||||
set_typeids = {0xA7};
|
||||
@@ -80,11 +85,13 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
register_telegram_type(set_typeids[i], F("RC30Set"), true, std::bind(&Thermostat::process_RC30Set, this, _1));
|
||||
}
|
||||
|
||||
// EASY
|
||||
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_EASY) {
|
||||
monitor_typeids = {0x0A};
|
||||
set_typeids = {};
|
||||
register_telegram_type(monitor_typeids[0], F("EasyMonitor"), true, std::bind(&Thermostat::process_EasyMonitor, this, _1));
|
||||
|
||||
// RC300/RC100
|
||||
} else if ((flags == EMSdevice::EMS_DEVICE_FLAG_RC300) || (flags == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
|
||||
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
||||
set_typeids = {0x02B9, 0x02BA, 0x02BB, 0x02BC};
|
||||
@@ -93,6 +100,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
register_telegram_type(set_typeids[i], F("RC300Set"), true, std::bind(&Thermostat::process_RC300Set, this, _1));
|
||||
}
|
||||
|
||||
// JUNKERS/HT3
|
||||
} else if (flags == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||
monitor_typeids = {0x6F, 0x70, 0x71, 0x72};
|
||||
set_typeids = {0x65, 0x66, 0x67, 0x68};
|
||||
@@ -194,8 +202,7 @@ void Thermostat::add_context_menu() {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(thermostat)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::THERMOSTAT);
|
||||
console_commands();
|
||||
Thermostat::console_commands(shell, ShellContext::THERMOSTAT);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,7 +333,7 @@ bool Thermostat::updated_values() {
|
||||
}
|
||||
|
||||
return false;
|
||||
} // namespace emsesp
|
||||
}
|
||||
|
||||
// publish values via MQTT
|
||||
void Thermostat::publish_values() {
|
||||
@@ -337,7 +344,7 @@ void Thermostat::publish_values() {
|
||||
|
||||
DEBUG_LOG(F("Performing a thermostat publish (device ID 0x%02X)"), device_id());
|
||||
|
||||
uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits
|
||||
uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, stripping the option bits
|
||||
bool has_data = false;
|
||||
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
@@ -364,6 +371,7 @@ void Thermostat::publish_values() {
|
||||
}
|
||||
|
||||
has_data = true;
|
||||
// if the MQTT format is 'nested' then create the parent object hc<n>
|
||||
if (mqtt_format_ == Settings::MQTT_format::NESTED) {
|
||||
// create nested json for each HC
|
||||
char hc_name[10]; // hc{1-4}
|
||||
@@ -533,6 +541,10 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
||||
// decodes the thermostat mode for the heating circuit based on the thermostat type
|
||||
// modes are off, manual, auto, day and night
|
||||
uint8_t Thermostat::HeatingCircuit::get_mode(uint8_t flags) const {
|
||||
if (mode == EMS_VALUE_UINT_NOTSET) {
|
||||
return HeatingCircuit::Mode::UNKNOWN;
|
||||
}
|
||||
|
||||
flags &= 0x0F; // strip top 4 bits
|
||||
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_RC20) {
|
||||
@@ -565,7 +577,7 @@ uint8_t Thermostat::HeatingCircuit::get_mode(uint8_t flags) const {
|
||||
}
|
||||
}
|
||||
|
||||
return HeatingCircuit::Mode::OFF;
|
||||
return HeatingCircuit::Mode::UNKNOWN;
|
||||
}
|
||||
|
||||
// figures out the thermostat day/night mode depending on the thermostat type
|
||||
@@ -629,10 +641,13 @@ std::string Thermostat::mode_tostring(uint8_t mode) const {
|
||||
case HeatingCircuit::Mode::NOFROST:
|
||||
return read_flash_string(F("nofrost"));
|
||||
break;
|
||||
default:
|
||||
case HeatingCircuit::Mode::AUTO:
|
||||
return read_flash_string(F("auto"));
|
||||
break;
|
||||
default:
|
||||
case HeatingCircuit::Mode::UNKNOWN:
|
||||
return read_flash_string(F("unknown"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,22 +658,22 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
|
||||
char buffer[10]; // for formatting only
|
||||
|
||||
if (datetime_.size()) {
|
||||
shell.printfln(F("Clock: %s"), datetime_.c_str());
|
||||
shell.printfln(F(" Clock: %s"), datetime_.c_str());
|
||||
if (ibaClockOffset != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, F("Offset clock"), Helpers::render_value(buffer, ibaClockOffset, 1)); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
|
||||
print_value(shell, 1, F("Offset clock"), Helpers::render_value(buffer, ibaClockOffset, 1)); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits
|
||||
|
||||
if (flags == EMS_DEVICE_FLAG_RC35) {
|
||||
print_value(shell, F("Damped Outdoor temperature"), F_(degrees), Helpers::render_value(buffer, dampedoutdoortemp, 1));
|
||||
print_value(shell, F("Tempsensor 1"), F_(degrees), Helpers::render_value(buffer, tempsensor1, 10));
|
||||
print_value(shell, F("Tempsensor 2"), F_(degrees), Helpers::render_value(buffer, tempsensor2, 10));
|
||||
print_value(shell, 1, F("Damped Outdoor temperature"), F_(degrees), Helpers::render_value(buffer, dampedoutdoortemp, 1));
|
||||
print_value(shell, 1, F("Tempsensor 1"), F_(degrees), Helpers::render_value(buffer, tempsensor1, 10));
|
||||
print_value(shell, 1, F("Tempsensor 2"), F_(degrees), Helpers::render_value(buffer, tempsensor2, 10));
|
||||
}
|
||||
|
||||
for (const auto & hc : heating_circuits_) {
|
||||
shell.printfln(F("Heating Circuit %d:"), hc->hc_num());
|
||||
shell.printfln(F(" Heating Circuit %d:"), hc->hc_num());
|
||||
|
||||
// different thermostat types store their temperature values differently
|
||||
uint8_t format_setpoint, format_curr;
|
||||
@@ -677,83 +692,87 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
|
||||
break;
|
||||
}
|
||||
|
||||
print_value(shell, F("Current room temperature"), F_(degrees), Helpers::render_value(buffer, hc->curr_roomTemp, format_curr));
|
||||
print_value(shell, F("Setpoint room temperature"), F_(degrees), Helpers::render_value(buffer, hc->setpoint_roomTemp, format_setpoint));
|
||||
print_value(shell, F("Mode"), mode_tostring(hc->get_mode(flags)).c_str());
|
||||
print_value(shell, F("Mode Type"), mode_tostring(hc->get_mode_type(flags)).c_str());
|
||||
print_value(shell, 2, F("Current room temperature"), F_(degrees), Helpers::render_value(buffer, hc->curr_roomTemp, format_curr));
|
||||
print_value(shell, 2, F("Setpoint room temperature"), F_(degrees), Helpers::render_value(buffer, hc->setpoint_roomTemp, format_setpoint));
|
||||
if (hc->mode != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, 2, F("Mode"), mode_tostring(hc->get_mode(flags)).c_str());
|
||||
}
|
||||
if (hc->mode_type != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, 2, F("Mode Type"), mode_tostring(hc->get_mode_type(flags)).c_str());
|
||||
}
|
||||
|
||||
if ((flags == EMS_DEVICE_FLAG_RC35) || (flags == EMS_DEVICE_FLAG_RC30_1)) {
|
||||
if (hc->summer_mode) {
|
||||
shell.printfln(F(" Program is set to Summer mode"));
|
||||
shell.printfln(F(" Program is set to Summer mode"));
|
||||
} else if (hc->holiday_mode) {
|
||||
shell.printfln(F(" Program is set to Holiday mode"));
|
||||
shell.printfln(F(" Program is set to Holiday mode"));
|
||||
}
|
||||
|
||||
print_value(shell, F("Day temperature"), F_(degrees), Helpers::render_value(buffer, hc->daytemp, 2));
|
||||
print_value(shell, F("Night temperature"), F_(degrees), Helpers::render_value(buffer, hc->nighttemp, 2));
|
||||
print_value(shell, F("Vacation temperature"), F_(degrees), Helpers::render_value(buffer, hc->holidaytemp, 2));
|
||||
print_value(shell, 2, F("Day temperature"), F_(degrees), Helpers::render_value(buffer, hc->daytemp, 2));
|
||||
print_value(shell, 2, F("Night temperature"), F_(degrees), Helpers::render_value(buffer, hc->nighttemp, 2));
|
||||
print_value(shell, 2, F("Vacation temperature"), F_(degrees), Helpers::render_value(buffer, hc->holidaytemp, 2));
|
||||
|
||||
if (hc->offsettemp < 100) {
|
||||
print_value(shell, F("Offset temperature"), F_(degrees), Helpers::render_value(buffer, hc->offsettemp, 2));
|
||||
print_value(shell, 2, F("Offset temperature"), F_(degrees), Helpers::render_value(buffer, hc->offsettemp, 2));
|
||||
}
|
||||
print_value(shell, F("Design temperature"), F_(degrees), Helpers::render_value(buffer, hc->designtemp, 2));
|
||||
print_value(shell, 2, F("Design temperature"), F_(degrees), Helpers::render_value(buffer, hc->designtemp, 2));
|
||||
}
|
||||
|
||||
// show flow temp if we have it
|
||||
if (hc->circuitcalctemp != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, F("Calculated flow temperature"), F_(degrees), Helpers::render_value(buffer, hc->circuitcalctemp, 1));
|
||||
print_value(shell, 2, F("Calculated flow temperature"), F_(degrees), Helpers::render_value(buffer, hc->circuitcalctemp, 1));
|
||||
}
|
||||
|
||||
// settings parameters
|
||||
if (ibaMainDisplay != EMS_VALUE_UINT_NOTSET) {
|
||||
if (ibaMainDisplay == 0) {
|
||||
shell.printfln(F(" Display: internal temperature"));
|
||||
shell.printfln(F(" Display: internal temperature"));
|
||||
} else if (ibaMainDisplay == 1) {
|
||||
shell.printfln(F(" Display: internal setpoint"));
|
||||
shell.printfln(F(" Display: internal setpoint"));
|
||||
} else if (ibaMainDisplay == 2) {
|
||||
shell.printfln(F(" Display: external temperature"));
|
||||
shell.printfln(F(" Display: external temperature"));
|
||||
} else if (ibaMainDisplay == 3) {
|
||||
shell.printfln(F(" Display: burner temperature"));
|
||||
shell.printfln(F(" Display: burner temperature"));
|
||||
} else if (ibaMainDisplay == 4) {
|
||||
shell.printfln(F(" Display: WW temperature"));
|
||||
shell.printfln(F(" Display: WW temperature"));
|
||||
} else if (ibaMainDisplay == 5) {
|
||||
shell.printfln(F(" Display: functioning mode"));
|
||||
shell.printfln(F(" Display: functioning mode"));
|
||||
} else if (ibaMainDisplay == 6) {
|
||||
shell.printfln(F(" Display: time"));
|
||||
shell.printfln(F(" Display: time"));
|
||||
} else if (ibaMainDisplay == 7) {
|
||||
shell.printfln(F(" Display: date"));
|
||||
shell.printfln(F(" Display: date"));
|
||||
} else if (ibaMainDisplay == 9) {
|
||||
shell.printfln(F(" Display: smoke temperature"));
|
||||
shell.printfln(F(" Display: smoke temperature"));
|
||||
}
|
||||
}
|
||||
|
||||
if (ibaLanguage != EMS_VALUE_UINT_NOTSET) {
|
||||
if (ibaLanguage == 0) {
|
||||
shell.printfln(F(" Language: German"));
|
||||
shell.printfln(F(" Language: German"));
|
||||
} else if (ibaLanguage == 1) {
|
||||
shell.printfln(F(" Language: Dutch"));
|
||||
shell.printfln(F(" Language: Dutch"));
|
||||
} else if (ibaLanguage == 2) {
|
||||
shell.printfln(F(" Language: French"));
|
||||
shell.printfln(F(" Language: French"));
|
||||
} else if (ibaLanguage == 3) {
|
||||
shell.printfln(F(" Language: Italian"));
|
||||
shell.printfln(F(" Language: Italian"));
|
||||
}
|
||||
}
|
||||
|
||||
if (ibaCalIntTemperature != EMS_VALUE_INT_NOTSET) {
|
||||
print_value(shell, F("Offset int. temperature"), F_(degrees), Helpers::render_value(buffer, ibaCalIntTemperature, 2));
|
||||
print_value(shell, 2, F("Offset int. temperature"), F_(degrees), Helpers::render_value(buffer, ibaCalIntTemperature, 2));
|
||||
}
|
||||
|
||||
if (ibaMinExtTemperature != EMS_VALUE_INT_NOTSET) {
|
||||
print_value(shell, F("Min ext. temperature"), F_(degrees), Helpers::render_value(buffer, ibaMinExtTemperature, 10)); // min ext temp for heating curve, in deg.
|
||||
print_value(shell, 2, F("Min ext. temperature"), F_(degrees), Helpers::render_value(buffer, ibaMinExtTemperature, 10)); // min ext temp for heating curve, in deg.
|
||||
}
|
||||
|
||||
if (ibaBuildingType != EMS_VALUE_UINT_NOTSET) {
|
||||
if (ibaBuildingType == 0) {
|
||||
shell.printfln(F(" Building: light"));
|
||||
shell.printfln(F(" Building: light"));
|
||||
} else if (ibaBuildingType == 1) {
|
||||
shell.printfln(F(" Building: medium"));
|
||||
shell.printfln(F(" Building: medium"));
|
||||
} else if (ibaBuildingType == 2) {
|
||||
shell.printfln(F(" Building: heavy"));
|
||||
shell.printfln(F(" Building: heavy"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1238,7 +1257,7 @@ void Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
}
|
||||
|
||||
// add console commands
|
||||
void Thermostat::console_commands() {
|
||||
void Thermostat::console_commands(Shell & shell, unsigned int context) {
|
||||
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(master)},
|
||||
@@ -1318,6 +1337,9 @@ void Thermostat::console_commands() {
|
||||
: Helpers::hextoa(buffer, settings.master_thermostat()));
|
||||
shell.println();
|
||||
});
|
||||
|
||||
// enter the context
|
||||
Console::enter_custom_context(shell, context);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -76,7 +76,7 @@ class Thermostat : public EMSdevice {
|
||||
return set_typeid_;
|
||||
}
|
||||
|
||||
enum Mode : uint8_t { OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN };
|
||||
enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN };
|
||||
|
||||
private:
|
||||
uint8_t hc_num_;
|
||||
@@ -103,7 +103,7 @@ class Thermostat : public EMSdevice {
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void console_commands();
|
||||
void console_commands(Shell & shell, unsigned int context);
|
||||
void init_mqtt();
|
||||
|
||||
std::string datetime_; // date and time stamp
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "2.0.0a4"
|
||||
#define EMSESP_APP_VERSION "2.0.0a6"
|
||||
|
||||
Reference in New Issue
Block a user