mirror of
https://github.com/esphome/esphome.git
synced 2026-09-17 18:18:43 +00:00
Merge branch 'wifi-libretiny-state-member' into integration
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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1034,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);
|
||||
|
||||
|
||||
@@ -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_() {
|
||||
|
||||
+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 {
|
||||
|
||||
+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