This commit is contained in:
Paul
2020-05-23 14:00:14 +02:00
parent 1ceef8afd5
commit b2bb8e2b5a
24 changed files with 391 additions and 304 deletions

View File

@@ -10,12 +10,12 @@ Note: Version 2.0 is not backward compatible with v1.0. The File system structur
### **Design & coding principles** ### **Design & coding principles**
- The code can be built and run without an ESP microcontroller, which is useful when testing and simulating handling of the different telegrams and devices. Make sure you have GNU make and g++ installed and use 'make' to build the image and execute the file `emsesp` (on linux). - The code can be built and run without an ESP microcontroller, which is useful when testing and simulating handling of the different telegrams and devices. Make sure you have GNU make and g++ installed and use 'make' to build the image and execute the file `emsesp` (on linux).
- I used C++11 containers where I could (std::string, std::deque, std::list, std::multimap etc). - I used C++11 containers where I could (std::string, std::deque, std::list, std::multimap etc).
- The core is based off the great libraries from @nomis' and I adopted his general design pattens such as making everything as asynchronous as possible so that no one operation should starve another operation of it's time to execute (https://isocpp.org/wiki/faq/ctors#static-init-order). - The core is based off the great libraries from @nomis' and I adopted his general design pattens such as making everything as asynchronous as possible so that no one operation should starve another operation of it's time to execute (https://isocpp.org/wiki/faq/ctors#static-init-order).
- All EMS devices (e.g. boiler, thermostat, solar modules etc) are derived from a factory base class and each class handles its own registering of telegram and mqtt handlers. This makes the EMS device code easier to manage and extend with new telegrams types and features. - All EMS devices (e.g. boiler, thermostat, solar modules etc) are derived from a factory base class and each class handles its own registering of telegram and mqtt handlers. This makes the EMS device code easier to manage and extend with new telegrams types and features.
- Built to work with both EMS8266 and ESP32. - Built to work with both EMS8266 and ESP32.
- Extended MQTT to use MQTT discovery on Home Assistant, just for the thermostat - Extended MQTT to use MQTT discovery on Home Assistant, just for the thermostat for now.
### **Features** ### **Features**
@@ -124,6 +124,7 @@ thermostat
### **Known issues, bugs and improvements currently working on** ### **Known issues, bugs and improvements currently working on**
``` ```
TODO when doing show, should we sort the ems devices?
TODO figure out why sometimes telnet on ESP32 (and sometimes ESP8266) has slow response times. After a manual reset it seems to fix itself. Perhaps the telnet service needs to start after the wifi is up & running. TODO figure out why sometimes telnet on ESP32 (and sometimes ESP8266) has slow response times. After a manual reset it seems to fix itself. Perhaps the telnet service needs to start after the wifi is up & running.
TODO Get the ESP32 UART code working. TODO Get the ESP32 UART code working.
TODO sometimes with tx_mode 0 there are a few CRC errors due to collision when waiting for a BRK signal. TODO sometimes with tx_mode 0 there are a few CRC errors due to collision when waiting for a BRK signal.

View File

@@ -58,7 +58,8 @@ Shell::~Shell() {
void Shell::start() { void Shell::start() {
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
uuid::log::Logger::register_handler(this, uuid::log::Level::DEBUG); // added by proddy // uuid::log::Logger::register_handler(this, uuid::log::Level::DEBUG); // added by proddy
uuid::log::Logger::register_handler(this, uuid::log::Level::INFO); // added by proddy
#else #else
uuid::log::Logger::register_handler(this, uuid::log::Level::NOTICE); uuid::log::Logger::register_handler(this, uuid::log::Level::NOTICE);
#endif #endif

View File

@@ -76,7 +76,6 @@ void Shell::output_logs() {
auto message = std::move(log_messages_.front()); auto message = std::move(log_messages_.front());
log_messages_.pop_front(); log_messages_.pop_front();
print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3)); print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3));
printf(F(" %c %lu: [%S] "), uuid::log::format_level_char(message.content_->level), message.id_, message.content_->name); printf(F(" %c %lu: [%S] "), uuid::log::format_level_char(message.content_->level), message.id_, message.content_->name);

View File

@@ -3,7 +3,7 @@
[platformio] [platformio]
default_envs = esp8266 default_envs = esp8266
;default_envs = esp32 ; default_envs = esp32
# override any settings with your own local ones in pio_local.ini # override any settings with your own local ones in pio_local.ini
extra_configs = pio_local.ini extra_configs = pio_local.ini
@@ -33,6 +33,7 @@ build_flags = -std=c++11 -Os -fno-exceptions
-D ARDUINOJSON_USE_LONG_LONG=0 -D ARDUINOJSON_USE_LONG_LONG=0
-D BEARSSL_SSL_BASIC -D BEARSSL_SSL_BASIC
-D PROGMEM_WWW -D PROGMEM_WWW
-D UUID_TELNET_HAVE_WIFICLIENT_NODELAY=0
libs_core = libs_core =
ArduinoJson ArduinoJson
@@ -44,9 +45,8 @@ libs_esp8266 =
libs_esp32 = libs_esp32 =
[env] [env]
build_unflags = -fno-rtti ; for dynamic_cast<> ;lib_ldf_mode = chain+
lib_ldf_mode = chain+ ;lib_compat_mode = strict
lib_compat_mode = strict
extra_scripts = scripts/main_script.py extra_scripts = scripts/main_script.py
framework = arduino framework = arduino
monitor_speed = 115200 monitor_speed = 115200
@@ -57,13 +57,14 @@ check_flags =
cppcheck: --std=c++11 cppcheck: --std=c++11
clangtidy: --checks=-*,clang-analyzer-*,performance-* clangtidy: --checks=-*,clang-analyzer-*,performance-*
; USB upload
upload_protocol = esptool
; example ports for OSX ; example ports for OSX
;upload_port = /dev/cu.wchusbserial14403 ;upload_port = /dev/cu.wchusbserial14403
;upload_port = /dev/cu.usbserial-1440 ;upload_port = /dev/cu.usbserial-1440
;upload_port = /dev/cu.SLAB_USBtoUART ;upload_port = /dev/cu.SLAB_USBtoUART
; OTA ; OTA upload
upload_protocol = esptool
; upload_protocol = espota ; upload_protocol = espota
; upload_flags = ; upload_flags =
; --port=8266 ; --port=8266
@@ -79,7 +80,7 @@ board = esp12e
lib_deps = ${common.libs_core} ${common.libs_esp8266} lib_deps = ${common.libs_core} ${common.libs_esp8266}
board_build.f_cpu = 160000000L ; 160MHz board_build.f_cpu = 160000000L ; 160MHz
;board_build.ldscript = eagle.flash.4m1m.ld ; 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer ;board_build.ldscript = eagle.flash.4m1m.ld ; 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer
board_build.ldscript = eagle.flash.4m2m.ld ; same as above but with 2024 KB SPIFFS board_build.ldscript = eagle.flash.4m2m.ld ; 1019 KB sketch, 2024 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 1028 KB OTA & buffer
build_flags = ${common.build_flags} ${common.debug_flags} -flto -D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY build_flags = ${common.build_flags} ${common.debug_flags} -flto -D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
[env:esp32] [env:esp32]

View File

@@ -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(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(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(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(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(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(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(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(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(0xE4, F("UBAMonitorFastPlus"), true, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus, 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(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)); 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, CommandFlags::USER,
flash_string_vector{F_(boiler)}, flash_string_vector{F_(boiler)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::BOILER); Boiler::console_commands(shell, ShellContext::BOILER);
console_commands();
}); });
} }
@@ -316,7 +315,10 @@ void Boiler::publish_values() {
DEBUG_LOG(F("[DEBUG] Performing a boiler publish")); DEBUG_LOG(F("[DEBUG] Performing a boiler publish"));
#endif #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 // 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 char buffer[10]; // used for formatting
print_value(shell, F("Selected flow temperature"), F_(degrees), Helpers::render_value(buffer, selFlowTemp_, 1)); print_value(shell, 2, 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("Warm Water selected temperature"), F_(degrees), Helpers::render_value(buffer, wWSelTemp_, 1));
if (tap_water_active_ != EMS_VALUE_BOOL_NOTSET) { 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) { 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, 2, 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, 2, 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 circulation pump type"), wWCircPumpType_ ? "3-way pump" : "charge pump");
if (wWCircPumpMode_ == 7) { 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 { } else {
char s[7]; char s[7];
char buffer[2]; char buffer[2];
@@ -353,64 +355,64 @@ void Boiler::show_values(uuid::console::Shell & shell) {
buffer[1] = '\0'; buffer[1] = '\0';
strlcpy(s, buffer, 7); strlcpy(s, buffer, 7);
strlcat(s, "x3min", 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) { 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) { } 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) { } 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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 # starts"), Helpers::render_value(buffer, wWStarts_, 1));
if (wWWorkM_ != EMS_VALUE_ULONG_NOTSET) { 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); 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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("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, 2, 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, 2, 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, 2, 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)); print_value(shell, 2, F("System pressure"), F("bar"), Helpers::render_value(buffer, sysPress_, 10));
if (serviceCode_ == EMS_VALUE_USHORT_NOTSET) { if (serviceCode_ != EMS_VALUE_USHORT_NOTSET) {
shell.printfln(F(" System service code: %s"), serviceCodeChar_);
} else {
shell.printfln(F(" System service code: %s (%d)"), serviceCodeChar_, serviceCode_); shell.printfln(F(" System service code: %s (%d)"), serviceCodeChar_, serviceCode_);
} else if (serviceCodeChar_[0] != '\0') {
shell.printfln(F(" System service code: %s"), serviceCodeChar_);
} }
// UBAParameters // UBAParameters
print_value(shell, F("Heating temperature setting on the boiler"), F_(degrees), Helpers::render_value(buffer, heating_temp_, 1)); print_value(shell, 2, 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, 2, 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("Boiler circuit pump modulation min power"), F_(percent), Helpers::render_value(buffer, pump_mod_min_, 1));
// UBAMonitorSlow // UBAMonitorSlow
if (extTemp_ != EMS_VALUE_SHORT_NOTSET) { 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, 2, 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, 2, 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("Burner # starts"), Helpers::render_value(buffer, burnStarts_, 1));
if (burnWorkMin_ != EMS_VALUE_ULONG_NOTSET) { 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); 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) { 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) { 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+ * 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) { void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(selFlowTemp_, 6); telegram->read_value(selFlowTemp_, 6);
@@ -562,8 +565,6 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
serviceCodeChar_[2] = '\0'; 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 // at this point do a quick check to see if the hot water or heating is active
check_active(); check_active();
} }
@@ -754,7 +755,7 @@ void Boiler::set_warmwater_circulation(const bool activated) {
} }
// add console commands // add console commands
void Boiler::console_commands() { void Boiler::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::BOILER, EMSESPShell::commands->add_command(ShellContext::BOILER,
CommandFlags::ADMIN, CommandFlags::ADMIN,
flash_string_vector{F_(read)}, 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.printfln(F_(shower_alert_fmt), settings.shower_alert() ? F_(enabled) : F_(disabled));
shell.println(); shell.println();
}); });
// enter the context
Console::enter_custom_context(shell, context);
} }
} // namespace emsesp } // namespace emsesp

View File

@@ -46,7 +46,7 @@ class Boiler : public EMSdevice {
private: private:
static uuid::log::Logger logger_; 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 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 uint8_t wWComfort_ = EMS_VALUE_UINT_NOTSET; // WW comfort mode
// UBAMonitorFast - 0x18 on EMS1 // UBAMonitorFast - 0x18 on EMS1
uint8_t selFlowTemp_ = EMS_VALUE_UINT_NOTSET; // Selected flow temperature uint8_t selFlowTemp_ = EMS_VALUE_UINT_NOTSET; // Selected flow temperature
uint16_t curFlowTemp_ = EMS_VALUE_USHORT_NOTSET; // Current 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 wwStorageTemp1_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 1
uint16_t wwStorageTemp2_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 2 uint16_t wwStorageTemp2_ = EMS_VALUE_USHORT_NOTSET; // warm water storage temp 2
uint16_t retTemp_ = EMS_VALUE_USHORT_NOTSET; // Return temperature uint16_t retTemp_ = EMS_VALUE_USHORT_NOTSET; // Return temperature
uint8_t burnGas_ = EMS_VALUE_BOOL_NOTSET; // Gas on/off uint8_t burnGas_ = EMS_VALUE_BOOL_NOTSET; // Gas on/off
uint8_t fanWork_ = EMS_VALUE_BOOL_NOTSET; // Fan on/off uint8_t fanWork_ = EMS_VALUE_BOOL_NOTSET; // Fan on/off
uint8_t ignWork_ = EMS_VALUE_BOOL_NOTSET; // Ignition 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 heatPmp_ = EMS_VALUE_BOOL_NOTSET; // Boiler pump on/off
uint8_t wWHeat_ = EMS_VALUE_BOOL_NOTSET; // 3-way valve on WW uint8_t wWHeat_ = EMS_VALUE_BOOL_NOTSET; // 3-way valve on WW
uint8_t wWCirc_ = EMS_VALUE_BOOL_NOTSET; // Circulation on/off uint8_t wWCirc_ = EMS_VALUE_BOOL_NOTSET; // Circulation on/off
uint8_t selBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner max power % uint8_t selBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner max power %
uint8_t curBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner current power % uint8_t curBurnPow_ = EMS_VALUE_UINT_NOTSET; // Burner current power %
uint16_t flameCurr_ = EMS_VALUE_USHORT_NOTSET; // Flame current in micro amps uint16_t flameCurr_ = EMS_VALUE_USHORT_NOTSET; // Flame current in micro amps
uint8_t sysPress_ = EMS_VALUE_UINT_NOTSET; // System pressure uint8_t sysPress_ = EMS_VALUE_UINT_NOTSET; // System pressure
char serviceCodeChar_[3]; // 2 character status/service code char serviceCodeChar_[3] = {'\0'}; // 2 character status/service code
uint16_t serviceCode_ = EMS_VALUE_USHORT_NOTSET; // error/service code uint16_t serviceCode_ = EMS_VALUE_USHORT_NOTSET; // error/service code
// UBAMonitorSlow - 0x19 on EMS1 // UBAMonitorSlow - 0x19 on EMS1
int16_t extTemp_ = EMS_VALUE_SHORT_NOTSET; // Outside temperature int16_t extTemp_ = EMS_VALUE_SHORT_NOTSET; // Outside temperature

View File

@@ -21,7 +21,11 @@
namespace emsesp { 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; static std::shared_ptr<EMSESPShell> shell;
std::vector<bool> EMSESPStreamConsole::ptys_; std::vector<bool> EMSESPStreamConsole::ptys_;
@@ -32,11 +36,6 @@ uuid::telnet::TelnetService telnet_([](Stream & stream, const IPAddress & addr,
}); });
#endif #endif
std::shared_ptr<Commands> EMSESPShell::commands = [] {
std::shared_ptr<Commands> commands = std::make_shared<Commands>();
return commands;
}();
EMSESPShell::EMSESPShell() EMSESPShell::EMSESPShell()
: Shell() { : Shell() {
} }
@@ -125,7 +124,7 @@ void EMSESPShell::add_console_commands() {
}); });
/* /*
* add the submenu contexts... * add all the submenu contexts...
*/ */
// MQTT // MQTT
@@ -133,8 +132,7 @@ void EMSESPShell::add_console_commands() {
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(mqtt)}, flash_string_vector{F_(mqtt)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::MQTT); Mqtt::console_commands(shell, ShellContext::MQTT);
Mqtt::console_commands();
}); });
// EMS // EMS
@@ -142,8 +140,7 @@ void EMSESPShell::add_console_commands() {
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(ems)}, flash_string_vector{F_(ems)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::EMS); EMSESP::console_commands(shell, ShellContext::EMS);
EMSESP::console_commands();
}); });
// System // System
@@ -151,15 +148,14 @@ void EMSESPShell::add_console_commands() {
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(system)}, flash_string_vector{F_(system)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::SYSTEM); System::console_commands(shell, ShellContext::SYSTEM);
System::console_commands();
}); });
// add all the context menus for the connected devices // add all the context menus for the connected devices
// this assumes they devices have been detected and registered // this assumes they devices have been detected and pre-registered
EMSESP::add_context_menu(); EMSESP::add_context_menus();
enter_custom_context(ShellContext::MAIN); // add su, exit and help Console::load_standard_commands(ShellContext::MAIN);
_console_commands_loaded = true; _console_commands_loaded = true;
} }
@@ -199,17 +195,29 @@ bool EMSESPShell::exit_context() {
return Shell::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 // 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 #ifdef EMSESP_DEBUG
commands->add_command(context, EMSESPShell::commands->add_command(context,
CommandFlags::ADMIN, CommandFlags::ADMIN,
flash_string_vector{F_(test)}, flash_string_vector{F_(test)},
flash_string_vector{F_(name_mandatory)}, flash_string_vector{F_(name_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { EMSESP::run_test(shell, arguments.front()); }); [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
EMSESP::run_test(shell, arguments.front());
});
#endif #endif
commands->add_command( EMSESPShell::commands->add_command(
context, context,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(log)}, 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 uint16_t watch_id = 0; // no watch ID set
if ((arguments.size() == 2) && (level == uuid::log::Level::TRACE)) { if ((arguments.size() == 2) && (level == uuid::log::Level::TRACE)) {
watch_id = Helpers::hextoint(arguments[1].c_str()); 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); emsesp::EMSESP::trace_watch_id(watch_id);
} }
@@ -239,68 +247,61 @@ void EMSESPShell::enter_custom_context(unsigned int context) {
return uuid::log::levels_lowercase(); return uuid::log::levels_lowercase();
}); });
commands->add_command(context, EMSESPShell::commands->add_command(context,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(help)}, flash_string_vector{F_(help)},
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { shell.print_all_available_commands(); }); [](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
shell.print_all_available_commands();
});
commands->add_command(context, EMSESPShell::commands->add_command(context,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(exit)}, flash_string_vector{F_(exit)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { shell.exit_context(); }); [=](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, EMSESPShell::commands->add_command(context,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(su)}, flash_string_vector{F_(su)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
auto become_admin = [](Shell & shell) { auto become_admin = [](Shell & shell) {
shell.logger().log(LogLevel::NOTICE, shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Admin session opened on console"));
LogFacility::AUTH, shell.add_flags(CommandFlags::ADMIN);
F("Admin session opened on console %s"), };
dynamic_cast<EMSESPShell &>(shell).console_name().c_str());
shell.add_flags(CommandFlags::ADMIN);
};
if (shell.has_flags(CommandFlags::LOCAL)) { if (shell.has_flags(CommandFlags::LOCAL)) {
become_admin(shell); become_admin(shell);
} else { } else {
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) { shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
if (completed) { if (completed) {
uint64_t now = uuid::get_uptime_ms(); uint64_t now = uuid::get_uptime_ms();
if (!password.empty() && password == Settings().admin_password()) { if (!password.empty() && password == Settings().admin_password()) {
become_admin(shell); become_admin(shell);
} else { } else {
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) { shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
shell.logger().log(LogLevel::NOTICE, shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Invalid admin password on console"));
LogFacility::AUTH, shell.println(F("su: incorrect password"));
F("Invalid admin password on console %s"), });
dynamic_cast<EMSESPShell &>(shell).console_name().c_str()); }
shell.println(F("su: incorrect password")); }
}); });
} }
} });
});
}
});
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
commands->add_command(context, EMSESPShell::commands->add_command(
CommandFlags::ADMIN, context, CommandFlags::ADMIN, flash_string_vector{F_(debug)}, [&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
flash_string_vector{F_(debug)}, shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET);
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { Settings settings;
shell.printfln(F("%s%sEMS-ESP version %s%s"), COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, Settings().app_version().c_str(), COLOR_RESET); settings.commit();
Settings settings; settings.show_settings(shell);
settings.commit(); shell.println();
settings.show_settings(shell); });
shell.println();
});
#endif #endif
// don't enter context if we're at the root
if (context != ShellContext::MAIN) {
Shell::enter_context(context);
}
} }
// prompt, change per context // prompt, change per context
@@ -339,8 +340,6 @@ std::string EMSESPShell::prompt_suffix() {
} }
void EMSESPShell::end_of_transmission() { void EMSESPShell::end_of_transmission() {
// delete MAIN console stuff
commands->remove_context_commands(ShellContext::MAIN);
invoke_command(uuid::read_flash_string(F_(exit))); 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 // note, this must be started after the network/wifi for ESP32 otherwise it'll crash
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
telnet_.start(); 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 #endif
} }
@@ -428,8 +427,6 @@ void Console::loop() {
#endif #endif
Shell::loop_all(); Shell::loop_all();
// delay(0); // in EMS-ESP 1.9.5 this helped with stability
} }
} // namespace emsesp } // namespace emsesp

View File

@@ -125,7 +125,13 @@ namespace emsesp {
using LogLevel = ::uuid::log::Level; using LogLevel = ::uuid::log::Level;
using LogFacility = ::uuid::log::Facility; 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 { 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<uuid::console::Commands> commands;
static std::shared_ptr<EMSESPShell> shell; static std::shared_ptr<EMSESPShell> shell;
void enter_custom_context(unsigned int context);
protected: protected:
EMSESPShell(); EMSESPShell();
@@ -191,6 +195,9 @@ class Console {
uuid::log::Level log_level(); uuid::log::Level log_level();
static void enter_custom_context(Shell & shell, unsigned int context);
static void load_standard_commands(unsigned int context);
private: private:
static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200; static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200;
static constexpr auto & serial_console_ = Serial; static constexpr auto & serial_console_ = Serial;

View File

@@ -27,7 +27,6 @@
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {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}, {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}, {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}, {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}, {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}, { 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}, {122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{170, DeviceType::BOILER, F("Logano GB212"), 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}, {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 // Solar Modules - 0x30
{ 73, DeviceType::SOLAR, F("SM10"), DeviceFlags::EMS_DEVICE_FLAG_SM10}, { 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}, {200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Switches - 0x11 // 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 // 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 {218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{190, DeviceType::CONTROLLER, F("BC10"), 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 {114, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{125, DeviceType::CONTROLLER, F("BC25"), 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 {169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{152, DeviceType::CONTROLLER, F("Controller"), 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 { 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{209, DeviceType::CONTROLLER, F("ErP"), 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 {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 {207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
// Connect devices - 0x02 // Connect devices - 0x02
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 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 {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 // Gateways - 0x48 / 0x18
{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48 {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 // 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 {202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write

View File

@@ -138,7 +138,7 @@ std::string EMSdevice::to_string() const {
} }
if (brand_ == Brand::NO_BRAND) { 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 { } else {
snprintf_P(&str[0], snprintf_P(&str[0],
str.capacity() + 1, 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 // for each telegram that has the fetch value set (true) do a read request
void EMSdevice::fetch_values() { void EMSdevice::fetch_values() {
DEBUG_LOG(F("Fetching values for device ID 0x%02X"), device_id()); DEBUG_LOG(F("Fetching values for device ID 0x%02X"), device_id());
for (const auto & tf : telegram_functions_) { for (const auto & tf : telegram_functions_) {
if (tf.fetch_) { if (tf.fetch_) {
read_command(tf.telegram_type_id_); 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 // prints a value to the console
void EMSdevice::print_value(uuid::console::Shell & shell, const __FlashStringHelper * name, const __FlashStringHelper * prefix, const char * value) { void EMSdevice::print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const __FlashStringHelper * suffix, const char * value) {
shell.printfln(PSTR(" %s: %s%s"), uuid::read_flash_string(name).c_str(), value, uuid::read_flash_string(prefix).c_str()); 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 // prints a value to the console - with no prefix
void EMSdevice::print_value(uuid::console::Shell & shell, const __FlashStringHelper * name, const char * value) { void EMSdevice::print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const char * value) {
shell.printfln(PSTR(" %s: %s"), uuid::read_flash_string(name).c_str(), value); print_value(shell, padding, name, nullptr, value);
} }
} // namespace emsesp } // namespace emsesp

View File

@@ -119,8 +119,8 @@ class EMSdevice {
void fetch_values(); 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, uint8_t padding, 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 char * value);
enum Brand : uint8_t { enum Brand : uint8_t {
NO_BRAND, // 0 NO_BRAND, // 0
@@ -195,9 +195,8 @@ class EMSdevice {
uint16_t telegram_type_id_; // it's type_id uint16_t telegram_type_id_; // it's type_id
const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message
// std::string telegram_type_name_; // e.g. RC20Message bool fetch_; // if this type_id be queried automatically
bool fetch_; // should this type_id be queried automatically? process_function_p process_function_;
process_function_p process_function_;
}; };
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
}; };

View File

@@ -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 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::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
bool EMSESP::ems_read_only_; bool EMSESP::ems_read_only_;
uint32_t EMSESP::last_fetch_ = 0;
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
#include "test/test_data.h" // used with the 'test' command, under su/admin #include "test/test_data.h" // used with the 'test' command, under su/admin
#endif #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() { 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 // 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 (emsdevice) {
if ((device_id == 0) || emsdevice->is_device_id(device_id)) { if ((device_id == 0) || emsdevice->is_device_id(device_id)) {
emsdevice->fetch_values(); 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; return;
} }
// show EMS device values
for (const auto & emsdevice : emsdevices) {
if (emsdevice) {
emsdevice->show_values(shell);
shell.println();
}
}
// Dallas sensors (if available) // Dallas sensors (if available)
char valuestr[8] = {0}; // for formatting temp char valuestr[8] = {0}; // for formatting temp
if (!sensor_devices().empty()) { if (!sensor_devices().empty()) {
@@ -187,14 +198,6 @@ void EMSESP::show_values(uuid::console::Shell & shell) {
shell.println(); shell.println();
} }
// show EMS device values
for (const auto & emsdevice : emsdevices) {
if (emsdevice) {
emsdevice->show_values(shell);
shell.println();
}
}
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 // 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) { void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// check for valid telegram, just in case // check for valid telegram, just in case
if (telegram->message_length < 3) { 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 // 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) { for (const auto & emsdevice : emsdevices) {
if (emsdevice) { if (emsdevice) {
emsdevice->add_context_menu(); 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 we don't recognize the product ID report it, but don't add it.
if (!found) { 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 return false; // not found
} else { } 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 // go and fetch its data, including asking for the version
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id); send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
fetch_device_values(device_id); fetch_device_values(device_id);
@@ -634,7 +638,7 @@ void EMSESP::set_ems_read_only() {
} }
// console commands to add // console commands to add
void EMSESP::console_commands() { void EMSESP::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::EMS, EMSESPShell::commands->add_command(ShellContext::EMS,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(show), F_(devices)}, 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_(bus_id_fmt), settings.ems_bus_id());
shell.printfln(F_(read_only_fmt), settings.ems_read_only() ? F_(enabled) : F_(disabled)); 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 // 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 rxservice_.loop(); // process what ever is in the rx queue
shower_.loop(); // check for shower on/off shower_.loop(); // check for shower on/off
sensors_.loop(); // this will also send out via MQTT 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();
}
} }
} }

View File

@@ -85,7 +85,7 @@ class EMSESP {
static void show_devices(uuid::console::Shell & shell); static void show_devices(uuid::console::Shell & shell);
static void show_emsbus(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); static void incoming_telegram(uint8_t * data, const uint8_t length);
@@ -111,7 +111,7 @@ class EMSESP {
return ems_read_only_; 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(const uint8_t device_id);
static void fetch_device_values(); static void fetch_device_values();
@@ -136,6 +136,9 @@ class EMSESP {
static RxService rxservice_; static RxService rxservice_;
static TxService txservice_; static TxService txservice_;
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
static uint32_t last_fetch_;
struct Device_record { struct Device_record {
uint8_t product_id; uint8_t product_id;
EMSdevice::DeviceType device_type; EMSdevice::DeviceType device_type;

View File

@@ -65,10 +65,10 @@ void Mixing::show_values(uuid::console::Shell & shell) {
} else { } else {
shell.printfln(F(" Heating Circuit #: %d"), hc_); 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, 2, 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, 2, 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, 2, 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 valve status"), Helpers::render_value(buffer, status_, 1));
} }
// publish values via MQTT // publish values via MQTT
@@ -151,9 +151,9 @@ void Mixing::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
type_ = Type::HC; type_ = Type::HC;
// the heating circuit is determine by which device_id it is, 0x20 - 0x23 // 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 // 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(flowTemp_, 1); // is * 10
telegram->read_value(pumpMod_, 3); telegram->read_value(pumpMod_, 3);
telegram->read_value(flowSetTemp_, 0); telegram->read_value(flowSetTemp_, 0);

View File

@@ -40,7 +40,7 @@ MAKE_PSTR(mqtt_enabled_fmt, "MQTT is %s")
MAKE_PSTR(mqtt_base_fmt, "Base = %s") MAKE_PSTR(mqtt_base_fmt, "Base = %s")
MAKE_PSTR(mqtt_qos_fmt, "QOS = %ld") MAKE_PSTR(mqtt_qos_fmt, "QOS = %ld")
MAKE_PSTR(mqtt_retain_fmt, "Retain Flag = %s") 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_heartbeat_fmt, "Heartbeat = %s")
MAKE_PSTR(mqtt_publish_time_fmt, "Publish time = %d seconds") MAKE_PSTR(mqtt_publish_time_fmt, "Publish time = %d seconds")
@@ -52,6 +52,7 @@ namespace emsesp {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
AsyncMqttClient Mqtt::mqttClient_; AsyncMqttClient Mqtt::mqttClient_;
#endif #endif
std::vector<Mqtt::MQTTFunction> Mqtt::mqtt_functions_; std::vector<Mqtt::MQTTFunction> Mqtt::mqtt_functions_;
bool Mqtt::mqtt_retain_; bool Mqtt::mqtt_retain_;
uint8_t Mqtt::mqtt_qos_; uint8_t Mqtt::mqtt_qos_;
@@ -463,17 +464,20 @@ void Mqtt::send_heartbeat() {
// add MQTT message to queue, payload is a JSON doc. // add MQTT message to queue, payload is a JSON doc.
// NOTE this only prints first 255 chars // NOTE this only prints first 255 chars
void Mqtt::queue_publish_message(const char * topic, const JsonDocument & payload, const bool retain) { void Mqtt::queue_publish_message(const std::string & topic, const JsonDocument & payload, const bool retain) {
if (strlen(topic) == 0) { // can't have bogus topics, but empty payloads are ok
if (topic.empty()) {
return; return;
} }
/*
// check for empty JSON doc - we don't like those // check for empty JSON doc - we don't like those
size_t capacity = measureJson(payload); size_t capacity = measureJson(payload);
if (capacity <= 3) { if (capacity <= 3) {
// DEBUG_LOG(("Empty JSON for topic %s. Skipping"), topic); // DEBUG_LOG(("Empty JSON payload for topic %s. Skipping"), topic);
return; return;
} }
*/
std::string payload_text; std::string payload_text;
serializeJson(payload, 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 // 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 // can't have bogus topics, but empty payloads are ok
if (strlen(topic) == 0) { if (topic.empty()) {
return; return;
} }
@@ -525,29 +529,29 @@ void Mqtt::queue_subscribe_message(const std::string & topic) {
} }
// MQTT Publish, using a specific retain flag // 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); queue_publish_message(topic, payload, retain);
} }
// Publish using the user's custom retain flag // 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_); 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_); 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); 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_); queue_publish_message(topic, value ? "1" : "0", mqtt_retain_);
} }
// no payload // no payload
void Mqtt::publish(const char * topic) { void Mqtt::publish(const std::string & topic) {
queue_publish_message(topic, "", mqtt_retain_); queue_publish_message(topic, "", mqtt_retain_);
} }
@@ -634,10 +638,10 @@ void Mqtt::process_queue() {
} }
mqtt_messages_.pop_front(); // remove the message from the queue mqtt_messages_.pop_front(); // remove the message from the queue
} // namespace emsesp }
// add console commands // add console commands
void Mqtt::console_commands() { void Mqtt::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command( EMSESPShell::commands->add_command(
ShellContext::MQTT, ShellContext::MQTT,
CommandFlags::ADMIN, CommandFlags::ADMIN,
@@ -853,6 +857,9 @@ void Mqtt::console_commands() {
shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time()); shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time());
shell.println(); shell.println();
}); });
} // namespace emsesp
// enter the context
Console::enter_custom_context(shell, context);
}
} // namespace emsesp } // namespace emsesp

View File

@@ -39,6 +39,8 @@
#include <uuid/log.h> #include <uuid/log.h>
using uuid::console::Shell;
#define EMSESP_MAX_JSON_SIZE_SMALL 200 // for smaller json docs #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_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 #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 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 subscribe(const std::string & topic, mqtt_function_p cb);
static void publish(const char * topic, const char * payload); static void publish(const std::string & topic, const std::string & payload);
static void publish(const char * topic, const char * payload, bool retain); static void publish(const std::string & topic, const std::string & payload, bool retain);
static void publish(const char * topic, const JsonDocument & payload); static void publish(const std::string & topic, const JsonDocument & payload);
static void publish(const char * topic, const JsonDocument & payload, bool retain); static void publish(const std::string & topic, const JsonDocument & payload, bool retain);
static void publish(const char * topic, const bool value); static void publish(const std::string & topic, const bool value);
static void publish(const char * topic); static void publish(const std::string & topic);
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_id); 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 void incoming(char * topic, char * payload); // for testing
@@ -130,8 +132,8 @@ class Mqtt {
static bool mqtt_retain_; static bool mqtt_retain_;
static void queue_publish_message(const char * topic, const JsonDocument & 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 char * topic, const std::string & 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); static void queue_subscribe_message(const std::string & topic);

View File

@@ -346,6 +346,7 @@ void Network::show_network(uuid::console::Shell & shell) {
shell.printfln(F("WiFi: unknown")); shell.printfln(F("WiFi: unknown"));
break; break;
} }
shell.println(); shell.println();
#endif #endif
} }

View File

@@ -54,20 +54,20 @@ void Solar::show_values(uuid::console::Shell & shell) {
char buffer[10]; // used for formatting char buffer[10]; // used for formatting
print_value(shell, F("Collector temperature (TS1)"), F_(degrees), Helpers::render_value(buffer, collectorTemp_, 10)); print_value(shell, 2, 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, 2, 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, 2, 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, 2, 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, 2, 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("Pump (PS1) active"), Helpers::render_value(buffer, pump_, EMS_VALUE_BOOL));
if (pumpWorkMin_ != EMS_VALUE_ULONG_NOTSET) { 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); 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, 2, 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, 2, 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 total"), F_(kwh), Helpers::render_value(buffer, energyTotal_, 10));
} }
// publish values via MQTT // publish values via MQTT

View File

@@ -334,7 +334,7 @@ void System::show_system(uuid::console::Shell & shell) {
} }
// console commands to add // console commands to add
void System::console_commands() { void System::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::SYSTEM, EMSESPShell::commands->add_command(ShellContext::SYSTEM,
CommandFlags::ADMIN, CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(hostname)}, flash_string_vector{F_(set), F_(hostname)},
@@ -509,7 +509,6 @@ void System::console_commands() {
flash_string_vector{F_(show)}, flash_string_vector{F_(show)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
Network::show_network(shell); Network::show_network(shell);
shell.println();
show_system(shell); show_system(shell);
shell.println(); shell.println();
}); });
@@ -522,7 +521,7 @@ void System::console_commands() {
shell.printfln(F_(hostname_fmt), shell.printfln(F_(hostname_fmt),
settings.hostname().empty() ? uuid::read_flash_string(F_(unset)).c_str() : settings.hostname().c_str()); 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.printfln("Wifi:");
shell.print(" "); shell.print(" ");
shell.printfln(F_(wifi_ssid_fmt), shell.printfln(F_(wifi_ssid_fmt),
@@ -540,6 +539,9 @@ void System::console_commands() {
shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval()); shell.printfln(F_(mark_interval_fmt), settings.syslog_mark_interval());
} }
}); });
// enter the context
Console::enter_custom_context(shell, context);
} }

View File

@@ -43,6 +43,8 @@
#include <uuid/log.h> #include <uuid/log.h>
using uuid::console::Shell;
namespace emsesp { namespace emsesp {
class System { class System {
@@ -63,7 +65,7 @@ class System {
static void show_mem(const char * text); static void show_mem(const char * text);
static void console_commands(); static void console_commands(Shell & shell, unsigned int context);
private: private:
static uuid::log::Logger logger_; static uuid::log::Logger logger_;

View File

@@ -1,12 +1,12 @@
// create some fake test data // create some fake test data
// used with the 'test' command, under su/admin // used with the 'test' command, under su/admin
void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command) { void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command) {
// A fake response - UBADevices(0x07) - only for testing offline
if (command == "devices") { if (command == "devices") {
rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); // this is important otherwise nothing will be picked up! 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)); 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}; 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)); rxservice_.add(t0, sizeof(t0));
@@ -21,6 +21,20 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
return; 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") { if (command == "thermostats") {
shell.printfln(F("Testing adding devices on the EMS bus...")); shell.printfln(F("Testing adding devices on the EMS bus..."));

View File

@@ -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_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 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) { if (flags == EMSdevice::EMS_DEVICE_FLAG_RC10) {
monitor_typeids = {0xB1}; monitor_typeids = {0xB1};
set_typeids = {0xB0}; 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)); 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)) { } else if ((flags == EMSdevice::EMS_DEVICE_FLAG_RC35) || (flags == EMSdevice::EMS_DEVICE_FLAG_RC30_1)) {
monitor_typeids = {0x3E, 0x48, 0x52, 0x5C}; monitor_typeids = {0x3E, 0x48, 0x52, 0x5C};
set_typeids = {0x3D, 0x47, 0x51, 0x5B}; 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)); 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) { } else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC20) {
monitor_typeids = {0x91}; monitor_typeids = {0x91};
set_typeids = {0xA8}; 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)); 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) { } else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC20_2) {
monitor_typeids = {0xAE}; monitor_typeids = {0xAE};
set_typeids = {0xAD}; 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)); 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) { } else if (flags == EMSdevice::EMS_DEVICE_FLAG_RC30) {
monitor_typeids = {0x41}; monitor_typeids = {0x41};
set_typeids = {0xA7}; 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)); 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) { } else if (flags == EMSdevice::EMS_DEVICE_FLAG_EASY) {
monitor_typeids = {0x0A}; monitor_typeids = {0x0A};
set_typeids = {}; set_typeids = {};
register_telegram_type(monitor_typeids[0], F("EasyMonitor"), true, std::bind(&Thermostat::process_EasyMonitor, this, _1)); 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)) { } else if ((flags == EMSdevice::EMS_DEVICE_FLAG_RC300) || (flags == EMSdevice::EMS_DEVICE_FLAG_RC100)) {
monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8}; monitor_typeids = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
set_typeids = {0x02B9, 0x02BA, 0x02BB, 0x02BC}; 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)); 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) { } else if (flags == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
monitor_typeids = {0x6F, 0x70, 0x71, 0x72}; monitor_typeids = {0x6F, 0x70, 0x71, 0x72};
set_typeids = {0x65, 0x66, 0x67, 0x68}; set_typeids = {0x65, 0x66, 0x67, 0x68};
@@ -194,8 +202,7 @@ void Thermostat::add_context_menu() {
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(thermostat)}, flash_string_vector{F_(thermostat)},
[&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [&](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
dynamic_cast<EMSESPShell &>(shell).enter_custom_context(ShellContext::THERMOSTAT); Thermostat::console_commands(shell, ShellContext::THERMOSTAT);
console_commands();
}); });
} }
@@ -326,7 +333,7 @@ bool Thermostat::updated_values() {
} }
return false; return false;
} // namespace emsesp }
// publish values via MQTT // publish values via MQTT
void Thermostat::publish_values() { 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()); 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; bool has_data = false;
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc; StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
@@ -364,6 +371,7 @@ void Thermostat::publish_values() {
} }
has_data = true; has_data = true;
// if the MQTT format is 'nested' then create the parent object hc<n>
if (mqtt_format_ == Settings::MQTT_format::NESTED) { if (mqtt_format_ == Settings::MQTT_format::NESTED) {
// create nested json for each HC // create nested json for each HC
char hc_name[10]; // hc{1-4} 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 // decodes the thermostat mode for the heating circuit based on the thermostat type
// modes are off, manual, auto, day and night // modes are off, manual, auto, day and night
uint8_t Thermostat::HeatingCircuit::get_mode(uint8_t flags) const { 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 flags &= 0x0F; // strip top 4 bits
if (flags == EMSdevice::EMS_DEVICE_FLAG_RC20) { 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 // 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: case HeatingCircuit::Mode::NOFROST:
return read_flash_string(F("nofrost")); return read_flash_string(F("nofrost"));
break; break;
default:
case HeatingCircuit::Mode::AUTO: case HeatingCircuit::Mode::AUTO:
return read_flash_string(F("auto")); return read_flash_string(F("auto"));
break; 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 char buffer[10]; // for formatting only
if (datetime_.size()) { 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) { 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 uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits
if (flags == EMS_DEVICE_FLAG_RC35) { if (flags == EMS_DEVICE_FLAG_RC35) {
print_value(shell, F("Damped Outdoor temperature"), F_(degrees), Helpers::render_value(buffer, dampedoutdoortemp, 1)); print_value(shell, 1, 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, 1, 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("Tempsensor 2"), F_(degrees), Helpers::render_value(buffer, tempsensor2, 10));
} }
for (const auto & hc : heating_circuits_) { 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 // different thermostat types store their temperature values differently
uint8_t format_setpoint, format_curr; uint8_t format_setpoint, format_curr;
@@ -677,83 +692,87 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
break; break;
} }
print_value(shell, F("Current room temperature"), F_(degrees), Helpers::render_value(buffer, hc->curr_roomTemp, format_curr)); print_value(shell, 2, 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, 2, 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()); if (hc->mode != EMS_VALUE_UINT_NOTSET) {
print_value(shell, F("Mode Type"), mode_tostring(hc->get_mode_type(flags)).c_str()); 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 ((flags == EMS_DEVICE_FLAG_RC35) || (flags == EMS_DEVICE_FLAG_RC30_1)) {
if (hc->summer_mode) { 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) { } 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, 2, 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, 2, 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("Vacation temperature"), F_(degrees), Helpers::render_value(buffer, hc->holidaytemp, 2));
if (hc->offsettemp < 100) { 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 // show flow temp if we have it
if (hc->circuitcalctemp != EMS_VALUE_UINT_NOTSET) { 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 // settings parameters
if (ibaMainDisplay != EMS_VALUE_UINT_NOTSET) { if (ibaMainDisplay != EMS_VALUE_UINT_NOTSET) {
if (ibaMainDisplay == 0) { if (ibaMainDisplay == 0) {
shell.printfln(F(" Display: internal temperature")); shell.printfln(F(" Display: internal temperature"));
} else if (ibaMainDisplay == 1) { } else if (ibaMainDisplay == 1) {
shell.printfln(F(" Display: internal setpoint")); shell.printfln(F(" Display: internal setpoint"));
} else if (ibaMainDisplay == 2) { } else if (ibaMainDisplay == 2) {
shell.printfln(F(" Display: external temperature")); shell.printfln(F(" Display: external temperature"));
} else if (ibaMainDisplay == 3) { } else if (ibaMainDisplay == 3) {
shell.printfln(F(" Display: burner temperature")); shell.printfln(F(" Display: burner temperature"));
} else if (ibaMainDisplay == 4) { } else if (ibaMainDisplay == 4) {
shell.printfln(F(" Display: WW temperature")); shell.printfln(F(" Display: WW temperature"));
} else if (ibaMainDisplay == 5) { } else if (ibaMainDisplay == 5) {
shell.printfln(F(" Display: functioning mode")); shell.printfln(F(" Display: functioning mode"));
} else if (ibaMainDisplay == 6) { } else if (ibaMainDisplay == 6) {
shell.printfln(F(" Display: time")); shell.printfln(F(" Display: time"));
} else if (ibaMainDisplay == 7) { } else if (ibaMainDisplay == 7) {
shell.printfln(F(" Display: date")); shell.printfln(F(" Display: date"));
} else if (ibaMainDisplay == 9) { } else if (ibaMainDisplay == 9) {
shell.printfln(F(" Display: smoke temperature")); shell.printfln(F(" Display: smoke temperature"));
} }
} }
if (ibaLanguage != EMS_VALUE_UINT_NOTSET) { if (ibaLanguage != EMS_VALUE_UINT_NOTSET) {
if (ibaLanguage == 0) { if (ibaLanguage == 0) {
shell.printfln(F(" Language: German")); shell.printfln(F(" Language: German"));
} else if (ibaLanguage == 1) { } else if (ibaLanguage == 1) {
shell.printfln(F(" Language: Dutch")); shell.printfln(F(" Language: Dutch"));
} else if (ibaLanguage == 2) { } else if (ibaLanguage == 2) {
shell.printfln(F(" Language: French")); shell.printfln(F(" Language: French"));
} else if (ibaLanguage == 3) { } else if (ibaLanguage == 3) {
shell.printfln(F(" Language: Italian")); shell.printfln(F(" Language: Italian"));
} }
} }
if (ibaCalIntTemperature != EMS_VALUE_INT_NOTSET) { 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) { 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 != EMS_VALUE_UINT_NOTSET) {
if (ibaBuildingType == 0) { if (ibaBuildingType == 0) {
shell.printfln(F(" Building: light")); shell.printfln(F(" Building: light"));
} else if (ibaBuildingType == 1) { } else if (ibaBuildingType == 1) {
shell.printfln(F(" Building: medium")); shell.printfln(F(" Building: medium"));
} else if (ibaBuildingType == 2) { } 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 // add console commands
void Thermostat::console_commands() { void Thermostat::console_commands(Shell & shell, unsigned int context) {
EMSESPShell::commands->add_command(ShellContext::THERMOSTAT, EMSESPShell::commands->add_command(ShellContext::THERMOSTAT,
CommandFlags::ADMIN, CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(master)}, flash_string_vector{F_(set), F_(master)},
@@ -1318,6 +1337,9 @@ void Thermostat::console_commands() {
: Helpers::hextoa(buffer, settings.master_thermostat())); : Helpers::hextoa(buffer, settings.master_thermostat()));
shell.println(); shell.println();
}); });
// enter the context
Console::enter_custom_context(shell, context);
} }
} // namespace emsesp } // namespace emsesp

View File

@@ -76,7 +76,7 @@ class Thermostat : public EMSdevice {
return set_typeid_; 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: private:
uint8_t hc_num_; uint8_t hc_num_;
@@ -103,7 +103,7 @@ class Thermostat : public EMSdevice {
private: private:
static uuid::log::Logger logger_; static uuid::log::Logger logger_;
void console_commands(); void console_commands(Shell & shell, unsigned int context);
void init_mqtt(); void init_mqtt();
std::string datetime_; // date and time stamp std::string datetime_; // date and time stamp

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "2.0.0a4" #define EMSESP_APP_VERSION "2.0.0a6"