This commit is contained in:
MichaelDvP
2025-10-23 13:32:39 +02:00
94 changed files with 1698 additions and 1100 deletions

View File

@@ -203,7 +203,7 @@
{163, DeviceType::WATER, "SM100, MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::WATER, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
{248, DeviceType::MIXER, "HM210", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{17, DeviceType::CONNECT, "MX400", DeviceFlags::EMS_DEVICE_FLAG_NONE} // 0x50 Wirelss Base
{17, DeviceType::CONNECT, "MX400", DeviceFlags::EMS_DEVICE_FLAG_NONE} // 0x50 Wireless Base
// {157, DeviceType::THERMOSTAT, "RC120", DeviceFlags::EMS_DEVICE_FLAG_CR120}
#endif

View File

@@ -29,22 +29,22 @@ static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe");
namespace emsesp {
// Static member definitions
std::deque<std::unique_ptr<EMSdevice>> EMSESP::emsdevices{};
std::vector<EMSESP::Device_record> EMSESP::device_library_;
uuid::log::Logger EMSESP::logger_{F_(emsesp), uuid::log::Facility::KERN};
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE;
uint8_t EMSESP::watch_ = 0;
uint16_t EMSESP::read_id_ = WATCH_ID_NONE;
bool EMSESP::read_next_ = false;
uint16_t EMSESP::publish_id_ = 0;
uint16_t EMSESP::response_id_ = 0;
bool EMSESP::tap_water_active_ = false;
uint8_t EMSESP::publish_all_idx_ = 0;
uint8_t EMSESP::unique_id_count_ = 0;
bool EMSESP::trace_raw_ = false;
uint16_t EMSESP::wait_validate_ = 0;
bool EMSESP::wait_km_ = false;
uint32_t EMSESP::last_fetch_ = 0;
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices{};
std::vector<EMSESP::Device_record> EMSESP::device_library_;
uuid::log::Logger EMSESP::logger_{F_(emsesp), uuid::log::Facility::KERN};
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE;
uint8_t EMSESP::watch_ = 0;
uint16_t EMSESP::read_id_ = WATCH_ID_NONE;
bool EMSESP::read_next_ = false;
uint16_t EMSESP::publish_id_ = 0;
uint16_t EMSESP::response_id_ = 0;
bool EMSESP::tap_water_active_ = false;
uint8_t EMSESP::publish_all_idx_ = 0;
uint8_t EMSESP::unique_id_count_ = 0;
bool EMSESP::trace_raw_ = false;
uint16_t EMSESP::wait_validate_ = 0;
bool EMSESP::wait_km_ = false;
uint32_t EMSESP::last_fetch_ = 0;
AsyncWebServer webServer(80);

View File

@@ -222,7 +222,7 @@ class EMSESP {
static void scan_devices();
static void clear_all_devices();
static std::deque<std::unique_ptr<EMSdevice>> emsdevices;
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
// services
static Mqtt mqtt_;

View File

@@ -787,8 +787,8 @@ std::string Helpers::toUpper(std::string const & s) {
}
// capitalizes one UTF-8 character in char array
// works with Latin1 (1 byte), Polish amd some other (2 bytes) characters
// TODO add special characters that occur in other supported languages
// works with Latin1 (1 byte), Polish and other (2 bytes) characters
// supports special characters for all 11 supported languages: EN, DE, NL, SV, PL, NO, FR, TR, IT, SK, CZ
#if defined(EMSESP_STANDALONE)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
@@ -803,23 +803,77 @@ void Helpers::CharToUpperUTF8(char * c) {
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
*p -= 0x20;
}
// Additional special characters for supported languages
switch (p_v) {
case (char)0xA0: // à -> À
case (char)0xA1: // á -> Á
case (char)0xA2: // â -> Â
case (char)0xA3: // ã -> Ã
case (char)0xA4: // ä -> Ä (German, Swedish)
case (char)0xA5: // å -> Å (Swedish, Norwegian)
case (char)0xA6: // æ -> Æ (Norwegian)
case (char)0xA7: // ç -> Ç (French, Turkish)
case (char)0xA8: // è -> È (French, Italian)
case (char)0xA9: // é -> É (French, Italian)
case (char)0xAA: // ê -> Ê (French)
case (char)0xAB: // ë -> Ë (French)
case (char)0xAC: // ì -> Ì (Italian)
case (char)0xAD: // í -> Í (Slovak, Czech)
case (char)0xAE: // î -> Î (French)
case (char)0xAF: // ï -> Ï (French)
case (char)0xB0: // ð -> Ð (Icelandic)
case (char)0xB1: // ñ -> Ñ (Spanish)
case (char)0xB2: // ò -> Ò (Italian)
case (char)0xB3: // ó -> Ó (Slovak, Czech)
case (char)0xB4: // ô -> Ô (French, Slovak)
case (char)0xB5: // õ -> Õ (Portuguese)
case (char)0xB6: // ö -> Ö (German, Swedish, Turkish)
case (char)0xB8: // ø -> Ø (Norwegian)
case (char)0xB9: // ù -> Ù (French, Italian)
case (char)0xBA: // ú -> Ú (Slovak, Czech)
case (char)0xBB: // û -> Û (French)
case (char)0xBC: // ü -> Ü (German, French, Turkish)
case (char)0xBD: // ý -> Ý (Slovak, Czech)
case (char)0xBE: // þ -> Þ (Icelandic)
case (char)0xBF: // ÿ -> Ÿ (French)
*p -= 0x20;
break;
}
break;
case (char)0xC4:
switch (p_v) {
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84) (Polish)
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86) (Polish)
case (char)0x8D: //č (0xC4,0x8D) -> Č (0xC4,0x8C) (Slovak, Czech)
case (char)0x8F: //ď (0xC4,0x8F) -> Ď (0xC4,0x8E) (Slovak, Czech)
case (char)0x9F: //ğ (0xC4,0x9F) -> Ğ (0xC4,0x9E) (Turkish)
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98) (Polish)
case (char)0x9B: //ě (0xC4,0x9B) -> Ě (0xC4,0x9A) (Czech)
case (char)0xAF: //ı (0xC4,0xAF) -> I (0xC4,0xAE) (Turkish)
case (char)0xB1: //ı (0xC4,0xB1) -> I (0xC4,0xB0) (Turkish)
case (char)0xB3: //ij (0xC4,0xB3) -> IJ (0xC4,0xB2) (Dutch)
*p -= 1;
break;
}
break;
case (char)0xC5:
switch (p_v) {
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
case (char)0x81: //ł (0xC5,0x81) -> Ł (0xC5,0x80) (Polish)
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81) (Polish)
case (char)0x83: //ń (0xC5,0x83) -> Ń (0xC5,0x82) (Polish)
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83) (Polish)
case (char)0x88: //ň (0xC5,0x88) -> Ň (0xC5,0x87) (Slovak, Czech)
case (char)0x95: //ŕ (0xC5,0x95) -> Ŕ (0xC5,0x94) (Slovak)
case (char)0x99: //ř (0xC5,0x99) -> Ř (0xC5,0x98) (Czech)
case (char)0x9A: //ś (0xC5,0x9A) -> Ś (0xC5,0x99) (Polish)
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A) (Polish)
case (char)0x9F: //ş (0xC5,0x9F) -> Ş (0xC5,0x9E) (Turkish)
case (char)0xA1: //š (0xC5,0xA1) -> Š (0xC5,0xA0) (Slovak, Czech)
case (char)0xA5: //ť (0xC5,0xA5) -> Ť (0xC5,0xA4) (Slovak, Czech)
case (char)0xAF: //ů (0xC5,0xAF) -> Ů (0xC5,0xAE) (Czech)
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9) (Polish)
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB) (Polish)
case (char)0xBE: //ž (0xC5,0xBE) -> Ž (0xC5,0xBD) (Slovak, Czech)
*p -= 1;
break;
}

View File

@@ -378,27 +378,28 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roomsensor), 199, 1), // roomsensor
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatup), 200, 1), // heatup
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(mode), 0, 1), // mode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 1, 1), // settemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 2, 1), // settemplow
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 3, 1), // circmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 4, 1), // chargeduration
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 5, 1), // charge
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 6, 1), // extra
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 7, 1), // disinfecting
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 8, 1), // disinfectday
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 9, 1), // disinfecttime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 10, 1), // dailyheating
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 11, 1), // dailyheattime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 12, 1), // progmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 13, 1), // circprog
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 14, 1), // disinfecthour
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 15, 1), // maxtemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 16, 1), // onetimekey
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(switchtime), 17, 8), // switchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 25, 8), // circswitchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 33, 13), // holidays
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(vacations), 46, 13), // vacations
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwWhenModeOff), 59, 1), // whenmodeoff
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(modetype), 1, 1), // modetype
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 2, 1), // settemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 3, 1), // settemplow
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 4, 1), // circmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 5, 1), // chargeduration
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 6, 1), // charge
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 7, 1), // extra
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 8, 1), // disinfecting
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 9, 1), // disinfectday
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 10, 1), // disinfecttime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 11, 1), // dailyheating
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 12, 1), // dailyheattime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 13, 1), // progmode
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 14, 1), // circprog
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 15, 1), // disinfecthour
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 16, 1), // maxtemp
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 17, 1), // onetimekey
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(switchtime), 18, 8), // switchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 26, 8), // circswitchtime
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 34, 13), // holidays
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(vacations), 47, 13), // vacations
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwWhenModeOff), 60, 1), // whenmodeoff
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowTempHc), 0, 1), // flowtemphc
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(valveStatus), 1, 1), // valvestatus
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowSetTemp), 2, 1), // flowsettemp
@@ -523,6 +524,8 @@ const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_ma
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(flowTempHc), 1, 1), // flowtemphc
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(status), 2, 1), // status
REGISTER_MAPPING(dt::CONTROLLER, TAG_TYPE_DEVICE_DATA, FL_(dateTime), 0, 13), // datetime
REGISTER_MAPPING(dt::CONNECT, TAG_TYPE_DEVICE_DATA, FL_(dateTime), 0, 13), // datetime
REGISTER_MAPPING(dt::CONNECT, TAG_TYPE_DEVICE_DATA, FL_(outdoorTemp), 13, 1), // outdoortemp
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setFlowTemp), 0, 1), // setflowtemp
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setBurnPow), 1, 1), // setburnpow
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(flowTempVf), 0, 1), // flowtempvf

View File

@@ -151,7 +151,7 @@ void Shower::loop() {
// turn off hot water to send a shot of cold
void Shower::shower_alert_start() {
LOG_DEBUG("Shower Alert started");
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", 9);
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", DeviceValueTAG::TAG_DHW1);
doing_cold_shot_ = true;
force_coldshot = false;
alert_timer_start_ = uuid::get_uptime_sec(); // timer starts now
@@ -161,7 +161,7 @@ void Shower::shower_alert_start() {
void Shower::shower_alert_stop() {
if (doing_cold_shot_) {
LOG_DEBUG("Shower Alert stopped");
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "true", 9);
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "true", DeviceValueTAG::TAG_DHW1);
doing_cold_shot_ = false;
force_coldshot = false;
next_alert_ += shower_alert_trigger_;

View File

@@ -22,7 +22,7 @@
#include "shuntingYard.h"
// find tokens
// find tokens - optimized to reduce string allocations
std::deque<Token> exprToTokens(const std::string & expr) {
std::deque<Token> tokens;
@@ -40,13 +40,14 @@ std::deque<Token> exprToTokens(const std::string & expr) {
if (*p) {
++p;
}
auto s = std::string(b, p);
auto n = s.find("\"\"");
while (n != std::string::npos) {
s.erase(n, 2);
// Use string_view to avoid unnecessary string copies
std::string_view s(b, p - b);
auto n = s.find("\"\"");
while (n != std::string_view::npos) {
s.remove_prefix(n + 2);
n = s.find("\"\"");
}
tokens.emplace_back(Token::Type::String, s, -3);
tokens.emplace_back(Token::Type::String, std::string(s), -3);
if (*p == '\0') {
--p;
}
@@ -225,11 +226,14 @@ std::deque<Token> exprToTokens(const std::string & expr) {
return tokens;
}
// sort tokens to RPN form
// sort tokens to RPN form - optimized for memory usage
std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
std::deque<Token> queue;
std::vector<Token> stack;
// Reserve space for vector to reduce reallocations
stack.reserve(tokens.size() / 2);
// While there are tokens to be read:
for (auto const & token : tokens) {
// Read a token

View File

@@ -135,7 +135,7 @@ void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
has_update(rc->dewtemp_, dt);
}
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in ofset 0
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in offset 0
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 00 4B 00 FC 00 63 00 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (offset 1)
void Connect::process_roomThermostatName(std::shared_ptr<const Telegram> telegram) {
auto rc = room_circuit(telegram->type_id - 0xB3D);

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.3-dev.20"
#define EMSESP_APP_VERSION "3.7.3-dev.21"

View File

@@ -263,8 +263,8 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) {
if (cmd == "src") {
EMSESP::logger().notice("Adding SRC plus thermostat...");
add_device(0x50, 17); // MX400 module
uart_telegram("50 00 FF 00 0A DD 00 E6 36 2A"); // monitor, temperatures
add_device(0x50, 17); // MX400 module
uart_telegram("50 00 FF 00 0A DD 00 E6 36 2A"); // monitor, temperatures
uart_telegram("50 00 FF 00 0A B5 00 FF 00 24 01 FF 24 00"); // mode, childlock
// switchprogram
uart_telegram("50 00 FF 00 0A 65 2A 00 3C 2A FF FF 2A FF FF 2A FF FF 2A FF FF 2A FF FF");
@@ -764,7 +764,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
test("boiler");
// device type, command, data
Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", 9);
Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", DeviceValueTAG::TAG_DHW1);
ok = true;
}