mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-09-13 13:44:10 +00:00
Merge remote-tracking branch 'emsesp/core3' into core3
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
#include "WiFiScanner.h"
|
||||
|
||||
#include "../core/psram_async_json_response.h"
|
||||
|
||||
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
securityManager->addEndpoint(server, SCAN_NETWORKS_SERVICE_PATH, AuthenticationPredicates::IS_ADMIN, [this](AsyncWebServerRequest * request) {
|
||||
scanNetworks(request);
|
||||
@@ -40,4 +38,4 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||
} else {
|
||||
request->send(202); // special code to indicate scan in progress
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,12 +207,12 @@ class AnalogSensor {
|
||||
uint32_t sensorreads_ = 0;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static void IRAM_ATTR freqIrq0();
|
||||
static void IRAM_ATTR freqIrq1();
|
||||
static void IRAM_ATTR freqIrq2();
|
||||
static void IRAM_ATTR freqIrq0();
|
||||
static void IRAM_ATTR freqIrq1();
|
||||
static void IRAM_ATTR freqIrq2();
|
||||
static volatile unsigned long edge[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
||||
static volatile unsigned long edgecnt[3]; // written from freqIrqN() ISRs, read from the main measure() loop (partly outside the critical section)
|
||||
unsigned long lastedge[3] = {0, 0, 0};
|
||||
unsigned long lastedge[3] = {0, 0, 0};
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -381,8 +381,8 @@ uint8_t Command::call(const uint8_t device_type, const char * command, const cha
|
||||
// for the Commands device, calling a named command executes it (using its stored value)
|
||||
// rather than returning its definition, so skip the value-info lookup and fall through
|
||||
// to the registered command function. The list keywords above are still handled.
|
||||
bool is_named_command = (device_type == EMSdevice::DeviceType::COMMAND) && strcmp(cmd, F_(info)) && strcmp(cmd, F_(values))
|
||||
&& strcmp(cmd, F_(entities)) && strcmp(cmd, F_(metrics));
|
||||
bool is_named_command = (device_type == EMSdevice::DeviceType::COMMAND) && strcmp(cmd, F_(info)) && strcmp(cmd, F_(values)) && strcmp(cmd, F_(entities))
|
||||
&& strcmp(cmd, F_(metrics));
|
||||
if (!is_named_command && EMSESP::get_device_value_info(output, cmd, id, device_type)) { // entity = cmd
|
||||
LOG_DEBUG("Fetched device entity/attributes for %s/%s (id=%d)", dname, cmd, id);
|
||||
return CommandRet::OK;
|
||||
|
||||
+6
-3
@@ -59,7 +59,7 @@ using cmd_function_p = std::function<bool(const char * data, const int8_t id, Js
|
||||
class Command {
|
||||
public:
|
||||
struct CmdFunction {
|
||||
uint8_t device_type_; // DeviceType::
|
||||
uint8_t device_type_; // DeviceType::
|
||||
uint8_t device_id_;
|
||||
uint8_t flags_; // mqtt flags for command subscriptions
|
||||
bool has_json_output_; // true if the command writes JSON output; such commands bypass the readonly check
|
||||
@@ -116,8 +116,11 @@ class Command {
|
||||
static void
|
||||
add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
// command that writes a JSON object as its output; bypasses the readonly check
|
||||
static void
|
||||
add_json(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
static void add_json(const uint8_t device_type,
|
||||
const char * cmd,
|
||||
const cmd_function_p cb,
|
||||
const char * const * description,
|
||||
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
|
||||
static void reserve(size_t num) {
|
||||
cmdfunctions_.reserve(num);
|
||||
|
||||
@@ -351,10 +351,10 @@ bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max) {
|
||||
|
||||
// extract custom min from custom_fullname
|
||||
bool DeviceValue::get_custom_min(int16_t & val) {
|
||||
const auto & cf = custom_fullname();
|
||||
auto min_pos = cf.find('>');
|
||||
bool has_min = (min_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
const auto & cf = custom_fullname();
|
||||
auto min_pos = cf.find('>');
|
||||
bool has_min = (min_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
if (has_min) {
|
||||
int32_t v = Helpers::atoint(cf.substr(min_pos + 1).c_str());
|
||||
if (fahrenheit) {
|
||||
@@ -370,10 +370,10 @@ bool DeviceValue::get_custom_min(int16_t & val) {
|
||||
|
||||
// extract custom max from custom_fullname
|
||||
bool DeviceValue::get_custom_max(uint32_t & val) {
|
||||
const auto & cf = custom_fullname();
|
||||
auto max_pos = cf.find('<');
|
||||
bool has_max = (max_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
const auto & cf = custom_fullname();
|
||||
auto max_pos = cf.find('<');
|
||||
bool has_max = (max_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
if (has_max) {
|
||||
int32_t v = Helpers::atoint(cf.substr(max_pos + 1).c_str());
|
||||
if (fahrenheit) {
|
||||
|
||||
+10
-10
@@ -1884,16 +1884,16 @@ void EMSESP::loop() {
|
||||
if (EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD
|
||||
&& EMSESP::system_.systemStatus() != SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING) {
|
||||
// loop through the services
|
||||
webStatusService.loop(); // periodic refresh of cached versions.json
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
shower_.loop(); // check for shower on/off
|
||||
temperaturesensor_.loop(); // read sensor temperatures
|
||||
analogsensor_.loop(); // read analog sensor values
|
||||
publish_all_loop(); // with HA messages in parts to avoid flooding the MQTT queue
|
||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||
webModulesService.loop(); // loop through the external library modules
|
||||
webSchedulerService.loop(); // scheduler timing logic; command execution is offloaded to WebCommandService's worker task
|
||||
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
webStatusService.loop(); // periodic refresh of cached versions.json
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
shower_.loop(); // check for shower on/off
|
||||
temperaturesensor_.loop(); // read sensor temperatures
|
||||
analogsensor_.loop(); // read analog sensor values
|
||||
publish_all_loop(); // with HA messages in parts to avoid flooding the MQTT queue
|
||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||
webModulesService.loop(); // loop through the external library modules
|
||||
webSchedulerService.loop(); // scheduler timing logic; command execution is offloaded to WebCommandService's worker task
|
||||
scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
compact_entities_if_stable(); // reclaim over-reserved entity vector capacity once device discovery settles
|
||||
}
|
||||
// check for GPIO Errors - this is called once when booting
|
||||
|
||||
+33
-29
@@ -31,8 +31,8 @@ uint8_t Roomctrl::type_[HCS] = {RemoteType::NONE, RemoteType::NONE, Rem
|
||||
uint32_t Roomctrl::timeout_ = 0;
|
||||
|
||||
/**
|
||||
* set the temperature,
|
||||
*/
|
||||
* set the temperature,
|
||||
*/
|
||||
void Roomctrl::set_timeout(uint8_t t) {
|
||||
timeout_ = t * 3600000; // ms
|
||||
}
|
||||
@@ -86,8 +86,8 @@ uint8_t Roomctrl::get_hc(uint8_t addr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* if remote control is active send the temperature every 15 seconds
|
||||
*/
|
||||
* if remote control is active send the temperature every 15 seconds
|
||||
*/
|
||||
void Roomctrl::send(uint8_t addr) {
|
||||
if ((addr & 0x80) || EMSESP::system_.readonly_mode()) {
|
||||
return;
|
||||
@@ -143,8 +143,8 @@ void Roomctrl::send(uint8_t addr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* check if there is a message for the remote room controller
|
||||
*/
|
||||
* check if there is a message for the remote room controller
|
||||
*/
|
||||
void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
||||
if (EMSESP::system_.readonly_mode()) {
|
||||
return;
|
||||
@@ -190,8 +190,8 @@ void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
||||
}
|
||||
|
||||
/**
|
||||
* send version info
|
||||
*/
|
||||
* send version info
|
||||
*/
|
||||
void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
uint8_t data[20];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
@@ -265,8 +265,8 @@ void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* unknown message id, we reply with empty message
|
||||
*/
|
||||
* unknown message id, we reply with empty message
|
||||
*/
|
||||
void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset) {
|
||||
uint8_t data[10];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
@@ -290,8 +290,8 @@ void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh,
|
||||
}
|
||||
|
||||
/**
|
||||
* send the room temperature in message 0xAF
|
||||
*/
|
||||
* send the room temperature in message 0xAF
|
||||
*/
|
||||
void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
uint8_t data[14];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
@@ -377,25 +377,29 @@ void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
|
||||
// send telegram 0x047B only for RC100H
|
||||
void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
int16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]);
|
||||
int8_t dew8 = EMS_VALUE_INT8_NOTSET;
|
||||
if (dew != EMS_VALUE_INT16_NOTSET) {
|
||||
dew8 = static_cast<int8_t>((dew >= 0 ? dew + 5 : dew - 5) / 10);
|
||||
}
|
||||
uint8_t data[11];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
uint16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]);
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x7B + hc;
|
||||
data[6] = dew == EMS_VALUE_INT16_NOTSET ? EMS_VALUE_INT8_NOTSET : (uint8_t)((dew + 5) / 10);
|
||||
data[7] = remotehum_[hc];
|
||||
data[8] = (uint8_t)(dew << 8);
|
||||
data[9] = (uint8_t)(dew & 0xFF);
|
||||
data[10] = EMSbus::calculate_crc(data, 10); // append CRC
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x7B + hc;
|
||||
data[6] = static_cast<uint8_t>(dew8);
|
||||
data[7] = remotehum_[hc];
|
||||
data[8] = static_cast<uint8_t>(static_cast<uint16_t>(dew) >> 8);
|
||||
data[9] = static_cast<uint8_t>(static_cast<uint16_t>(dew) & 0xFF);
|
||||
data[10] = EMSbus::calculate_crc(data, 10); // append CRC
|
||||
EMSuart::transmit(data, 11);
|
||||
}
|
||||
|
||||
/**
|
||||
* send a nack if someone want to write to us.
|
||||
*/
|
||||
* send a nack if someone want to write to us.
|
||||
*/
|
||||
void Roomctrl::nack_write() {
|
||||
uint8_t data[1];
|
||||
data[0] = TxService::TX_WRITE_FAIL;
|
||||
@@ -403,8 +407,8 @@ void Roomctrl::nack_write() {
|
||||
}
|
||||
|
||||
/**
|
||||
* send a ack if someone want to write to us.
|
||||
*/
|
||||
* send a ack if someone want to write to us.
|
||||
*/
|
||||
void Roomctrl::ack_write() {
|
||||
uint8_t data[1];
|
||||
data[0] = TxService::TX_WRITE_SUCCESS;
|
||||
@@ -449,4 +453,4 @@ int16_t Roomctrl::calc_dew(int16_t temp, uint8_t humi) {
|
||||
}
|
||||
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -34,7 +34,8 @@ class Roomctrl {
|
||||
static bool is_remote(const uint8_t hc) {
|
||||
return (hc < 4 && remotetemp_[hc] != EMS_VALUE_INT16_NOTSET);
|
||||
}
|
||||
static void set_timeout(uint8_t t);
|
||||
static void set_timeout(uint8_t t);
|
||||
static int16_t calc_dew(int16_t temp, uint8_t hum);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t SEND_INTERVAL = 15000; // 15 sec
|
||||
@@ -50,7 +51,6 @@ class Roomctrl {
|
||||
static void nack_write();
|
||||
static void ack_write();
|
||||
static void replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc);
|
||||
static int16_t calc_dew(int16_t temp, uint8_t hum);
|
||||
|
||||
static bool switch_off_[HCS];
|
||||
static uint32_t send_time_[HCS];
|
||||
@@ -64,4 +64,4 @@ class Roomctrl {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -334,9 +334,12 @@ std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
|
||||
return queue;
|
||||
}
|
||||
|
||||
// check if string is a number
|
||||
// check if string is a number. Must contain at least one digit
|
||||
bool isnum(const std::string & s) {
|
||||
if (!s.empty() && (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos))) {
|
||||
if (s.empty() || s.find_first_of("0123456789") == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
if (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -499,6 +502,18 @@ std::string calculate(const std::string & expr) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (token.str[0] == 'h') {
|
||||
// hex string to number
|
||||
if (rhs.empty() || rhs.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
stack.push_back(to_string(std::stoi(rhs, 0, 16)));
|
||||
break;
|
||||
}
|
||||
// bail out on non-numeric operand
|
||||
if (!isnum(rhs)) {
|
||||
return "";
|
||||
}
|
||||
auto rhd = std::stod(rhs);
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
@@ -531,9 +546,6 @@ std::string calculate(const std::string & expr) {
|
||||
case 'p':
|
||||
stack.push_back(to_string(std::pow(rhd, 2)));
|
||||
break;
|
||||
case 'h':
|
||||
stack.push_back(to_string(std::stoi(rhs, 0, 16)));
|
||||
break;
|
||||
case 'x':
|
||||
stack.push_back(to_hex(static_cast<int>(rhd)));
|
||||
break;
|
||||
@@ -641,6 +653,10 @@ std::string calculate(const std::string & expr) {
|
||||
stack.push_back(lhs + rhs);
|
||||
break;
|
||||
}
|
||||
// bail out on non-numeric operands
|
||||
if (!isnum(lhs) || !isnum(rhs)) {
|
||||
return "";
|
||||
}
|
||||
auto lhd = std::stod(lhs);
|
||||
auto rhd = std::stod(rhs);
|
||||
switch (token.str[0]) {
|
||||
|
||||
+13
-10
@@ -816,16 +816,16 @@ void Thermostat::process_RemoteTemp(const std::shared_ptr<const Telegram> & tele
|
||||
// e.g. "38 10 FF 00 03 7B 08 24 00 4B"
|
||||
void Thermostat::process_RemoteHumidity(const std::shared_ptr<const Telegram> & telegram) {
|
||||
// has_update(telegram, dewtemperature_, 0); // this is int8
|
||||
has_update(telegram, humidity_, 1);
|
||||
has_update(telegram, dewtemperature_, 2); // this is int16
|
||||
|
||||
// some thermostats use short telegram with int8 dewpoint, https://github.com/emsesp/EMS-ESP32/issues/1491
|
||||
if (telegram->offset == 0 && telegram->message_length < 4) {
|
||||
int8_t dew = dewtemperature_ / 10;
|
||||
telegram->read_value(dew, 0);
|
||||
if (dew != EMS_VALUE_INT8_NOTSET && dewtemperature_ != dew * 10) {
|
||||
dewtemperature_ = dew * 10;
|
||||
has_update(dewtemperature_);
|
||||
}
|
||||
// but it's not a dewpoint in offset 0, removed, see https://github.com/emsesp/EMS-ESP32/issues/3135
|
||||
has_update(telegram, humidity_, 1);
|
||||
// has_update(telegram, dewtemperature_, 2); // this is int16
|
||||
int16_t dew = EMS_VALUE_INT16_NOTSET;
|
||||
if (telegram->read_value(dew, 2)) {
|
||||
has_update(dewtemperature_, dew);
|
||||
} else if (telegram->offset == 0 && telegram->message_length < 4) {
|
||||
has_update(dewtemperature_, Roomctrl::calc_dew(tempsensor1_, humidity_));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3077,7 +3077,6 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
|
||||
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
|
||||
data[7] = (id == 0) ? 2 : tm_->tm_isdst + 2; // set DST and flag for ext. clock
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||
data[6]++; // Junkers use 1-7;
|
||||
data[7] = 0;
|
||||
}
|
||||
} else if (dt.length() == 23) {
|
||||
@@ -3101,6 +3100,10 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||
data[6]++; // Junkers use 1-7 for day of the week
|
||||
}
|
||||
|
||||
// LOG_INFO("Setting date and time: %02d.%02d.2%03d-%02d:%02d:%02d-%d-%d", data[3], data[1], data[0], data[2], data[4], data[5], data[6], data[7]);
|
||||
write_command(EMS_TYPE_time, 0, data, 8, EMS_TYPE_time);
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.9.0-dev.14"
|
||||
#define EMSESP_APP_VERSION "3.9.0-dev.15"
|
||||
|
||||
@@ -83,8 +83,8 @@ class WebCommandService : public StatefulService<WebCommands> {
|
||||
bool queueCommand(const char * name, const char * value = nullptr);
|
||||
bool dispatchCommand(const char * name, const char * value = nullptr);
|
||||
|
||||
static bool isUrlCommand(const std::string & command); // true if the command definition is a HTTP/URL command
|
||||
static bool valueContainsUrl(const std::string & value); // true if a value embeds a {"url":...} compute() will fetch
|
||||
static bool isUrlCommand(const std::string & command); // true if the command definition is a HTTP/URL command
|
||||
static bool valueContainsUrl(const std::string & value); // true if a value embeds a {"url":...} compute() will fetch
|
||||
|
||||
const CommandItem * find(const char * name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user