Skip to content

Commit

Permalink
Merge pull request #1873 from MichaelDvP/dev
Browse files Browse the repository at this point in the history
Scheduler web access and others
  • Loading branch information
proddy authored Jul 20, 2024
2 parents 5603360 + c3296cc commit a0ba0e8
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 15 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG_LATEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
- allow device name to be customized [#1174](https://github.com/emsesp/EMS-ESP32/issues/1174)
- Modbus support by @mheyse [#1744](https://github.com/emsesp/EMS-ESP32/issues/1744)
- System Message command [#1854](https://github.com/emsesp/EMS-ESP32/issues/1854)
- scheduler can use web get/post for values and commands [#1806](https://github.com/emsesp/EMS-ESP32/issues/1806)
- RT800 remote emulation [#1867](https://github.com/emsesp/EMS-ESP32/issues/1867)
- RC310 cooling parameters [#1857](https://github.com/emsesp/EMS-ESP32/issues/1857)

## Fixed

Expand Down
2 changes: 1 addition & 1 deletion src/device_library.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
{192, DeviceType::THERMOSTAT, "FW120", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},

// Thermostat remote - 0x38
{ 3, DeviceType::THERMOSTAT, "RT800", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
{ 3, DeviceType::THERMOSTAT, "RT800/RC220", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
{200, DeviceType::THERMOSTAT, "RC100H", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
{249, DeviceType::THERMOSTAT, "TR120RF/CR20RF", DeviceFlags::EMS_DEVICE_FLAG_RC100H},

Expand Down
43 changes: 42 additions & 1 deletion src/devices/thermostat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hc->fastHeatup, 10);
}

// types 0x471 ff
// types 0x471 ff summer2_typeids
// (0x473), data: 00 11 04 01 01 1C 08 04
void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram) {
auto hc = heating_circuit(telegram);
Expand All @@ -1156,6 +1156,9 @@ void Thermostat::process_RC300Summer2(std::shared_ptr<const Telegram> telegram)
has_update(telegram, hc->heatondelay, 2);
has_update(telegram, hc->heatoffdelay, 3);
has_update(telegram, hc->instantstart, 4);
has_update(telegram, hc->coolstart, 5);
has_update(telegram, hc->coolondelay, 6);
has_update(telegram, hc->cooloffdelay, 7);
}

// types 0x29B ff
Expand Down Expand Up @@ -2248,6 +2251,35 @@ bool Thermostat::set_cooling(const char * value, const int8_t id) {
return true;
}

// set cooling delays
bool Thermostat::set_coolondelay(const char * value, const int8_t id) {
auto hc = heating_circuit(id);
if (hc == nullptr) {
return false;
}

int v;
if (!Helpers::value2number(value, v)) {
return false;
}
write_command(summer2_typeids[hc->hc()], 6, v, summer2_typeids[hc->hc()]);
return true;
}

bool Thermostat::set_cooloffdelay(const char * value, const int8_t id) {
auto hc = heating_circuit(id);
if (hc == nullptr) {
return false;
}

int v;
if (!Helpers::value2number(value, v)) {
return false;
}
write_command(summer2_typeids[hc->hc()], 7, v, summer2_typeids[hc->hc()]);
return true;
}

// sets the thermostat ww circulation working mode, where mode is a string
bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
uint8_t dhw = id2dhw(id);
Expand Down Expand Up @@ -3564,6 +3596,12 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
validate_typeid = set_typeid;
factor = 1;
break;
case HeatingCircuit::Mode::COOLSTART:
offset = 5;
set_typeid = summer2_typeids[hc->hc()];
validate_typeid = set_typeid;
factor = 1;
break;
case HeatingCircuit::Mode::MANUAL:
if (model == EMSdevice::EMS_DEVICE_FLAG_CR120) {
offset = 22; // manual offset CR120
Expand Down Expand Up @@ -4470,6 +4508,9 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->instantstart, DeviceValueType::UINT8, FL_(instantstart), DeviceValueUOM::K, MAKE_CF_CB(set_instantstart), 1, 10);
register_device_value(tag, &hc->boost, DeviceValueType::BOOL, FL_(boost), DeviceValueUOM::NONE, MAKE_CF_CB(set_boost));
register_device_value(tag, &hc->boosttime, DeviceValueType::UINT8, FL_(boosttime), DeviceValueUOM::HOURS, MAKE_CF_CB(set_boosttime));
register_device_value(tag, &hc->coolstart, DeviceValueType::UINT8, FL_(coolstart), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_coolstart), 20, 35);
register_device_value(tag, &hc->coolondelay, DeviceValueType::UINT8, FL_(coolondelay), DeviceValueUOM::HOURS, MAKE_CF_CB(set_coolondelay), 1, 48);
register_device_value(tag, &hc->cooloffdelay, DeviceValueType::UINT8, FL_(cooloffdelay), DeviceValueUOM::HOURS, MAKE_CF_CB(set_cooloffdelay), 1, 48);

break;
case EMSdevice::EMS_DEVICE_FLAG_CRF:
Expand Down
9 changes: 9 additions & 0 deletions src/devices/thermostat.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ class Thermostat : public EMSdevice {
uint8_t hpmode;
uint8_t cooling;
uint8_t coolingon;
uint8_t coolstart; // starttemperature 20-35°C
uint8_t coolondelay; // 1-48 hours
uint8_t cooloffdelay; // 1-48 hours
// RC300
uint8_t heatoffdelay; // 1-48h
uint8_t heatondelay; // 1-48h
Expand Down Expand Up @@ -152,6 +155,7 @@ class Thermostat : public EMSdevice {
DAYLOW,
DAYMID,
REMOTESELTEMP,
COOLSTART,
UNKNOWN

};
Expand Down Expand Up @@ -562,6 +566,9 @@ class Thermostat : public EMSdevice {
inline bool set_remoteseltemp(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::REMOTESELTEMP);
}
inline bool set_coolstart(const char * value, const int8_t id) {
return set_temperature_value(value, id, HeatingCircuit::Mode::COOLSTART);
}

// set functions - these don't use the id/hc, the parameters are ignored
bool set_wwmode(const char * value, const int8_t id);
Expand Down Expand Up @@ -646,6 +653,8 @@ class Thermostat : public EMSdevice {
bool set_hpminflowtemp(const char * value, const int8_t id);
bool set_hpmode(const char * value, const int8_t id);
bool set_cooling(const char * value, const int8_t id);
bool set_coolondelay(const char * value, const int8_t id);
bool set_cooloffdelay(const char * value, const int8_t id);
};

} // namespace emsesp
Expand Down
2 changes: 1 addition & 1 deletion src/emsesp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,7 +1489,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {

// if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0
// not for response to raw send commands without read_id set
if ((response_id_ == 0 || read_id_ > 0) && (length >= 31) && (txservice_.read_next_tx(data[3], length) == read_id_)) {
if ((response_id_ == 0 || read_id_ > 0) && (txservice_.read_next_tx(data[3], length) == read_id_)) {
read_next_ = true;
txservice_.send();
} else {
Expand Down
5 changes: 2 additions & 3 deletions src/locale_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,7 @@ MAKE_NOTRANSLATION(rc100, "RC100")
MAKE_NOTRANSLATION(rc100h, "RC100H")
MAKE_NOTRANSLATION(tc100, "TC100")
MAKE_NOTRANSLATION(rc120rf, "RC120RF")
MAKE_NOTRANSLATION(rc220, "RC220")
MAKE_NOTRANSLATION(rt800, "RT800")
MAKE_NOTRANSLATION(rc220, "RC220/RT800")
MAKE_NOTRANSLATION(single, "single")
MAKE_NOTRANSLATION(dash, "-")
MAKE_NOTRANSLATION(BLANK, "")
Expand Down Expand Up @@ -347,7 +346,7 @@ MAKE_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
MAKE_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
MAKE_ENUM(enum_roominfluence, FL_(off), FL_(intern), FL_(extern), FL_(auto))
MAKE_ENUM(enum_control1, FL_(rc310), FL_(rc200), FL_(rc100), FL_(rc100h), FL_(tc100))
MAKE_ENUM(enum_control2, FL_(off), FL_(dash), FL_(rc100), FL_(rc100h), FL_(dash), FL_(rc120rf), FL_(rt800), FL_(single)) // BC400
MAKE_ENUM(enum_control2, FL_(off), FL_(dash), FL_(rc100), FL_(rc100h), FL_(dash), FL_(rc120rf), FL_(rc220), FL_(single)) // BC400

MAKE_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))

Expand Down
3 changes: 3 additions & 0 deletions src/locale_translations.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,9 @@ MAKE_TRANSLATION(dewoffset, "dewoffset", "dew point offset", "Taupunkt Differenz
MAKE_TRANSLATION(roomtempdiff, "roomtempdiff", "room temp difference", "Raumtemperatur Differenz", "Verschiltemperatuur kamertemp", "", "różnica temp. pomieszczenia", "", "", "oda sıcaklığı farkı", "differenza temperatura ambiente", "rozdiel izbovej teploty") // TODO translate
MAKE_TRANSLATION(hpminflowtemp, "hpminflowtemp", "HP min. flow temp.", "WP minimale Vorlauftemperatur", "Minimale aanvoertemperatuur WP", "", "pompa ciepła, min. temperatura przepływu", "", "", "yüksek güç minimum akış sıcaklığı", "temperatura minima di mandata", "VT min. teplota prietoku.") // TODO translate
MAKE_TRANSLATION(hpcooling, "cooling", "cooling", "Kühlen", "Koelen", "Kyler", "chłodzenie", "kjøling", "refroidissement", "soğuma", "raffreddamento", "chladenie")
MAKE_TRANSLATION(coolstart, "coolstart", "cooling starttemp", "Kühlbetrieb ab") // TODO translate
MAKE_TRANSLATION(coolondelay, "coolondelay", "cooling on delay", "Einschaltverzögerung Kühlen") // TODO translate
MAKE_TRANSLATION(cooloffdelay, "cooloffdelay", "cooling off delay", "Ausschaltverzögerung Kühlen") // TODO translate

// heatpump and RC100H
MAKE_TRANSLATION(airHumidity, "airhumidity", "relative air humidity", "relative Luftfeuchte", "Relatieve luchtvochtigheid", "Relativ Luftfuktighet", "wilgotność względna w pomieszczeniu", "luftfuktighet", "humidité relative air", "havadaki bağıl nem", "umidità relativa aria", "relatívna vlhkosť vzduchu")
Expand Down
5 changes: 3 additions & 2 deletions src/telegram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,11 +638,12 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
uint16_t TxService::read_next_tx(const uint8_t offset, const uint8_t length) {
uint8_t old_length = telegram_last_->type_id > 0xFF ? length - 7 : length - 5;
uint8_t next_length = telegram_last_->type_id > 0xFF ? EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 2 : EMS_MAX_TELEGRAM_MESSAGE_LENGTH;
uint8_t next_offset = telegram_last_->offset + old_length;
uint8_t next_offset = offset + old_length;
uint8_t message_data = (UINT8_MAX - next_offset) >= next_length ? next_length : UINT8_MAX - next_offset;
// check telegram, offset and overflow
// some telegrams only reply with one byte less, but have higher offsets (0x10)
if (old_length >= (next_length - 1) && telegram_last_->offset == offset) {
// some reply with higher offset than requestes and have not_set values intermediate (0xEA)
if (old_length >= (next_length - 1) || telegram_last_->offset < offset) {
add(Telegram::Operation::TX_READ, telegram_last_->dest, telegram_last_->type_id, next_offset, &message_data, 1, 0, true);
return telegram_last_->type_id;
}
Expand Down
2 changes: 1 addition & 1 deletion src/version.h
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.0-dev.25"
#define EMSESP_APP_VERSION "3.7.0-dev.26"
38 changes: 33 additions & 5 deletions src/web/WebSchedulerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "emsesp.h"
#include "WebSchedulerService.h"
#include <HTTPClient.h>

namespace emsesp {

Expand Down Expand Up @@ -330,6 +331,33 @@ bool WebSchedulerService::has_commands() {

// execute scheduled command
bool WebSchedulerService::command(const char * name, const char * cmd, const char * data) {
// check http commands. e.g.
// tasmota(get): http://<tasmotsIP>/cm?cmnd=power%20ON
// shelly(get): http://<shellyIP>/relais/0?turn=on
const char * c = strchr(cmd, '{');
if (c) { // parse json
JsonDocument doc;
int httpResult = 0;
if (DeserializationError::Ok == deserializeJson(doc, c)) {
HTTPClient http;
String url = doc["url"];
if (http.begin(url)) {
for (JsonPair p : doc["header"].as<JsonObject>()) {
http.addHeader(p.key().c_str(), p.value().as<String>().c_str());
}
String value = doc["value"] | "";
if (value.length()) {
httpResult = http.POST(value);
} else if (data && data[0] != '\0') { // post
httpResult = http.POST(String(data));
} else {
httpResult = http.GET();
}
http.end();
}
}
return httpResult > 0;
}
JsonDocument doc_input;
JsonObject input = doc_input.to<JsonObject>();
if (strlen(data)) { // empty data queries a value
Expand Down Expand Up @@ -390,12 +418,12 @@ void WebSchedulerService::condition() {
#ifdef EMESESP_DEBUG
// emsesp::EMSESP::logger().debug("condition match: %s", match.c_str());
#endif
if (!match.empty() && match[0] == '1') {
if (scheduleItem.retry_cnt == 0xFF) { // default unswitched
scheduleItem.retry_cnt = command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 1 : 0xFF;
}
} else if (scheduleItem.retry_cnt == 1) {
if (match.length() == 1 && match[0] == '1' && scheduleItem.retry_cnt == 0xFF) {
scheduleItem.retry_cnt = command(scheduleItem.name.c_str(), scheduleItem.cmd.c_str(), compute(scheduleItem.value).c_str()) ? 1 : 0xFF;
} else if (match.length() == 1 && match[0] == '0' && scheduleItem.retry_cnt == 1) {
scheduleItem.retry_cnt = 0xFF;
} else if (match.length() != 1) { // the match is not boolean
emsesp::EMSESP::logger().debug("condition result: %s", match.c_str());
}
}
}
Expand Down
63 changes: 62 additions & 1 deletion src/web/shuntingYard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ std::deque<Token> exprToTokens(const std::string & expr) {
for (const auto * p = expr.c_str(); *p; ++p) {
if (isblank(*p)) {
// do nothing
} else if (*p == '{') { // json is stored as string including {}
const auto * b = p;
++p;
uint8_t i = 1;
while (*p && i > 0) {
i += (*p == '{') ? 1 : (*p == '}') ? -1 : 0;
++p;
}
if (*p) {
++p;
}
const auto s = std::string(b, p);
tokens.push_back(Token{Token::Type::String, s, -3});
if (*p == '\0') {
--p;
}
} else if (*p >= 'a' && *p <= 'z') {
const auto * b = p;
while ((*p >= 'a' && *p <= 'z') || (*p == '_')) {
Expand Down Expand Up @@ -579,7 +595,52 @@ std::string calculate(const std::string & expr) {

// check for multiple instances of <cond> ? <expr1> : <expr2>
std::string compute(const std::string & expr) {
auto expr_new = expr;
auto expr_new = emsesp::Helpers::toLower(expr);

// search json with url:
auto f = expr_new.find_first_of("{");
while (f != std::string::npos) {
auto e = f + 1;
for (uint8_t i = 1; i > 0; e++) {
if (e >= expr_new.length()) {
return "";
} else if (expr_new[e] == '}') {
i--;
} else if (expr_new[e] == '{') {
i++;
}
}
std::string cmd = expr_new.substr(f, e - f).c_str();
JsonDocument doc;
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
HTTPClient http;
String url = doc["url"];
if (http.begin(url)) {
int httpResult = 0;
for (JsonPair p : doc["header"].as<JsonObject>()) {
http.addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
}
String data = doc["value"] | "";
if (data.length()) {
httpResult = http.POST(data);
} else {
httpResult = http.GET();
}
if (httpResult > 0) {
std::string result = emsesp::Helpers::toLower(http.getString().c_str());
String key = doc["key"] | "";
doc.clear();
if (key.length() && DeserializationError::Ok == deserializeJson(doc, result)) {
result = doc[key.c_str()].as<std::string>();
}
expr_new.replace(f, e - f, result.c_str());
}
http.end();
}
}
f = expr_new.find_first_of("{", e);
}

// positions: q-questionmark, c-colon
auto q = expr_new.find_first_of("?");
while (q != std::string::npos) {
Expand Down

0 comments on commit a0ba0e8

Please sign in to comment.