mirror of
https://github.com/esphome/esphome.git
synced 2026-09-15 17:18:40 +00:00
Merge upstream/dev, resolve conflict keeping LibreTiny sta_state_ guard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,10 +6,7 @@
|
||||
#ifdef USE_BSEC2
|
||||
#include "bme68x_bsec2.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
namespace bme68x_bsec2 {
|
||||
namespace esphome::bme68x_bsec2 {
|
||||
|
||||
#define BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(a) (a == ALGORITHM_OUTPUT_CLASSIFICATION ? "Classification" : "Regression")
|
||||
#define BME68X_BSEC2_OPERATING_AGE_LOG(o) (o == OPERATING_AGE_4D ? "4 days" : "28 days")
|
||||
@@ -18,9 +15,19 @@ namespace bme68x_bsec2 {
|
||||
|
||||
static const char *const TAG = "bme68x_bsec2.sensor";
|
||||
|
||||
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
||||
static constexpr const char *const IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
||||
|
||||
static bool is_no_new_data_warning(int8_t status) {
|
||||
#ifdef BME68X_W_NO_NEW_DATA
|
||||
return status == BME68X_W_NO_NEW_DATA;
|
||||
#else
|
||||
return status == 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BME68xBSEC2Component::setup() {
|
||||
this->warn_if_blocking_over_ = 60; // initial reads may block for up to 60ms
|
||||
|
||||
this->bsec_status_ = bsec_init_m(&this->bsec_instance_);
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
this->mark_failed();
|
||||
@@ -114,7 +121,8 @@ void BME68xBSEC2Component::loop() {
|
||||
} else {
|
||||
this->status_clear_error();
|
||||
}
|
||||
if (this->bsec_status_ > BSEC_OK || this->bme68x_status_ > BME68X_OK) {
|
||||
const bool has_bme68x_warning = this->bme68x_status_ > BME68X_OK && !is_no_new_data_warning(this->bme68x_status_);
|
||||
if (this->bsec_status_ > BSEC_OK || has_bme68x_warning) {
|
||||
this->status_set_warning();
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
@@ -130,7 +138,7 @@ void BME68xBSEC2Component::loop() {
|
||||
|
||||
void BME68xBSEC2Component::set_config_(const uint8_t *config, uint32_t len) {
|
||||
if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) {
|
||||
ESP_LOGE(TAG, "Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE");
|
||||
ESP_LOGE(TAG, "Configuration blob too large");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
@@ -212,14 +220,12 @@ void BME68xBSEC2Component::run_() {
|
||||
if (curr_time_ns < this->bsec_settings_.next_call) {
|
||||
return;
|
||||
}
|
||||
uint8_t status;
|
||||
|
||||
ESP_LOGV(TAG, "Performing sensor run");
|
||||
|
||||
struct bme68x_conf bme68x_conf;
|
||||
this->bsec_status_ = bsec_sensor_control_m(&this->bsec_instance_, curr_time_ns, &this->bsec_settings_);
|
||||
if (this->bsec_status_ < BSEC_OK) {
|
||||
ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
|
||||
ESP_LOGW(TAG, "Fetching control settings failed (BSEC2 error code %d)", this->bsec_status_);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -235,9 +241,9 @@ void BME68xBSEC2Component::run_() {
|
||||
this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature;
|
||||
this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
|
||||
|
||||
// status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
|
||||
status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
||||
status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
|
||||
// this->bme68x_status_ = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
|
||||
this->bme68x_status_ = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
||||
this->bme68x_status_ = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
|
||||
this->op_mode_ = BME68X_FORCED_MODE;
|
||||
ESP_LOGV(TAG, "Using forced mode");
|
||||
|
||||
@@ -259,9 +265,8 @@ void BME68xBSEC2Component::run_() {
|
||||
BSEC_TOTAL_HEAT_DUR -
|
||||
(bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000));
|
||||
|
||||
status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
||||
|
||||
status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
|
||||
this->bme68x_status_ = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
||||
this->bme68x_status_ = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
|
||||
this->op_mode_ = BME68X_PARALLEL_MODE;
|
||||
ESP_LOGV(TAG, "Using parallel mode");
|
||||
}
|
||||
@@ -282,24 +287,15 @@ void BME68xBSEC2Component::run_() {
|
||||
this->trigger_time_ns_ = curr_time_ns;
|
||||
this->set_timeout("read", meas_dur / 1000, [this]() { this->read_(this->trigger_time_ns_); });
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Measurement not required");
|
||||
this->read_(curr_time_ns);
|
||||
ESP_LOGV(TAG, "Measurement not required, queueing immediate read");
|
||||
this->trigger_time_ns_ = curr_time_ns;
|
||||
this->set_timeout("read", 0, [this]() { this->read_(this->trigger_time_ns_); });
|
||||
}
|
||||
}
|
||||
|
||||
void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
|
||||
ESP_LOGV(TAG, "Reading data");
|
||||
|
||||
if (this->bsec_settings_.trigger_measurement) {
|
||||
uint8_t current_op_mode;
|
||||
this->bme68x_status_ = bme68x_get_op_mode(¤t_op_mode, &this->bme68x_);
|
||||
|
||||
if (current_op_mode == BME68X_SLEEP_MODE) {
|
||||
ESP_LOGV(TAG, "Still in sleep mode, doing nothing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->bsec_settings_.process_data) {
|
||||
ESP_LOGV(TAG, "Data processing not required");
|
||||
return;
|
||||
@@ -309,12 +305,16 @@ void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
|
||||
uint8_t nFields = 0;
|
||||
this->bme68x_status_ = bme68x_get_data(this->op_mode_, &data[0], &nFields, &this->bme68x_);
|
||||
|
||||
if (is_no_new_data_warning(this->bme68x_status_)) {
|
||||
ESP_LOGV(TAG, "BME68X did not provide new data");
|
||||
return;
|
||||
}
|
||||
if (this->bme68x_status_ != BME68X_OK) {
|
||||
ESP_LOGW(TAG, "Failed to get sensor data (BME68X error code %d)", this->bme68x_status_);
|
||||
ESP_LOGW(TAG, "Fetching data failed (BME68X error code %d)", this->bme68x_status_);
|
||||
return;
|
||||
}
|
||||
if (nFields < 1) {
|
||||
ESP_LOGD(TAG, "BME68X did not provide new data");
|
||||
ESP_LOGV(TAG, "BME68X did not provide new fields");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
|
||||
uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
|
||||
this->bsec_status_ = bsec_do_steps_m(&this->bsec_instance_, inputs, num_inputs, outputs, &num_outputs);
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
ESP_LOGW(TAG, "BSEC2 failed to process signals (BSEC2 error code %d)", this->bsec_status_);
|
||||
ESP_LOGW(TAG, "Signal processing failed (BSEC2 error code %d)", this->bsec_status_);
|
||||
return;
|
||||
}
|
||||
if (num_outputs < 1) {
|
||||
@@ -474,7 +474,7 @@ void BME68xBSEC2Component::publish_sensor_(sensor::Sensor *sensor, float value,
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void BME68xBSEC2Component::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) {
|
||||
void BME68xBSEC2Component::publish_sensor_(text_sensor::TextSensor *sensor, const char *value) {
|
||||
if (!sensor || (sensor->has_state() && sensor->state == value)) {
|
||||
return;
|
||||
}
|
||||
@@ -526,6 +526,5 @@ void BME68xBSEC2Component::save_state_(uint8_t accuracy) {
|
||||
ESP_LOGI(TAG, "Saved state");
|
||||
}
|
||||
|
||||
} // namespace bme68x_bsec2
|
||||
} // namespace esphome
|
||||
} // namespace esphome::bme68x_bsec2
|
||||
#endif
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
#include <bsec2.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace bme68x_bsec2 {
|
||||
namespace esphome::bme68x_bsec2 {
|
||||
|
||||
enum AlgorithmOutput {
|
||||
ALGORITHM_OUTPUT_IAQ,
|
||||
@@ -97,7 +96,7 @@ class BME68xBSEC2Component : public Component {
|
||||
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only = false);
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value);
|
||||
void publish_sensor_(text_sensor::TextSensor *sensor, const char *value);
|
||||
#endif
|
||||
|
||||
void load_state_();
|
||||
@@ -108,39 +107,12 @@ class BME68xBSEC2Component : public Component {
|
||||
struct bme68x_dev bme68x_;
|
||||
bsec_bme_settings_t bsec_settings_;
|
||||
bsec_version_t version_;
|
||||
uint8_t bsec_instance_[BSEC_INSTANCE_SIZE];
|
||||
|
||||
struct bme68x_heatr_conf bme68x_heatr_conf_;
|
||||
uint8_t op_mode_; // operating mode of sensor
|
||||
bsec_library_return_t bsec_status_{BSEC_OK};
|
||||
int8_t bme68x_status_{BME68X_OK};
|
||||
|
||||
int64_t last_time_ms_{0};
|
||||
int64_t trigger_time_ns_{0}; // Stored for set_timeout lambda to help avoid heap allocation on supported 32-bit
|
||||
// toolchains with small std::function SBO
|
||||
uint32_t millis_overflow_counter_{0};
|
||||
|
||||
std::queue<std::function<void()>> queue_;
|
||||
ESPPreferenceObject bsec_state_;
|
||||
|
||||
uint8_t const *bsec2_configuration_{nullptr};
|
||||
uint32_t bsec2_configuration_length_{0};
|
||||
bool bsec2_blob_configured_{false};
|
||||
|
||||
ESPPreferenceObject bsec_state_;
|
||||
uint32_t state_save_interval_ms_{21600000}; // 6 hours - 4 times a day
|
||||
uint32_t last_state_save_ms_ = 0;
|
||||
|
||||
float temperature_offset_{0};
|
||||
|
||||
AlgorithmOutput algorithm_output_{ALGORITHM_OUTPUT_IAQ};
|
||||
OperatingAge operating_age_{OPERATING_AGE_28D};
|
||||
Voltage voltage_{VOLTAGE_3_3V};
|
||||
|
||||
SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate
|
||||
SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *pressure_sensor_{nullptr};
|
||||
@@ -155,8 +127,32 @@ class BME68xBSEC2Component : public Component {
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
text_sensor::TextSensor *iaq_accuracy_text_sensor_{nullptr};
|
||||
#endif
|
||||
|
||||
int64_t last_time_ms_{0};
|
||||
int64_t trigger_time_ns_{0}; // Stored for set_timeout lambda to help avoid heap allocation on supported 32-bit
|
||||
// toolchains with small std::function SBO
|
||||
|
||||
uint32_t state_save_interval_ms_{21600000}; // 6 hours - 4 times a day
|
||||
uint32_t last_state_save_ms_{0};
|
||||
uint32_t millis_overflow_counter_{0};
|
||||
uint32_t bsec2_configuration_length_{0};
|
||||
bsec_library_return_t bsec_status_{BSEC_OK};
|
||||
|
||||
float temperature_offset_{0};
|
||||
|
||||
AlgorithmOutput algorithm_output_{ALGORITHM_OUTPUT_IAQ};
|
||||
OperatingAge operating_age_{OPERATING_AGE_28D};
|
||||
Voltage voltage_{VOLTAGE_3_3V};
|
||||
SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate
|
||||
SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
|
||||
uint8_t bsec_instance_[BSEC_INSTANCE_SIZE];
|
||||
uint8_t op_mode_; // operating mode of sensor
|
||||
int8_t bme68x_status_{BME68X_OK};
|
||||
bool bsec2_blob_configured_{false};
|
||||
};
|
||||
|
||||
} // namespace bme68x_bsec2
|
||||
} // namespace esphome
|
||||
} // namespace esphome::bme68x_bsec2
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dht {
|
||||
namespace esphome::dht {
|
||||
|
||||
static const char *const TAG = "dht";
|
||||
|
||||
@@ -45,16 +44,13 @@ void DHT::update() {
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ESP_LOGD(TAG, "Temperature %.1f°C Humidity %.1f%%", temperature, humidity);
|
||||
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
this->status_clear_warning();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid readings! Check pin number and pull-up resistor%s.",
|
||||
this->is_auto_detect_ ? " and try manually specifying the model" : "");
|
||||
ESP_LOGW(TAG, "Invalid readings");
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(NAN);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
@@ -73,8 +69,7 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
*temperature = NAN;
|
||||
|
||||
int error_code = 0;
|
||||
int8_t i = 0;
|
||||
uint8_t data[5] = {0, 0, 0, 0, 0};
|
||||
uint8_t data[5] = {};
|
||||
|
||||
#ifndef USE_ESP32
|
||||
this->pin_.pin_mode(gpio::FLAG_OUTPUT);
|
||||
@@ -107,7 +102,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
uint8_t bit = 7;
|
||||
uint8_t byte = 0;
|
||||
|
||||
for (i = -1; i < 40; i++) {
|
||||
// On 32-bit Xtensa/RISC-V cores, int8_t would require masking/sign-extension for comparisons
|
||||
// vs. native int. Using int i is native word size — small win in the timing-critical section.
|
||||
for (int i = -1; i < 40; i++) {
|
||||
uint32_t start_time = micros();
|
||||
|
||||
// Wait for rising edge
|
||||
@@ -156,11 +153,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!report_errors && error_code != 0)
|
||||
return false;
|
||||
|
||||
if (error_code) {
|
||||
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
if (error_code != 0) {
|
||||
if (report_errors)
|
||||
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -177,7 +172,7 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
|
||||
if (checksum_a != data[4] && checksum_b != data[4]) {
|
||||
if (report_errors) {
|
||||
ESP_LOGW(TAG, "Checksum invalid: %u!=%u", checksum_a, data[4]);
|
||||
ESP_LOGW(TAG, "Invalid checksum");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -234,5 +229,4 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dht
|
||||
} // namespace esphome
|
||||
} // namespace esphome::dht
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace dht {
|
||||
namespace esphome::dht {
|
||||
|
||||
enum DHTModel {
|
||||
enum DHTModel : uint8_t {
|
||||
DHT_MODEL_AUTO_DETECT = 0,
|
||||
DHT_MODEL_DHT11,
|
||||
DHT_MODEL_DHT22,
|
||||
@@ -42,7 +41,6 @@ class DHT : public PollingComponent {
|
||||
this->t_pin_ = pin;
|
||||
this->pin_ = pin->to_isr();
|
||||
}
|
||||
void set_model(DHTModel model) { model_ = model; }
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
|
||||
|
||||
@@ -55,13 +53,12 @@ class DHT : public PollingComponent {
|
||||
protected:
|
||||
bool read_sensor_(float *temperature, float *humidity, bool report_errors);
|
||||
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *humidity_sensor_{nullptr};
|
||||
InternalGPIOPin *t_pin_;
|
||||
ISRInternalGPIOPin pin_;
|
||||
DHTModel model_{DHT_MODEL_AUTO_DETECT};
|
||||
bool is_auto_detect_{false};
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *humidity_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace dht
|
||||
} // namespace esphome
|
||||
} // namespace esphome::dht
|
||||
|
||||
@@ -134,10 +134,38 @@ class HandlerCounts:
|
||||
_handler_counts = HandlerCounts()
|
||||
|
||||
|
||||
def _add_callback(
|
||||
parent_var: cg.MockObj,
|
||||
method: str,
|
||||
handler_var: cg.MockObj,
|
||||
params: str,
|
||||
call_args: str,
|
||||
) -> None:
|
||||
"""Generate a lambda callback that forwards to a handler method.
|
||||
|
||||
Uses a braced scope with a local pointer variable so the generated C++
|
||||
lambda captures only that pointer, avoiding GCC warnings about capturing
|
||||
variables with static storage duration.
|
||||
"""
|
||||
cg.add(
|
||||
cg.RawStatement(
|
||||
f"{{ auto *h = {handler_var}; "
|
||||
f"{parent_var}->{method}("
|
||||
f"[h]({params}) {{ h->{call_args}; }}); }}"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def register_gap_event_handler(parent_var: cg.MockObj, handler_var: cg.MockObj) -> None:
|
||||
"""Register a GAP event handler and track the count."""
|
||||
_handler_counts.gap_event += 1
|
||||
cg.add(parent_var.register_gap_event_handler(handler_var))
|
||||
_add_callback(
|
||||
parent_var,
|
||||
"add_gap_event_callback",
|
||||
handler_var,
|
||||
"esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param",
|
||||
"gap_event_handler(event, param)",
|
||||
)
|
||||
|
||||
|
||||
def register_gap_scan_event_handler(
|
||||
@@ -145,7 +173,13 @@ def register_gap_scan_event_handler(
|
||||
) -> None:
|
||||
"""Register a GAP scan event handler and track the count."""
|
||||
_handler_counts.gap_scan_event += 1
|
||||
cg.add(parent_var.register_gap_scan_event_handler(handler_var))
|
||||
_add_callback(
|
||||
parent_var,
|
||||
"add_gap_scan_event_callback",
|
||||
handler_var,
|
||||
"const esphome::esp32_ble::BLEScanResult &scan_result",
|
||||
"gap_scan_event_handler(scan_result)",
|
||||
)
|
||||
|
||||
|
||||
def register_gattc_event_handler(
|
||||
@@ -153,7 +187,13 @@ def register_gattc_event_handler(
|
||||
) -> None:
|
||||
"""Register a GATTc event handler and track the count."""
|
||||
_handler_counts.gattc_event += 1
|
||||
cg.add(parent_var.register_gattc_event_handler(handler_var))
|
||||
_add_callback(
|
||||
parent_var,
|
||||
"add_gattc_event_callback",
|
||||
handler_var,
|
||||
"esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param",
|
||||
"gattc_event_handler(event, gattc_if, param)",
|
||||
)
|
||||
|
||||
|
||||
def register_gatts_event_handler(
|
||||
@@ -161,7 +201,13 @@ def register_gatts_event_handler(
|
||||
) -> None:
|
||||
"""Register a GATTs event handler and track the count."""
|
||||
_handler_counts.gatts_event += 1
|
||||
cg.add(parent_var.register_gatts_event_handler(handler_var))
|
||||
_add_callback(
|
||||
parent_var,
|
||||
"add_gatts_event_callback",
|
||||
handler_var,
|
||||
"esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param",
|
||||
"gatts_event_handler(event, gatts_if, param)",
|
||||
)
|
||||
|
||||
|
||||
def register_ble_status_event_handler(
|
||||
@@ -169,7 +215,13 @@ def register_ble_status_event_handler(
|
||||
) -> None:
|
||||
"""Register a BLE status event handler and track the count."""
|
||||
_handler_counts.ble_status_event += 1
|
||||
cg.add(parent_var.register_ble_status_event_handler(handler_var))
|
||||
_add_callback(
|
||||
parent_var,
|
||||
"add_ble_status_event_callback",
|
||||
handler_var,
|
||||
"",
|
||||
"ble_before_disabled_event_handler()",
|
||||
)
|
||||
|
||||
|
||||
def register_bt_logger(*loggers: BTLoggers) -> None:
|
||||
@@ -225,10 +277,6 @@ NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
|
||||
esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble")
|
||||
ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component)
|
||||
|
||||
GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler")
|
||||
GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
|
||||
GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
|
||||
|
||||
BLEEnabledCondition = esp32_ble_ns.class_("BLEEnabledCondition", automation.Condition)
|
||||
BLEEnableAction = esp32_ble_ns.class_("BLEEnableAction", automation.Action)
|
||||
BLEDisableAction = esp32_ble_ns.class_("BLEDisableAction", automation.Action)
|
||||
|
||||
@@ -408,9 +408,7 @@ void ESP32BLE::loop() {
|
||||
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
|
||||
esp_ble_gatts_cb_param_t *param = &ble_event->event_.gatts.gatts_param;
|
||||
ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
|
||||
for (auto *gatts_handler : this->gatts_event_handlers_) {
|
||||
gatts_handler->gatts_event_handler(event, gatts_if, param);
|
||||
}
|
||||
this->gatts_event_callbacks_.call(event, gatts_if, param);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -420,9 +418,7 @@ void ESP32BLE::loop() {
|
||||
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
|
||||
esp_ble_gattc_cb_param_t *param = &ble_event->event_.gattc.gattc_param;
|
||||
ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
|
||||
for (auto *gattc_handler : this->gattc_event_handlers_) {
|
||||
gattc_handler->gattc_event_handler(event, gattc_if, param);
|
||||
}
|
||||
this->gattc_event_callbacks_.call(event, gattc_if, param);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -431,10 +427,7 @@ void ESP32BLE::loop() {
|
||||
switch (gap_event) {
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
|
||||
// Use the new scan event handler - no memcpy!
|
||||
for (auto *scan_handler : this->gap_scan_event_handlers_) {
|
||||
scan_handler->gap_scan_event_handler(ble_event->scan_result());
|
||||
}
|
||||
this->gap_scan_event_callbacks_.call(ble_event->scan_result());
|
||||
#endif
|
||||
break;
|
||||
|
||||
@@ -478,9 +471,7 @@ void ESP32BLE::loop() {
|
||||
}
|
||||
// clang-format on
|
||||
// Dispatch to all registered handlers
|
||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||
gap_handler->gap_event_handler(gap_event, param);
|
||||
}
|
||||
this->gap_event_callbacks_.call(gap_event, param);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
@@ -518,9 +509,7 @@ void ESP32BLE::loop_handle_state_transition_not_active_() {
|
||||
ESP_LOGD(TAG, "Disabling");
|
||||
|
||||
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
|
||||
for (auto *ble_event_handler : this->ble_status_event_handlers_) {
|
||||
ble_event_handler->ble_before_disabled_event_handler();
|
||||
}
|
||||
this->ble_status_event_callbacks_.call();
|
||||
#endif
|
||||
|
||||
if (!ble_dismantle_()) {
|
||||
|
||||
@@ -87,37 +87,6 @@ enum BLEComponentState : uint8_t {
|
||||
BLE_COMPONENT_STATE_ACTIVE,
|
||||
};
|
||||
|
||||
class GAPEventHandler {
|
||||
public:
|
||||
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
|
||||
};
|
||||
|
||||
class GAPScanEventHandler {
|
||||
public:
|
||||
virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0;
|
||||
};
|
||||
|
||||
#ifdef USE_ESP32_BLE_CLIENT
|
||||
class GATTcEventHandler {
|
||||
public:
|
||||
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_BLE_SERVER
|
||||
class GATTsEventHandler {
|
||||
public:
|
||||
virtual void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t *param) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
class BLEStatusEventHandler {
|
||||
public:
|
||||
virtual void ble_before_disabled_event_handler() = 0;
|
||||
};
|
||||
|
||||
class ESP32BLE : public Component {
|
||||
public:
|
||||
void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
|
||||
@@ -154,22 +123,28 @@ class ESP32BLE : public Component {
|
||||
#endif
|
||||
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
|
||||
template<typename F> void add_gap_event_callback(F &&callback) {
|
||||
this->gap_event_callbacks_.add(std::forward<F>(callback));
|
||||
}
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
|
||||
void register_gap_scan_event_handler(GAPScanEventHandler *handler) {
|
||||
this->gap_scan_event_handlers_.push_back(handler);
|
||||
template<typename F> void add_gap_scan_event_callback(F &&callback) {
|
||||
this->gap_scan_event_callbacks_.add(std::forward<F>(callback));
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
|
||||
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
|
||||
template<typename F> void add_gattc_event_callback(F &&callback) {
|
||||
this->gattc_event_callbacks_.add(std::forward<F>(callback));
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
|
||||
template<typename F> void add_gatts_event_callback(F &&callback) {
|
||||
this->gatts_event_callbacks_.add(std::forward<F>(callback));
|
||||
}
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
|
||||
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
|
||||
this->ble_status_event_handlers_.push_back(handler);
|
||||
template<typename F> void add_ble_status_event_callback(F &&callback) {
|
||||
this->ble_status_event_callbacks_.add(std::forward<F>(callback));
|
||||
}
|
||||
#endif
|
||||
void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
|
||||
@@ -202,21 +177,27 @@ class ESP32BLE : public Component {
|
||||
private:
|
||||
template<typename... Args> friend void enqueue_ble_event(Args... args);
|
||||
|
||||
// Handler vectors - use StaticVector when counts are known at compile time
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
|
||||
StaticVector<GAPEventHandler *, ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT> gap_event_handlers_;
|
||||
StaticCallbackManager<ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT,
|
||||
void(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *)>
|
||||
gap_event_callbacks_;
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT
|
||||
StaticVector<GAPScanEventHandler *, ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT> gap_scan_event_handlers_;
|
||||
StaticCallbackManager<ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT, void(const BLEScanResult &)>
|
||||
gap_scan_event_callbacks_;
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_CLIENT) && defined(ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT)
|
||||
StaticVector<GATTcEventHandler *, ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT> gattc_event_handlers_;
|
||||
StaticCallbackManager<ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT,
|
||||
void(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *)>
|
||||
gattc_event_callbacks_;
|
||||
#endif
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
StaticVector<GATTsEventHandler *, ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT> gatts_event_handlers_;
|
||||
StaticCallbackManager<ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT,
|
||||
void(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *)>
|
||||
gatts_event_callbacks_;
|
||||
#endif
|
||||
#ifdef ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT
|
||||
StaticVector<BLEStatusEventHandler *, ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT> ble_status_event_handlers_;
|
||||
StaticCallbackManager<ESPHOME_ESP32_BLE_BLE_STATUS_EVENT_HANDLER_COUNT, void()> ble_status_event_callbacks_;
|
||||
#endif
|
||||
|
||||
// Large objects (size depends on template parameters, but typically aligned to 4 bytes)
|
||||
|
||||
@@ -13,7 +13,6 @@ esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon")
|
||||
ESP32BLEBeacon = esp32_ble_beacon_ns.class_(
|
||||
"ESP32BLEBeacon",
|
||||
cg.Component,
|
||||
esp32_ble.GAPEventHandler,
|
||||
cg.Parented.template(esp32_ble.ESP32BLE),
|
||||
)
|
||||
CONF_MAJOR = "major"
|
||||
|
||||
@@ -35,7 +35,7 @@ using esp_ble_ibeacon_t = struct {
|
||||
|
||||
using namespace esp32_ble;
|
||||
|
||||
class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented<ESP32BLE> {
|
||||
class ESP32BLEBeacon : public Component, public Parented<ESP32BLE> {
|
||||
public:
|
||||
explicit ESP32BLEBeacon(const std::array<uint8_t, 16> &uuid) : uuid_(uuid) {}
|
||||
|
||||
@@ -51,7 +51,7 @@ class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented
|
||||
#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
|
||||
void set_tx_power(esp_power_level_t val) { this->tx_power_ = val; }
|
||||
#endif
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
||||
|
||||
protected:
|
||||
void on_advertise_();
|
||||
|
||||
@@ -72,7 +72,6 @@ BLECharacteristic_ns = esp32_ble_server_ns.namespace("BLECharacteristic")
|
||||
BLEServer = esp32_ble_server_ns.class_(
|
||||
"BLEServer",
|
||||
cg.Component,
|
||||
esp32_ble.GATTsEventHandler,
|
||||
cg.Parented.template(esp32_ble.ESP32BLE),
|
||||
)
|
||||
esp32_ble_server_automations_ns = esp32_ble_server_ns.namespace(
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace esp32_ble_server {
|
||||
using namespace esp32_ble;
|
||||
using namespace bytebuffer;
|
||||
|
||||
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
|
||||
class BLEServer : public Component, public Parented<ESP32BLE> {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
@@ -53,10 +53,9 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
|
||||
const uint16_t *get_clients() const { return this->clients_; }
|
||||
uint8_t get_client_count() const { return this->client_count_; }
|
||||
|
||||
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t *param) override;
|
||||
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
|
||||
void ble_before_disabled_event_handler() override;
|
||||
void ble_before_disabled_event_handler();
|
||||
|
||||
// Direct callback registration - supports multiple callbacks
|
||||
void on_connect(std::function<void(uint16_t)> &&callback) {
|
||||
|
||||
@@ -90,8 +90,6 @@ esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
|
||||
ESP32BLETracker = esp32_ble_tracker_ns.class_(
|
||||
"ESP32BLETracker",
|
||||
cg.Component,
|
||||
esp32_ble.GAPEventHandler,
|
||||
esp32_ble.GATTcEventHandler,
|
||||
cg.Parented.template(esp32_ble.ESP32BLE),
|
||||
)
|
||||
ESPBTClient = esp32_ble_tracker_ns.class_("ESPBTClient")
|
||||
|
||||
@@ -291,10 +291,6 @@ class ESPBTClient : public ESPBTDeviceListener {
|
||||
};
|
||||
|
||||
class ESP32BLETracker : public Component,
|
||||
public GAPEventHandler,
|
||||
public GAPScanEventHandler,
|
||||
public GATTcEventHandler,
|
||||
public BLEStatusEventHandler,
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
public ota::OTAGlobalStateListener,
|
||||
#endif
|
||||
@@ -325,11 +321,10 @@ class ESP32BLETracker : public Component,
|
||||
void start_scan();
|
||||
void stop_scan();
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||
void gap_scan_event_handler(const BLEScanResult &scan_result) override;
|
||||
void ble_before_disabled_event_handler() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
||||
void gap_scan_event_handler(const BLEScanResult &scan_result);
|
||||
void ble_before_disabled_event_handler();
|
||||
|
||||
#ifdef USE_OTA_STATE_LISTENER
|
||||
void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
modbus_ns = cg.esphome_ns.namespace("modbus")
|
||||
modbus_helpers_ns = modbus_ns.namespace("helpers")
|
||||
|
||||
ModbusFunctionCode_ns = modbus_ns.namespace("ModbusFunctionCode")
|
||||
ModbusFunctionCode = ModbusFunctionCode_ns.enum("ModbusFunctionCode")
|
||||
|
||||
MODBUS_FUNCTION_CODE = {
|
||||
"read_coils": ModbusFunctionCode.READ_COILS,
|
||||
"read_discrete_inputs": ModbusFunctionCode.READ_DISCRETE_INPUTS,
|
||||
"read_holding_registers": ModbusFunctionCode.READ_HOLDING_REGISTERS,
|
||||
"read_input_registers": ModbusFunctionCode.READ_INPUT_REGISTERS,
|
||||
"write_single_coil": ModbusFunctionCode.WRITE_SINGLE_COIL,
|
||||
"write_single_register": ModbusFunctionCode.WRITE_SINGLE_REGISTER,
|
||||
"write_multiple_coils": ModbusFunctionCode.WRITE_MULTIPLE_COILS,
|
||||
"write_multiple_registers": ModbusFunctionCode.WRITE_MULTIPLE_REGISTERS,
|
||||
}
|
||||
|
||||
ModbusRegisterType_ns = modbus_ns.namespace("ModbusRegisterType")
|
||||
ModbusRegisterType = ModbusRegisterType_ns.enum("ModbusRegisterType")
|
||||
|
||||
MODBUS_WRITE_REGISTER_TYPE = {
|
||||
"custom": ModbusRegisterType.CUSTOM,
|
||||
"coil": ModbusRegisterType.COIL,
|
||||
"holding": ModbusRegisterType.HOLDING,
|
||||
}
|
||||
|
||||
MODBUS_REGISTER_TYPE = {
|
||||
**MODBUS_WRITE_REGISTER_TYPE,
|
||||
"discrete_input": ModbusRegisterType.DISCRETE_INPUT,
|
||||
"read": ModbusRegisterType.READ,
|
||||
}
|
||||
|
||||
SensorValueType_ns = modbus_helpers_ns.namespace("SensorValueType")
|
||||
SensorValueType = SensorValueType_ns.enum("SensorValueType")
|
||||
SENSOR_VALUE_TYPE = {
|
||||
"RAW": SensorValueType.RAW,
|
||||
"U_WORD": SensorValueType.U_WORD,
|
||||
"S_WORD": SensorValueType.S_WORD,
|
||||
"U_DWORD": SensorValueType.U_DWORD,
|
||||
"U_DWORD_R": SensorValueType.U_DWORD_R,
|
||||
"S_DWORD": SensorValueType.S_DWORD,
|
||||
"S_DWORD_R": SensorValueType.S_DWORD_R,
|
||||
"U_QWORD": SensorValueType.U_QWORD,
|
||||
"U_QWORD_R": SensorValueType.U_QWORD_R,
|
||||
"S_QWORD": SensorValueType.S_QWORD,
|
||||
"S_QWORD_R": SensorValueType.S_QWORD_R,
|
||||
"FP32": SensorValueType.FP32,
|
||||
"FP32_R": SensorValueType.FP32_R,
|
||||
}
|
||||
|
||||
TYPE_REGISTER_MAP = {
|
||||
"RAW": 1,
|
||||
"U_WORD": 1,
|
||||
"S_WORD": 1,
|
||||
"U_DWORD": 2,
|
||||
"U_DWORD_R": 2,
|
||||
"S_DWORD": 2,
|
||||
"S_DWORD_R": 2,
|
||||
"U_QWORD": 4,
|
||||
"U_QWORD_R": 4,
|
||||
"S_QWORD": 4,
|
||||
"S_QWORD_R": 4,
|
||||
"FP32": 2,
|
||||
"FP32_R": 2,
|
||||
}
|
||||
|
||||
CPP_TYPE_REGISTER_MAP = {
|
||||
"RAW": cg.uint16,
|
||||
"U_WORD": cg.uint16,
|
||||
"S_WORD": cg.int16,
|
||||
"U_DWORD": cg.uint32,
|
||||
"U_DWORD_R": cg.uint32,
|
||||
"S_DWORD": cg.int32,
|
||||
"S_DWORD_R": cg.int32,
|
||||
"U_QWORD": cg.uint64,
|
||||
"U_QWORD_R": cg.uint64,
|
||||
"S_QWORD": cg.int64,
|
||||
"S_QWORD_R": cg.int64,
|
||||
"FP32": cg.float_,
|
||||
"FP32_R": cg.float_,
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/modbus/modbus_definitions.h"
|
||||
|
||||
namespace esphome::modbus::helpers {
|
||||
|
||||
enum class SensorValueType : uint8_t {
|
||||
RAW = 0x00, // variable length
|
||||
U_WORD = 0x1, // 1 Register unsigned
|
||||
U_DWORD = 0x2, // 2 Registers unsigned
|
||||
S_WORD = 0x3, // 1 Register signed
|
||||
S_DWORD = 0x4, // 2 Registers signed
|
||||
BIT = 0x5,
|
||||
U_DWORD_R = 0x6, // 2 Registers unsigned
|
||||
S_DWORD_R = 0x7, // 2 Registers unsigned
|
||||
U_QWORD = 0x8,
|
||||
S_QWORD = 0x9,
|
||||
U_QWORD_R = 0xA,
|
||||
S_QWORD_R = 0xB,
|
||||
FP32 = 0xC,
|
||||
FP32_R = 0xD
|
||||
};
|
||||
|
||||
inline bool value_type_is_float(SensorValueType v) {
|
||||
return v == SensorValueType::FP32 || v == SensorValueType::FP32_R;
|
||||
}
|
||||
|
||||
inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) {
|
||||
switch (reg_type) {
|
||||
case ModbusRegisterType::COIL:
|
||||
return ModbusFunctionCode::READ_COILS;
|
||||
case ModbusRegisterType::DISCRETE_INPUT:
|
||||
return ModbusFunctionCode::READ_DISCRETE_INPUTS;
|
||||
case ModbusRegisterType::HOLDING:
|
||||
return ModbusFunctionCode::READ_HOLDING_REGISTERS;
|
||||
case ModbusRegisterType::READ:
|
||||
return ModbusFunctionCode::READ_INPUT_REGISTERS;
|
||||
default:
|
||||
return ModbusFunctionCode::CUSTOM;
|
||||
}
|
||||
}
|
||||
|
||||
inline ModbusFunctionCode modbus_register_write_function(ModbusRegisterType reg_type) {
|
||||
switch (reg_type) {
|
||||
case ModbusRegisterType::COIL:
|
||||
return ModbusFunctionCode::WRITE_SINGLE_COIL;
|
||||
case ModbusRegisterType::DISCRETE_INPUT:
|
||||
return ModbusFunctionCode::CUSTOM;
|
||||
case ModbusRegisterType::HOLDING:
|
||||
return ModbusFunctionCode::READ_WRITE_MULTIPLE_REGISTERS;
|
||||
case ModbusRegisterType::READ:
|
||||
default:
|
||||
return ModbusFunctionCode::CUSTOM;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint8_t c_to_hex(char c) { return (c >= 'A') ? (c >= 'a') ? (c - 'a' + 10) : (c - 'A' + 10) : (c - '0'); }
|
||||
|
||||
/** Get a byte from a hex string
|
||||
* byte_from_hex_str("1122", 1) returns uint_8 value 0x22 == 34
|
||||
* byte_from_hex_str("1122", 0) returns 0x11
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return byte value
|
||||
*/
|
||||
inline uint8_t byte_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
if (value.length() < pos * 2 + 2)
|
||||
return 0;
|
||||
return (c_to_hex(value[pos * 2]) << 4) | c_to_hex(value[pos * 2 + 1]);
|
||||
}
|
||||
|
||||
/** Get a word from a hex string
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return word value
|
||||
*/
|
||||
inline uint16_t word_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
return byte_from_hex_str(value, pos) << 8 | byte_from_hex_str(value, pos + 1);
|
||||
}
|
||||
|
||||
/** Get a dword from a hex string
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return dword value
|
||||
*/
|
||||
inline uint32_t dword_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
return word_from_hex_str(value, pos) << 16 | word_from_hex_str(value, pos + 2);
|
||||
}
|
||||
|
||||
/** Get a qword from a hex string
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return qword value
|
||||
*/
|
||||
inline uint64_t qword_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
return static_cast<uint64_t>(dword_from_hex_str(value, pos)) << 32 | dword_from_hex_str(value, pos + 4);
|
||||
}
|
||||
|
||||
} // namespace esphome::modbus::helpers
|
||||
@@ -4,6 +4,13 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import modbus
|
||||
from esphome.components.const import CONF_ENABLED
|
||||
from esphome.components.modbus.helpers import (
|
||||
CPP_TYPE_REGISTER_MAP,
|
||||
MODBUS_REGISTER_TYPE,
|
||||
SENSOR_VALUE_TYPE,
|
||||
TYPE_REGISTER_MAP,
|
||||
ModbusRegisterType,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OFFSET
|
||||
from esphome.cpp_helpers import logging
|
||||
@@ -41,7 +48,6 @@ CONF_SERVER_REGISTERS = "server_registers"
|
||||
MULTI_CONF = True
|
||||
|
||||
modbus_controller_ns = cg.esphome_ns.namespace("modbus_controller")
|
||||
modbus_ns = cg.esphome_ns.namespace("modbus")
|
||||
ModbusController = modbus_controller_ns.class_(
|
||||
"ModbusController", cg.PollingComponent, modbus.ModbusDevice
|
||||
)
|
||||
@@ -50,85 +56,6 @@ SensorItem = modbus_controller_ns.struct("SensorItem")
|
||||
ServerCourtesyResponse = modbus_controller_ns.struct("ServerCourtesyResponse")
|
||||
ServerRegister = modbus_controller_ns.struct("ServerRegister")
|
||||
|
||||
ModbusFunctionCode_ns = modbus_ns.namespace("ModbusFunctionCode")
|
||||
ModbusFunctionCode = ModbusFunctionCode_ns.enum("ModbusFunctionCode")
|
||||
MODBUS_FUNCTION_CODE = {
|
||||
"read_coils": ModbusFunctionCode.READ_COILS,
|
||||
"read_discrete_inputs": ModbusFunctionCode.READ_DISCRETE_INPUTS,
|
||||
"read_holding_registers": ModbusFunctionCode.READ_HOLDING_REGISTERS,
|
||||
"read_input_registers": ModbusFunctionCode.READ_INPUT_REGISTERS,
|
||||
"write_single_coil": ModbusFunctionCode.WRITE_SINGLE_COIL,
|
||||
"write_single_register": ModbusFunctionCode.WRITE_SINGLE_REGISTER,
|
||||
"write_multiple_coils": ModbusFunctionCode.WRITE_MULTIPLE_COILS,
|
||||
"write_multiple_registers": ModbusFunctionCode.WRITE_MULTIPLE_REGISTERS,
|
||||
}
|
||||
|
||||
ModbusRegisterType_ns = modbus_controller_ns.namespace("ModbusRegisterType")
|
||||
ModbusRegisterType = ModbusRegisterType_ns.enum("ModbusRegisterType")
|
||||
|
||||
MODBUS_WRITE_REGISTER_TYPE = {
|
||||
"custom": ModbusRegisterType.CUSTOM,
|
||||
"coil": ModbusRegisterType.COIL,
|
||||
"holding": ModbusRegisterType.HOLDING,
|
||||
}
|
||||
|
||||
MODBUS_REGISTER_TYPE = {
|
||||
**MODBUS_WRITE_REGISTER_TYPE,
|
||||
"discrete_input": ModbusRegisterType.DISCRETE_INPUT,
|
||||
"read": ModbusRegisterType.READ,
|
||||
}
|
||||
|
||||
SensorValueType_ns = modbus_controller_ns.namespace("SensorValueType")
|
||||
SensorValueType = SensorValueType_ns.enum("SensorValueType")
|
||||
SENSOR_VALUE_TYPE = {
|
||||
"RAW": SensorValueType.RAW,
|
||||
"U_WORD": SensorValueType.U_WORD,
|
||||
"S_WORD": SensorValueType.S_WORD,
|
||||
"U_DWORD": SensorValueType.U_DWORD,
|
||||
"U_DWORD_R": SensorValueType.U_DWORD_R,
|
||||
"S_DWORD": SensorValueType.S_DWORD,
|
||||
"S_DWORD_R": SensorValueType.S_DWORD_R,
|
||||
"U_QWORD": SensorValueType.U_QWORD,
|
||||
"U_QWORD_R": SensorValueType.U_QWORD_R,
|
||||
"S_QWORD": SensorValueType.S_QWORD,
|
||||
"S_QWORD_R": SensorValueType.S_QWORD_R,
|
||||
"FP32": SensorValueType.FP32,
|
||||
"FP32_R": SensorValueType.FP32_R,
|
||||
}
|
||||
|
||||
TYPE_REGISTER_MAP = {
|
||||
"RAW": 1,
|
||||
"U_WORD": 1,
|
||||
"S_WORD": 1,
|
||||
"U_DWORD": 2,
|
||||
"U_DWORD_R": 2,
|
||||
"S_DWORD": 2,
|
||||
"S_DWORD_R": 2,
|
||||
"U_QWORD": 4,
|
||||
"U_QWORD_R": 4,
|
||||
"S_QWORD": 4,
|
||||
"S_QWORD_R": 4,
|
||||
"FP32": 2,
|
||||
"FP32_R": 2,
|
||||
}
|
||||
|
||||
CPP_TYPE_REGISTER_MAP = {
|
||||
"RAW": cg.uint16,
|
||||
"U_WORD": cg.uint16,
|
||||
"S_WORD": cg.int16,
|
||||
"U_DWORD": cg.uint32,
|
||||
"U_DWORD_R": cg.uint32,
|
||||
"S_DWORD": cg.int32,
|
||||
"S_DWORD_R": cg.int32,
|
||||
"U_QWORD": cg.uint64,
|
||||
"U_QWORD_R": cg.uint64,
|
||||
"S_QWORD": cg.int64,
|
||||
"S_QWORD_R": cg.int64,
|
||||
"FP32": cg.float_,
|
||||
"FP32_R": cg.float_,
|
||||
}
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVER_COURTESY_RESPONSE_SCHEMA = cv.Schema(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.components.modbus.helpers import MODBUS_REGISTER_TYPE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID
|
||||
|
||||
from .. import (
|
||||
MODBUS_REGISTER_TYPE,
|
||||
ModbusItemBaseSchema,
|
||||
SensorItem,
|
||||
add_modbus_base_properties,
|
||||
|
||||
@@ -535,7 +535,7 @@ ModbusCommandItem ModbusCommandItem::create_read_command(
|
||||
ModbusCommandItem cmd;
|
||||
cmd.modbusdevice = modbusdevice;
|
||||
cmd.register_type = register_type;
|
||||
cmd.function_code = modbus_register_read_function(register_type);
|
||||
cmd.function_code = modbus::helpers::modbus_register_read_function(register_type);
|
||||
cmd.register_address = start_address;
|
||||
cmd.register_count = register_count;
|
||||
cmd.on_data_func = std::move(handler);
|
||||
@@ -548,7 +548,7 @@ ModbusCommandItem ModbusCommandItem::create_read_command(ModbusController *modbu
|
||||
ModbusCommandItem cmd;
|
||||
cmd.modbusdevice = modbusdevice;
|
||||
cmd.register_type = register_type;
|
||||
cmd.function_code = modbus_register_read_function(register_type);
|
||||
cmd.function_code = modbus::helpers::modbus_register_read_function(register_type);
|
||||
cmd.register_address = start_address;
|
||||
cmd.register_count = register_count;
|
||||
cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "esphome/components/modbus/modbus.h"
|
||||
#include "esphome/components/modbus/modbus_helpers.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
#include <list>
|
||||
@@ -19,109 +20,43 @@ class ModbusController;
|
||||
using modbus::ModbusFunctionCode;
|
||||
using modbus::ModbusRegisterType;
|
||||
using modbus::ModbusExceptionCode;
|
||||
using modbus::helpers::SensorValueType;
|
||||
|
||||
enum class SensorValueType : uint8_t {
|
||||
RAW = 0x00, // variable length
|
||||
U_WORD = 0x1, // 1 Register unsigned
|
||||
U_DWORD = 0x2, // 2 Registers unsigned
|
||||
S_WORD = 0x3, // 1 Register signed
|
||||
S_DWORD = 0x4, // 2 Registers signed
|
||||
BIT = 0x5,
|
||||
U_DWORD_R = 0x6, // 2 Registers unsigned
|
||||
S_DWORD_R = 0x7, // 2 Registers unsigned
|
||||
U_QWORD = 0x8,
|
||||
S_QWORD = 0x9,
|
||||
U_QWORD_R = 0xA,
|
||||
S_QWORD_R = 0xB,
|
||||
FP32 = 0xC,
|
||||
FP32_R = 0xD
|
||||
};
|
||||
|
||||
inline bool value_type_is_float(SensorValueType v) {
|
||||
return v == SensorValueType::FP32 || v == SensorValueType::FP32_R;
|
||||
}
|
||||
// Remove before 2026.10.0 — these helpers have moved to modbus::helpers
|
||||
ESPDEPRECATED("Use modbus::helpers::value_type_is_float() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline bool value_type_is_float(SensorValueType v) { return modbus::helpers::value_type_is_float(v); }
|
||||
|
||||
ESPDEPRECATED("Use modbus::helpers::modbus_register_read_function() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) {
|
||||
switch (reg_type) {
|
||||
case ModbusRegisterType::COIL:
|
||||
return ModbusFunctionCode::READ_COILS;
|
||||
break;
|
||||
case ModbusRegisterType::DISCRETE_INPUT:
|
||||
return ModbusFunctionCode::READ_DISCRETE_INPUTS;
|
||||
break;
|
||||
case ModbusRegisterType::HOLDING:
|
||||
return ModbusFunctionCode::READ_HOLDING_REGISTERS;
|
||||
break;
|
||||
case ModbusRegisterType::READ:
|
||||
return ModbusFunctionCode::READ_INPUT_REGISTERS;
|
||||
break;
|
||||
default:
|
||||
return ModbusFunctionCode::CUSTOM;
|
||||
break;
|
||||
}
|
||||
return modbus::helpers::modbus_register_read_function(reg_type);
|
||||
}
|
||||
|
||||
ESPDEPRECATED("Use modbus::helpers::modbus_register_write_function() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline ModbusFunctionCode modbus_register_write_function(ModbusRegisterType reg_type) {
|
||||
switch (reg_type) {
|
||||
case ModbusRegisterType::COIL:
|
||||
return ModbusFunctionCode::WRITE_SINGLE_COIL;
|
||||
break;
|
||||
case ModbusRegisterType::DISCRETE_INPUT:
|
||||
return ModbusFunctionCode::CUSTOM;
|
||||
break;
|
||||
case ModbusRegisterType::HOLDING:
|
||||
return ModbusFunctionCode::READ_WRITE_MULTIPLE_REGISTERS;
|
||||
break;
|
||||
case ModbusRegisterType::READ:
|
||||
default:
|
||||
return ModbusFunctionCode::CUSTOM;
|
||||
break;
|
||||
}
|
||||
return modbus::helpers::modbus_register_write_function(reg_type);
|
||||
}
|
||||
|
||||
inline uint8_t c_to_hex(char c) { return (c >= 'A') ? (c >= 'a') ? (c - 'a' + 10) : (c - 'A' + 10) : (c - '0'); }
|
||||
ESPDEPRECATED("Use modbus::helpers::c_to_hex() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline uint8_t c_to_hex(char c) { return modbus::helpers::c_to_hex(c); }
|
||||
|
||||
/** Get a byte from a hex string
|
||||
* hex_byte_from_str("1122",1) returns uint_8 value 0x22 == 34
|
||||
* hex_byte_from_str("1122",0) returns 0x11
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return byte value
|
||||
*/
|
||||
ESPDEPRECATED("Use modbus::helpers::byte_from_hex_str() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline uint8_t byte_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
if (value.length() < pos * 2 + 1)
|
||||
return 0;
|
||||
return (c_to_hex(value[pos * 2]) << 4) | c_to_hex(value[pos * 2 + 1]);
|
||||
return modbus::helpers::byte_from_hex_str(value, pos);
|
||||
}
|
||||
|
||||
/** Get a word from a hex string
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return word value
|
||||
*/
|
||||
ESPDEPRECATED("Use modbus::helpers::word_from_hex_str() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline uint16_t word_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
return byte_from_hex_str(value, pos) << 8 | byte_from_hex_str(value, pos + 1);
|
||||
return modbus::helpers::word_from_hex_str(value, pos);
|
||||
}
|
||||
|
||||
/** Get a dword from a hex string
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return dword value
|
||||
*/
|
||||
ESPDEPRECATED("Use modbus::helpers::dword_from_hex_str() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline uint32_t dword_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
return word_from_hex_str(value, pos) << 16 | word_from_hex_str(value, pos + 2);
|
||||
return modbus::helpers::dword_from_hex_str(value, pos);
|
||||
}
|
||||
|
||||
/** Get a qword from a hex string
|
||||
* @param value string containing hex encoding
|
||||
* @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in
|
||||
* the hex string is byte_pos * 2
|
||||
* @return qword value
|
||||
*/
|
||||
ESPDEPRECATED("Use modbus::helpers::qword_from_hex_str() instead. Removed in 2026.10.0", "2026.4.0")
|
||||
inline uint64_t qword_from_hex_str(const std::string &value, uint8_t pos) {
|
||||
return static_cast<uint64_t>(dword_from_hex_str(value, pos)) << 32 | dword_from_hex_str(value, pos + 4);
|
||||
return modbus::helpers::qword_from_hex_str(value, pos);
|
||||
}
|
||||
|
||||
// Extract data from modbus response buffer
|
||||
@@ -585,7 +520,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem
|
||||
int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask);
|
||||
|
||||
float float_value;
|
||||
if (value_type_is_float(item.sensor_value_type)) {
|
||||
if (modbus::helpers::value_type_is_float(item.sensor_value_type)) {
|
||||
float_value = bit_cast<float>(static_cast<uint32_t>(number));
|
||||
} else {
|
||||
float_value = static_cast<float>(number);
|
||||
@@ -597,7 +532,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem
|
||||
inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
|
||||
int64_t val;
|
||||
|
||||
if (value_type_is_float(value_type)) {
|
||||
if (modbus::helpers::value_type_is_float(value_type)) {
|
||||
val = bit_cast<uint32_t>(value);
|
||||
} else {
|
||||
val = llroundf(value);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import number
|
||||
from esphome.components.modbus.helpers import (
|
||||
MODBUS_WRITE_REGISTER_TYPE,
|
||||
SENSOR_VALUE_TYPE,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
@@ -11,8 +15,6 @@ from esphome.const import (
|
||||
)
|
||||
|
||||
from .. import (
|
||||
MODBUS_WRITE_REGISTER_TYPE,
|
||||
SENSOR_VALUE_TYPE,
|
||||
ModbusItemBaseSchema,
|
||||
SensorItem,
|
||||
add_modbus_base_properties,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
from esphome.components.modbus.helpers import SENSOR_VALUE_TYPE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_MULTIPLY
|
||||
|
||||
from .. import (
|
||||
SENSOR_VALUE_TYPE,
|
||||
ModbusItemBaseSchema,
|
||||
SensorItem,
|
||||
modbus_calc_properties,
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
from esphome.components.modbus.helpers import SENSOR_VALUE_TYPE, TYPE_REGISTER_MAP
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_OPTIMISTIC
|
||||
|
||||
from .. import (
|
||||
SENSOR_VALUE_TYPE,
|
||||
TYPE_REGISTER_MAP,
|
||||
ModbusController,
|
||||
SensorItem,
|
||||
modbus_controller_ns,
|
||||
)
|
||||
from .. import ModbusController, SensorItem, modbus_controller_ns
|
||||
from ..const import (
|
||||
CONF_FORCE_NEW_RANGE,
|
||||
CONF_MODBUS_CONTROLLER_ID,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
from esphome.components.modbus.helpers import MODBUS_REGISTER_TYPE, SENSOR_VALUE_TYPE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID
|
||||
|
||||
from .. import (
|
||||
MODBUS_REGISTER_TYPE,
|
||||
SENSOR_VALUE_TYPE,
|
||||
ModbusItemBaseSchema,
|
||||
SensorItem,
|
||||
add_modbus_base_properties,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
from esphome.components.modbus.helpers import MODBUS_REGISTER_TYPE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ASSUMED_STATE, CONF_ID
|
||||
|
||||
from .. import (
|
||||
MODBUS_REGISTER_TYPE,
|
||||
ModbusItemBaseSchema,
|
||||
SensorItem,
|
||||
add_modbus_base_properties,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
from esphome.components.modbus.helpers import MODBUS_REGISTER_TYPE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ADDRESS, CONF_ID
|
||||
|
||||
from .. import (
|
||||
MODBUS_REGISTER_TYPE,
|
||||
ModbusItemBaseSchema,
|
||||
SensorItem,
|
||||
add_modbus_base_properties,
|
||||
|
||||
@@ -21,15 +21,15 @@ class NextionBinarySensor : public NextionComponent,
|
||||
void process_touch(uint8_t page_id, uint8_t component_id, bool state) override;
|
||||
|
||||
// Set the components page id for Nextion Touch Component
|
||||
void set_page_id(uint8_t page_id) { page_id_ = page_id; }
|
||||
void set_page_id(uint8_t page_id) { this->page_id_ = page_id; }
|
||||
// Set the components component id for Nextion Touch Component
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
void set_component_id(uint8_t component_id) { this->component_id_ = component_id; }
|
||||
|
||||
void set_state(bool state) override { this->set_state(state, true, true); }
|
||||
void set_state(bool state, bool publish) override { this->set_state(state, publish, true); }
|
||||
void set_state(bool state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
NextionQueueType get_queue_type() override { return NextionQueueType::BINARY_SENSOR; }
|
||||
NextionQueueType get_queue_type() const override { return NextionQueueType::BINARY_SENSOR; }
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value != 0, publish, send_to_nextion);
|
||||
|
||||
@@ -281,7 +281,7 @@ void Nextion::print_queue_members_() {
|
||||
ESP_LOGN(TAG, "Queue null");
|
||||
} else {
|
||||
ESP_LOGN(TAG, "Queue type: %d:%s, name: %s", i->component->get_queue_type(),
|
||||
i->component->get_queue_type_string().c_str(), i->component->get_variable_name().c_str());
|
||||
i->component->get_queue_type_string(), i->component->get_variable_name().c_str());
|
||||
}
|
||||
}
|
||||
ESP_LOGN(TAG, "*******************************************");
|
||||
@@ -423,7 +423,7 @@ void Nextion::process_nextion_commands_() {
|
||||
DELIMITER_SIZE)) != std::string::npos) {
|
||||
#ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP
|
||||
if (++commands_processed > this->max_commands_per_loop_) {
|
||||
ESP_LOGW(TAG, "Command processing limit exceeded");
|
||||
ESP_LOGV(TAG, "Command limit reached, deferring");
|
||||
break;
|
||||
}
|
||||
#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP
|
||||
@@ -607,7 +607,7 @@ void Nextion::process_nextion_commands_() {
|
||||
ESP_LOGE(TAG, "String return but '%s' not text sensor", component->get_variable_name().c_str());
|
||||
} else {
|
||||
ESP_LOGN(TAG, "String resp: '%s' id: %s type: %s", to_process.c_str(), component->get_variable_name().c_str(),
|
||||
component->get_queue_type_string().c_str());
|
||||
component->get_queue_type_string());
|
||||
}
|
||||
|
||||
delete nb; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
@@ -649,7 +649,7 @@ void Nextion::process_nextion_commands_() {
|
||||
component->get_queue_type());
|
||||
} else {
|
||||
ESP_LOGN(TAG, "Numeric: %s type %d:%s val %d", component->get_variable_name().c_str(),
|
||||
component->get_queue_type(), component->get_queue_type_string().c_str(), value);
|
||||
component->get_queue_type(), component->get_queue_type_string(), value);
|
||||
component->set_state_from_int(value, true, false);
|
||||
}
|
||||
|
||||
@@ -842,24 +842,10 @@ void Nextion::process_nextion_commands_() {
|
||||
if (this->max_q_age_ms_ > 0 && !this->nextion_queue_.empty() &&
|
||||
ms - this->nextion_queue_.front()->queue_time > this->max_q_age_ms_) {
|
||||
for (auto it = this->nextion_queue_.begin(); it != this->nextion_queue_.end();) {
|
||||
NextionComponentBase *component = (*it)->component;
|
||||
if (ms - (*it)->queue_time > this->max_q_age_ms_) {
|
||||
if ((*it)->queue_time == 0) {
|
||||
ESP_LOGD(TAG, "Remove old queue '%s':'%s' (t=0)", component->get_queue_type_string().c_str(),
|
||||
component->get_variable_name().c_str());
|
||||
}
|
||||
|
||||
if (component->get_variable_name() == "sleep_wake") {
|
||||
this->is_sleeping_ = false;
|
||||
}
|
||||
|
||||
if ((*it)->pending_command.empty()) {
|
||||
ESP_LOGD(TAG, "Remove old queue '%s':'%s'", component->get_queue_type_string().c_str(),
|
||||
component->get_variable_name().c_str());
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Remove old queue '%s':'%s' cmd:'%s'", component->get_queue_type_string().c_str(),
|
||||
component->get_variable_name().c_str(), (*it)->pending_command.c_str());
|
||||
}
|
||||
NextionComponentBase *component = (*it)->component;
|
||||
ESP_LOGV(TAG, "Remove old queue '%s':'%s'", component->get_queue_type_string(),
|
||||
component->get_variable_name().c_str());
|
||||
|
||||
if (component->get_queue_type() == NextionQueueType::NO_RESULT) {
|
||||
if (component->get_variable_name() == "sleep_wake") {
|
||||
@@ -940,7 +926,7 @@ void Nextion::all_components_send_state_(bool force_update) {
|
||||
binarysensortype->send_state_to_nextion();
|
||||
}
|
||||
for (auto *sensortype : this->sensortype_) {
|
||||
if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_chan_id() == 0)
|
||||
if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_channel_id() == 0)
|
||||
sensortype->send_state_to_nextion();
|
||||
}
|
||||
for (auto *switchtype : this->switchtype_) {
|
||||
@@ -1048,7 +1034,7 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) {
|
||||
nextion_queue->component = new nextion::NextionComponentBase;
|
||||
nextion_queue->component->set_variable_name(variable_name);
|
||||
|
||||
nextion_queue->queue_time = millis();
|
||||
nextion_queue->queue_time = App.get_loop_component_start_time();
|
||||
|
||||
this->nextion_queue_.push_back(nextion_queue);
|
||||
|
||||
@@ -1236,7 +1222,7 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) {
|
||||
nextion_queue->component = component;
|
||||
nextion_queue->queue_time = App.get_loop_component_start_time();
|
||||
|
||||
ESP_LOGN(TAG, "Queue %s: %s", component->get_queue_type_string().c_str(), component->get_variable_name().c_str());
|
||||
ESP_LOGN(TAG, "Queue %s: %s", component->get_queue_type_string(), component->get_variable_name().c_str());
|
||||
|
||||
std::string command = "get " + component->get_variable_name_to_send();
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "esphome/core/defines.h"
|
||||
@@ -35,12 +37,8 @@ class NextionComponentBase {
|
||||
virtual ~NextionComponentBase() = default;
|
||||
|
||||
void set_variable_name(const std::string &variable_name, const std::string &variable_name_to_send = "") {
|
||||
variable_name_ = variable_name;
|
||||
if (variable_name_to_send.empty()) {
|
||||
variable_name_to_send_ = variable_name;
|
||||
} else {
|
||||
variable_name_to_send_ = variable_name_to_send;
|
||||
}
|
||||
this->variable_name_ = variable_name;
|
||||
this->variable_name_to_send_ = variable_name_to_send.empty() ? variable_name : variable_name_to_send;
|
||||
}
|
||||
|
||||
virtual void update_component_settings(){};
|
||||
@@ -64,14 +62,14 @@ class NextionComponentBase {
|
||||
virtual void set_state(const std::string &state, bool publish) {}
|
||||
virtual void set_state(const std::string &state, bool publish, bool send_to_nextion){};
|
||||
|
||||
uint8_t get_component_id() { return this->component_id_; }
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
uint8_t get_component_id() const { return this->component_id_; }
|
||||
void set_component_id(uint8_t component_id) { this->component_id_ = component_id; }
|
||||
|
||||
uint8_t get_wave_channel_id() { return this->wave_chan_id_; }
|
||||
uint8_t get_wave_channel_id() const { return this->wave_chan_id_; }
|
||||
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
|
||||
|
||||
std::vector<uint8_t> get_wave_buffer() { return this->wave_buffer_; }
|
||||
size_t get_wave_buffer_size() { return this->wave_buffer_.size(); }
|
||||
const std::vector<uint8_t> &get_wave_buffer() const { return this->wave_buffer_; }
|
||||
size_t get_wave_buffer_size() const { return this->wave_buffer_.size(); }
|
||||
void clear_wave_buffer(size_t buffer_sent) {
|
||||
if (this->wave_buffer_.size() <= buffer_sent) {
|
||||
this->wave_buffer_.clear();
|
||||
@@ -80,15 +78,17 @@ class NextionComponentBase {
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_variable_name() { return this->variable_name_; }
|
||||
std::string get_variable_name_to_send() { return this->variable_name_to_send_; }
|
||||
virtual NextionQueueType get_queue_type() { return NextionQueueType::NO_RESULT; }
|
||||
virtual std::string get_queue_type_string() { return NEXTION_QUEUE_TYPE_STRINGS[this->get_queue_type()]; }
|
||||
const std::string &get_variable_name() const { return this->variable_name_; }
|
||||
const std::string &get_variable_name_to_send() const { return this->variable_name_to_send_; }
|
||||
virtual NextionQueueType get_queue_type() const { return NextionQueueType::NO_RESULT; }
|
||||
virtual const char *get_queue_type_string() const { return NEXTION_QUEUE_TYPE_STRINGS[this->get_queue_type()]; }
|
||||
virtual void set_state_from_int(int state_value, bool publish, bool send_to_nextion){};
|
||||
virtual void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion){};
|
||||
virtual void send_state_to_nextion(){};
|
||||
bool get_needs_to_send_update() { return this->needs_to_send_update_; }
|
||||
uint8_t get_wave_chan_id() { return this->wave_chan_id_; }
|
||||
bool get_needs_to_send_update() const { return this->needs_to_send_update_; }
|
||||
// Remove before 2026.10.0
|
||||
ESPDEPRECATED("Use get_wave_channel_id() instead. Will be removed in 2026.10.0", "2026.4.0")
|
||||
uint8_t get_wave_chan_id() const { return this->get_wave_channel_id(); }
|
||||
void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -17,7 +17,7 @@ class NextionSensor : public NextionComponent, public sensor::Sensor, public Pol
|
||||
void update() override;
|
||||
void add_to_wave_buffer(float state);
|
||||
void set_precision(uint8_t precision) { this->precision_ = precision; }
|
||||
void set_component_id(uint8_t component_id) { component_id_ = component_id; }
|
||||
void set_component_id(uint8_t component_id) { this->component_id_ = component_id; }
|
||||
void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; }
|
||||
void set_wave_max_value(uint32_t wave_maxvalue) { this->wave_maxvalue_ = wave_maxvalue; }
|
||||
void process_sensor(const std::string &variable_name, int state) override;
|
||||
@@ -27,9 +27,8 @@ class NextionSensor : public NextionComponent, public sensor::Sensor, public Pol
|
||||
void set_state(float state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
void set_waveform_send_last_value(bool send_last_value) { this->send_last_value_ = send_last_value; }
|
||||
uint8_t get_wave_chan_id() { return this->wave_chan_id_; }
|
||||
void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; }
|
||||
NextionQueueType get_queue_type() override {
|
||||
NextionQueueType get_queue_type() const override {
|
||||
return this->wave_chan_id_ == UINT8_MAX ? NextionQueueType::SENSOR : NextionQueueType::WAVEFORM_SENSOR;
|
||||
}
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
|
||||
|
||||
@@ -21,7 +21,7 @@ class NextionSwitch : public NextionComponent, public switch_::Switch, public Po
|
||||
void set_state(bool state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
void send_state_to_nextion() override { this->set_state(this->state, false, true); };
|
||||
NextionQueueType get_queue_type() override { return NextionQueueType::SWITCH; }
|
||||
NextionQueueType get_queue_type() const override { return NextionQueueType::SWITCH; }
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value != 0, publish, send_to_nextion);
|
||||
|
||||
@@ -22,7 +22,7 @@ class NextionTextSensor : public NextionComponent, public text_sensor::TextSenso
|
||||
void set_state(const std::string &state, bool publish, bool send_to_nextion) override;
|
||||
|
||||
void send_state_to_nextion() override { this->set_state(this->state, false, true); };
|
||||
NextionQueueType get_queue_type() override { return NextionQueueType::TEXT_SENSOR; }
|
||||
NextionQueueType get_queue_type() const override { return NextionQueueType::TEXT_SENSOR; }
|
||||
void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {}
|
||||
void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {
|
||||
this->set_state(state_value, publish, send_to_nextion);
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace shtcx {
|
||||
namespace esphome::shtcx {
|
||||
|
||||
static const char *const TAG = "shtcx";
|
||||
|
||||
static const uint16_t SHTCX_COMMAND_SLEEP = 0xB098;
|
||||
static const uint16_t SHTCX_COMMAND_WAKEUP = 0x3517;
|
||||
static const uint16_t SHTCX_COMMAND_READ_ID_REGISTER = 0xEFC8;
|
||||
static const uint16_t SHTCX_COMMAND_SOFT_RESET = 0x805D;
|
||||
static const uint16_t SHTCX_COMMAND_POLLING_H = 0x7866;
|
||||
static constexpr uint16_t SHTCX_COMMAND_SLEEP = 0xB098;
|
||||
static constexpr uint16_t SHTCX_COMMAND_WAKEUP = 0x3517;
|
||||
static constexpr uint16_t SHTCX_COMMAND_READ_ID_REGISTER = 0xEFC8;
|
||||
static constexpr uint16_t SHTCX_COMMAND_SOFT_RESET = 0x805D;
|
||||
static constexpr uint16_t SHTCX_COMMAND_POLLING_H = 0x7866;
|
||||
|
||||
static const LogString *shtcx_type_to_string(SHTCXType type) {
|
||||
switch (type) {
|
||||
@@ -91,8 +90,6 @@ void SHTCXComponent::update() {
|
||||
} else {
|
||||
temperature = 175.0f * float(raw_data[0]) / 65536.0f - 45.0f;
|
||||
humidity = 100.0f * float(raw_data[1]) / 65536.0f;
|
||||
|
||||
ESP_LOGD(TAG, "Temperature=%.2f°C Humidity=%.2f%%", temperature, humidity);
|
||||
}
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
@@ -117,5 +114,4 @@ void SHTCXComponent::wake_up() {
|
||||
delayMicroseconds(200);
|
||||
}
|
||||
|
||||
} // namespace shtcx
|
||||
} // namespace esphome
|
||||
} // namespace esphome::shtcx
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/sensirion_common/i2c_sensirion.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace shtcx {
|
||||
namespace esphome::shtcx {
|
||||
|
||||
enum SHTCXType { SHTCX_TYPE_SHTC3 = 0, SHTCX_TYPE_SHTC1, SHTCX_TYPE_UNKNOWN };
|
||||
enum SHTCXType : uint8_t {
|
||||
SHTCX_TYPE_SHTC3 = 0,
|
||||
SHTCX_TYPE_SHTC1,
|
||||
SHTCX_TYPE_UNKNOWN,
|
||||
};
|
||||
|
||||
/// This class implements support for the SHT3x-DIS family of temperature+humidity i2c sensors.
|
||||
class SHTCXComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice {
|
||||
@@ -23,11 +26,10 @@ class SHTCXComponent : public PollingComponent, public sensirion_common::Sensiri
|
||||
void wake_up();
|
||||
|
||||
protected:
|
||||
SHTCXType type_;
|
||||
uint16_t sensor_id_;
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *humidity_sensor_{nullptr};
|
||||
uint16_t sensor_id_;
|
||||
SHTCXType type_;
|
||||
};
|
||||
|
||||
} // namespace shtcx
|
||||
} // namespace esphome
|
||||
} // namespace esphome::shtcx
|
||||
|
||||
@@ -606,6 +606,16 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
|
||||
}
|
||||
|
||||
void ThermostatClimate::switch_to_supplemental_action_(climate::ClimateAction action) {
|
||||
// Always cancel max-runtime timers and clear exceeded flags when transitioning to idle/off,
|
||||
// even if supplemental_action_ is already idle (early-return path). This prevents a stale
|
||||
// heating_max_runtime_exceeded_ flag from triggering supplemental on the next heating cycle
|
||||
// when HEATING_MAX_RUN_TIME fires while the main action is already IDLE.
|
||||
if (action == climate::CLIMATE_ACTION_OFF || action == climate::CLIMATE_ACTION_IDLE) {
|
||||
this->cancel_timer_(thermostat::THERMOSTAT_TIMER_COOLING_MAX_RUN_TIME);
|
||||
this->cancel_timer_(thermostat::THERMOSTAT_TIMER_HEATING_MAX_RUN_TIME);
|
||||
this->cooling_max_runtime_exceeded_ = false;
|
||||
this->heating_max_runtime_exceeded_ = false;
|
||||
}
|
||||
// setup_complete_ helps us ensure an action is called immediately after boot
|
||||
if ((action == this->supplemental_action_) && this->setup_complete_) {
|
||||
// already in target mode
|
||||
@@ -975,8 +985,10 @@ void ThermostatClimate::cooling_on_timer_callback_() {
|
||||
void ThermostatClimate::fan_mode_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fan_mode timer expired");
|
||||
this->switch_to_fan_mode_(this->fan_mode.value_or(climate::CLIMATE_FAN_ON));
|
||||
if (this->supports_fan_only_action_uses_fan_mode_timer_)
|
||||
if (this->supports_fan_only_action_uses_fan_mode_timer_) {
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
}
|
||||
|
||||
void ThermostatClimate::fanning_off_timer_callback_() {
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#ifdef USE_ESP32
|
||||
#include "esphome/core/lock_free_queue.h"
|
||||
#endif
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
#include <span>
|
||||
@@ -727,6 +730,7 @@ class WiFiComponent final : public Component {
|
||||
|
||||
#ifdef USE_ESP32
|
||||
void wifi_process_event_(IDFWiFiEvent *data);
|
||||
friend void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
@@ -871,6 +875,13 @@ class WiFiComponent final : public Component {
|
||||
bool is_high_performance_mode_{false};
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
// Lock-free SPSC queue for WiFi events from ESP-IDF event handler.
|
||||
// 17 slots = 16 usable (ring buffer reserves one slot). WiFi events are rare.
|
||||
// Placed at end of class to avoid padding between smaller fields.
|
||||
LockFreeQueue<IDFWiFiEvent, 17> event_queue_;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Stores a pointer to a string literal (static storage duration).
|
||||
// ONLY set from Python-generated code with string literals - never dynamic strings.
|
||||
|
||||
@@ -47,7 +47,6 @@ namespace esphome::wifi {
|
||||
static const char *const TAG = "wifi_esp32";
|
||||
|
||||
static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static QueueHandle_t s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
#ifdef USE_WIFI_AP
|
||||
static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -132,11 +131,10 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi
|
||||
return;
|
||||
}
|
||||
|
||||
// copy to heap to keep queue object small
|
||||
// copy to heap — WiFi events are rare so heap alloc is fine
|
||||
auto *to_send = new IDFWiFiEvent; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
memcpy(to_send, &event, sizeof(IDFWiFiEvent));
|
||||
// don't block, we may miss events but the core can handle that
|
||||
if (xQueueSend(s_event_queue, &to_send, 0L) != pdPASS) {
|
||||
if (!global_wifi_component->event_queue_.push(to_send)) {
|
||||
delete to_send; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
@@ -157,12 +155,6 @@ void WiFiComponent::wifi_pre_setup_() {
|
||||
ESP_LOGE(TAG, "xEventGroupCreate failed");
|
||||
return;
|
||||
}
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
s_event_queue = xQueueCreate(64, sizeof(IDFWiFiEvent *));
|
||||
if (s_event_queue == nullptr) {
|
||||
ESP_LOGE(TAG, "xQueueCreate failed");
|
||||
return;
|
||||
}
|
||||
err = esp_event_loop_create_default();
|
||||
if (err != ERR_OK) {
|
||||
ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err));
|
||||
@@ -724,16 +716,14 @@ const char *get_disconnect_reason_str(uint8_t reason) {
|
||||
}
|
||||
|
||||
void WiFiComponent::wifi_loop_() {
|
||||
while (true) {
|
||||
IDFWiFiEvent *data;
|
||||
if (xQueueReceive(s_event_queue, &data, 0L) != pdTRUE) {
|
||||
// no event ready
|
||||
break;
|
||||
}
|
||||
uint16_t dropped = this->event_queue_.get_and_reset_dropped_count();
|
||||
if (dropped > 0) {
|
||||
ESP_LOGW(TAG, "Dropped %u WiFi events due to buffer overflow", dropped);
|
||||
}
|
||||
|
||||
// process event
|
||||
IDFWiFiEvent *data;
|
||||
while ((data = this->event_queue_.pop()) != nullptr) {
|
||||
wifi_process_event_(data);
|
||||
|
||||
delete data; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
|
||||
+21
-18
@@ -419,44 +419,48 @@ template<typename... Ts> class Action {
|
||||
template<typename... Ts> class ActionList {
|
||||
public:
|
||||
void add_action(Action<Ts...> *action) {
|
||||
if (this->actions_end_ == nullptr) {
|
||||
this->actions_begin_ = action;
|
||||
} else {
|
||||
this->actions_end_->next_ = action;
|
||||
}
|
||||
this->actions_end_ = action;
|
||||
// Walk to end of chain - action lists are short and only built during setup()
|
||||
Action<Ts...> **tail = &this->actions_;
|
||||
while (*tail != nullptr)
|
||||
tail = &(*tail)->next_;
|
||||
*tail = action;
|
||||
}
|
||||
void add_actions(const std::initializer_list<Action<Ts...> *> &actions) {
|
||||
// Find tail once, then append all actions in a single pass
|
||||
Action<Ts...> **tail = &this->actions_;
|
||||
while (*tail != nullptr)
|
||||
tail = &(*tail)->next_;
|
||||
for (auto *action : actions) {
|
||||
this->add_action(action);
|
||||
*tail = action;
|
||||
tail = &action->next_;
|
||||
}
|
||||
}
|
||||
// Force-inline: part of the Trigger→Automation→ActionList forwarding
|
||||
// chain collapsed to reduce automation call stack depth.
|
||||
inline void play(const Ts &...x) ESPHOME_ALWAYS_INLINE {
|
||||
if (this->actions_begin_ != nullptr)
|
||||
this->actions_begin_->play_complex(x...);
|
||||
if (this->actions_ != nullptr)
|
||||
this->actions_->play_complex(x...);
|
||||
}
|
||||
void play_tuple(const std::tuple<Ts...> &tuple) {
|
||||
this->play_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
|
||||
}
|
||||
void stop() {
|
||||
if (this->actions_begin_ != nullptr)
|
||||
this->actions_begin_->stop_complex();
|
||||
if (this->actions_ != nullptr)
|
||||
this->actions_->stop_complex();
|
||||
}
|
||||
bool empty() const { return this->actions_begin_ == nullptr; }
|
||||
bool empty() const { return this->actions_ == nullptr; }
|
||||
|
||||
/// Check if any action in this action list is currently running.
|
||||
bool is_running() {
|
||||
if (this->actions_begin_ == nullptr)
|
||||
if (this->actions_ == nullptr)
|
||||
return false;
|
||||
return this->actions_begin_->is_running();
|
||||
return this->actions_->is_running();
|
||||
}
|
||||
/// Return the number of actions in this action list that are currently running.
|
||||
int num_running() {
|
||||
if (this->actions_begin_ == nullptr)
|
||||
if (this->actions_ == nullptr)
|
||||
return 0;
|
||||
return this->actions_begin_->num_running_total();
|
||||
return this->actions_->num_running_total();
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -464,8 +468,7 @@ template<typename... Ts> class ActionList {
|
||||
this->play(std::get<S>(tuple)...);
|
||||
}
|
||||
|
||||
Action<Ts...> *actions_begin_{nullptr};
|
||||
Action<Ts...> *actions_end_{nullptr};
|
||||
Action<Ts...> *actions_{nullptr};
|
||||
};
|
||||
|
||||
template<typename... Ts> class Automation {
|
||||
|
||||
@@ -1830,6 +1830,38 @@ template<typename... Ts> class CallbackManager<void(Ts...)> {
|
||||
std::vector<Callback<void(Ts...)>> callbacks_;
|
||||
};
|
||||
|
||||
/** CallbackManager backed by StaticVector for compile-time-known callback counts.
|
||||
*
|
||||
* Drop-in replacement for CallbackManager that avoids std::vector template bloat
|
||||
* (_M_realloc_insert, etc.) when the maximum number of callbacks is known at compile time.
|
||||
*
|
||||
* @tparam N Maximum number of callbacks (compile-time constant, typically from cg.add_define())
|
||||
* @tparam Ts The arguments for the callbacks, wrapped in void().
|
||||
*/
|
||||
template<size_t N, typename... X> class StaticCallbackManager;
|
||||
|
||||
template<size_t N, typename... Ts> class StaticCallbackManager<N, void(Ts...)> {
|
||||
public:
|
||||
/// Add any callable. Small trivially-copyable callables (like [this] lambdas)
|
||||
/// are stored inline without heap allocation.
|
||||
template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
|
||||
|
||||
/// Call all callbacks in this manager.
|
||||
void call(Ts... args) {
|
||||
for (auto &cb : this->callbacks_)
|
||||
cb.call(args...);
|
||||
}
|
||||
size_t size() const { return this->callbacks_.size(); }
|
||||
|
||||
/// Call all callbacks in this manager.
|
||||
void operator()(Ts... args) { call(args...); }
|
||||
|
||||
protected:
|
||||
/// Non-template core to avoid code duplication per lambda type.
|
||||
void add_(Callback<void(Ts...)> cb) { this->callbacks_.push_back(cb); }
|
||||
StaticVector<Callback<void(Ts...)>, N> callbacks_;
|
||||
};
|
||||
|
||||
template<typename... X> class LazyCallbackManager;
|
||||
|
||||
/** Lazy-allocating callback manager that only allocates memory when callbacks are registered.
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ platformio==6.1.19
|
||||
esptool==5.2.0
|
||||
click==8.3.1
|
||||
esphome-dashboard==20260210.0
|
||||
aioesphomeapi==44.8.0
|
||||
aioesphomeapi==44.8.1
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.19.1 # dashboard_import
|
||||
|
||||
@@ -10,10 +10,9 @@ namespace esphome::api::benchmarks {
|
||||
// sub-microsecond benchmarks.
|
||||
static constexpr int kInnerIterations = 2000;
|
||||
|
||||
// Helper: encode a message into a buffer and return it.
|
||||
// Benchmarks encode once in setup, then decode the resulting bytes in a loop.
|
||||
// This keeps decode benchmarks in sync with the actual protobuf schema —
|
||||
// hand-encoded byte arrays would silently break when fields change.
|
||||
// Helper: encode a message into an APIBuffer for reuse in decode benchmarks.
|
||||
// Optimization barriers are applied to the decode target objects via
|
||||
// DoNotOptimize/ClobberMemory, not to this buffer.
|
||||
template<typename T> static APIBuffer encode_message(const T &msg) {
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
@@ -23,6 +22,12 @@ template<typename T> static APIBuffer encode_message(const T &msg) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// Force a pointer through an asm barrier so the compiler cannot
|
||||
/// prove its contents are unchanged across iterations.
|
||||
/// benchmark::DoNotOptimize/ClobberMemory are insufficient under
|
||||
/// CodSpeed's valgrind-based instrumentation.
|
||||
static void escape(void *p) { asm volatile("" : : "g"(p) : "memory"); }
|
||||
|
||||
// --- HelloRequest decode (string + varint fields) ---
|
||||
|
||||
static void Decode_HelloRequest(benchmark::State &state) {
|
||||
@@ -31,13 +36,18 @@ static void Decode_HelloRequest(benchmark::State &state) {
|
||||
source.api_version_major = 1;
|
||||
source.api_version_minor = 10;
|
||||
auto encoded = encode_message(source);
|
||||
auto *data = encoded.data();
|
||||
auto size = encoded.size();
|
||||
benchmark::DoNotOptimize(data);
|
||||
benchmark::DoNotOptimize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
HelloRequest msg;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
msg.decode(encoded.data(), encoded.size());
|
||||
HelloRequest msg;
|
||||
escape(&msg);
|
||||
msg.decode(data, size);
|
||||
escape(&msg);
|
||||
}
|
||||
benchmark::DoNotOptimize(msg.api_version_major);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
@@ -50,13 +60,18 @@ static void Decode_SwitchCommandRequest(benchmark::State &state) {
|
||||
source.key = 0x12345678;
|
||||
source.state = true;
|
||||
auto encoded = encode_message(source);
|
||||
auto *data = encoded.data();
|
||||
auto size = encoded.size();
|
||||
benchmark::DoNotOptimize(data);
|
||||
benchmark::DoNotOptimize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
SwitchCommandRequest msg;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
msg.decode(encoded.data(), encoded.size());
|
||||
SwitchCommandRequest msg;
|
||||
escape(&msg);
|
||||
msg.decode(data, size);
|
||||
escape(&msg);
|
||||
}
|
||||
benchmark::DoNotOptimize(msg.state);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
@@ -78,13 +93,18 @@ static void Decode_LightCommandRequest(benchmark::State &state) {
|
||||
source.has_effect = true;
|
||||
source.effect = StringRef::from_lit("rainbow");
|
||||
auto encoded = encode_message(source);
|
||||
auto *data = encoded.data();
|
||||
auto size = encoded.size();
|
||||
benchmark::DoNotOptimize(data);
|
||||
benchmark::DoNotOptimize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
LightCommandRequest msg;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
msg.decode(encoded.data(), encoded.size());
|
||||
LightCommandRequest msg;
|
||||
escape(&msg);
|
||||
msg.decode(data, size);
|
||||
escape(&msg);
|
||||
}
|
||||
benchmark::DoNotOptimize(msg.brightness);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user