mirror of
https://github.com/esphome/esphome.git
synced 2026-09-02 02:56:01 +00:00
Merge branch 'dev' into std-optional
This commit is contained in:
@@ -242,24 +242,11 @@ void APIConnection::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->flags_.sent_ping) {
|
||||
// Disconnect if not responded within 2.5*keepalive
|
||||
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
|
||||
on_fatal_error();
|
||||
this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("is unresponsive; disconnecting"));
|
||||
}
|
||||
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
|
||||
// Only send ping if we're not disconnecting
|
||||
ESP_LOGVV(TAG, "Sending keepalive PING");
|
||||
PingRequest req;
|
||||
this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
|
||||
if (!this->flags_.sent_ping) {
|
||||
// If we can't send the ping request directly (tx_buffer full),
|
||||
// schedule it at the front of the batch so it will be sent with priority
|
||||
ESP_LOGW(TAG, "Buffer full, ping queued");
|
||||
this->schedule_message_front_(nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
|
||||
this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
|
||||
}
|
||||
// Keepalive: only call into the cold path when enough time has elapsed.
|
||||
// When sent_ping is true, last_traffic_ hasn't been updated so this
|
||||
// condition is already satisfied — covers both send-ping and disconnect cases.
|
||||
if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
|
||||
this->check_keepalive_(now);
|
||||
}
|
||||
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
@@ -275,6 +262,29 @@ void APIConnection::loop() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void APIConnection::check_keepalive_(uint32_t now) {
|
||||
// Caller guarantees: now - last_traffic_ > KEEPALIVE_TIMEOUT_MS
|
||||
if (this->flags_.sent_ping) {
|
||||
// Disconnect if not responded within 2.5*keepalive
|
||||
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
|
||||
on_fatal_error();
|
||||
this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("is unresponsive; disconnecting"));
|
||||
}
|
||||
} else if (!this->flags_.remove) {
|
||||
// Only send ping if we're not disconnecting
|
||||
ESP_LOGVV(TAG, "Sending keepalive PING");
|
||||
PingRequest req;
|
||||
this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
|
||||
if (!this->flags_.sent_ping) {
|
||||
// If we can't send the ping request directly (tx_buffer full),
|
||||
// schedule it at the front of the batch so it will be sent with priority
|
||||
ESP_LOGW(TAG, "Buffer full, ping queued");
|
||||
this->schedule_message_front_(nullptr, PingRequest::MESSAGE_TYPE, PingRequest::ESTIMATED_SIZE);
|
||||
this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APIConnection::process_active_iterator_() {
|
||||
// Caller ensures active_iterator_ != NONE
|
||||
if (this->active_iterator_ == ActiveIterator::LIST_ENTITIES) {
|
||||
|
||||
@@ -370,6 +370,10 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
|
||||
}
|
||||
|
||||
// Send keepalive ping or disconnect unresponsive client.
|
||||
// Cold path — extracted from loop() to reduce instruction cache pressure.
|
||||
void __attribute__((noinline)) check_keepalive_(uint32_t now);
|
||||
|
||||
// Process active iterator (list_entities/initial_state) during connection setup.
|
||||
// Extracted from loop() — only runs during initial handshake, NONE in steady state.
|
||||
void __attribute__((noinline)) process_active_iterator_();
|
||||
|
||||
@@ -550,22 +550,8 @@ def binary_sensor_schema(
|
||||
return _BINARY_SENSOR_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
async def setup_binary_sensor_core_(var, config):
|
||||
await setup_entity(var, config, "binary_sensor")
|
||||
|
||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||
cg.add(var.set_device_class(device_class))
|
||||
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
|
||||
CONF_PUBLISH_INITIAL_STATE, False
|
||||
)
|
||||
cg.add(var.set_trigger_on_initial_state(trigger))
|
||||
if inverted := config.get(CONF_INVERTED):
|
||||
cg.add(var.set_inverted(inverted))
|
||||
if filters_config := config.get(CONF_FILTERS):
|
||||
cg.add_define("USE_BINARY_SENSOR_FILTER")
|
||||
filters = await cg.build_registry_list(FILTER_REGISTRY, filters_config)
|
||||
cg.add(var.add_filters(filters))
|
||||
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_binary_sensor_automations(var, config):
|
||||
for conf in config.get(CONF_ON_PRESS, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
@@ -617,6 +603,25 @@ async def setup_binary_sensor_core_(var, config):
|
||||
conf,
|
||||
)
|
||||
|
||||
|
||||
async def setup_binary_sensor_core_(var, config):
|
||||
await setup_entity(var, config, "binary_sensor")
|
||||
|
||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||
cg.add(var.set_device_class(device_class))
|
||||
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
|
||||
CONF_PUBLISH_INITIAL_STATE, False
|
||||
)
|
||||
cg.add(var.set_trigger_on_initial_state(trigger))
|
||||
if inverted := config.get(CONF_INVERTED):
|
||||
cg.add(var.set_inverted(inverted))
|
||||
if filters_config := config.get(CONF_FILTERS):
|
||||
cg.add_define("USE_BINARY_SENSOR_FILTER")
|
||||
filters = await cg.build_registry_list(FILTER_REGISTRY, filters_config)
|
||||
cg.add(var.add_filters(filters))
|
||||
|
||||
CORE.add_job(_build_binary_sensor_automations, var, config)
|
||||
|
||||
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
@@ -952,6 +952,7 @@ CONF_HEAP_IN_IRAM = "heap_in_iram"
|
||||
CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
|
||||
CONF_USE_FULL_CERTIFICATE_BUNDLE = "use_full_certificate_bundle"
|
||||
CONF_DISABLE_DEBUG_STUBS = "disable_debug_stubs"
|
||||
CONF_ENABLE_FULL_PRINTF = "enable_full_printf"
|
||||
CONF_DISABLE_OCD_AWARE = "disable_ocd_aware"
|
||||
CONF_DISABLE_USB_SERIAL_JTAG_SECONDARY = "disable_usb_serial_jtag_secondary"
|
||||
CONF_DISABLE_DEV_NULL_VFS = "disable_dev_null_vfs"
|
||||
@@ -1126,6 +1127,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
cv.Optional(
|
||||
CONF_INCLUDE_BUILTIN_IDF_COMPONENTS, default=[]
|
||||
): cv.ensure_list(cv.string_strict),
|
||||
cv.Optional(CONF_ENABLE_FULL_PRINTF, default=False): cv.boolean,
|
||||
cv.Optional(CONF_DISABLE_DEBUG_STUBS, default=True): cv.boolean,
|
||||
cv.Optional(CONF_DISABLE_OCD_AWARE, default=True): cv.boolean,
|
||||
cv.Optional(
|
||||
@@ -1469,6 +1471,14 @@ async def to_code(config):
|
||||
"_ZSt25__throw_bad_function_callv",
|
||||
]:
|
||||
cg.add_build_flag(f"-Wl,--wrap={mangled}")
|
||||
|
||||
# Wrap FILE*-based printf functions to eliminate newlib's _vfprintf_r
|
||||
# (~11 KB). See printf_stubs.cpp for implementation.
|
||||
if conf[CONF_ADVANCED][CONF_ENABLE_FULL_PRINTF]:
|
||||
cg.add_define("USE_FULL_PRINTF")
|
||||
else:
|
||||
for symbol in ("vprintf", "printf", "fprintf"):
|
||||
cg.add_build_flag(f"-Wl,--wrap={symbol}")
|
||||
else:
|
||||
cg.add_build_flag("-DUSE_ARDUINO")
|
||||
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Linker wrap stubs for FILE*-based printf functions.
|
||||
*
|
||||
* ESP-IDF SDK components (gpio driver, ringbuf, log_write) reference
|
||||
* fprintf(), printf(), and vprintf() which pull in newlib's _vfprintf_r
|
||||
* (~11 KB). This is a separate implementation from _svfprintf_r (used by
|
||||
* snprintf/vsnprintf) that handles FILE* stream I/O with buffering and
|
||||
* locking.
|
||||
*
|
||||
* ESPHome replaces the ESP-IDF log handler via esp_log_set_vprintf_(),
|
||||
* so the SDK's vprintf() path is dead code at runtime. The fprintf()
|
||||
* and printf() calls in SDK components are only in debug/assert paths
|
||||
* (gpio_dump_io_configuration, ringbuf diagnostics) that are either
|
||||
* GC'd or never called. Crash backtraces and panic output are
|
||||
* unaffected — they use esp_rom_printf() which is a ROM function
|
||||
* and does not go through libc.
|
||||
*
|
||||
* These stubs redirect through vsnprintf() (which uses _svfprintf_r
|
||||
* already in the binary) and fwrite(), allowing the linker to
|
||||
* dead-code eliminate _vfprintf_r.
|
||||
*
|
||||
* Saves ~11 KB of flash.
|
||||
*
|
||||
* To disable these wraps, set enable_full_printf: true in the esp32
|
||||
* advanced config section.
|
||||
*/
|
||||
|
||||
#if defined(USE_ESP_IDF) && !defined(USE_FULL_PRINTF)
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
#include "esp_system.h"
|
||||
|
||||
namespace esphome::esp32 {}
|
||||
|
||||
static constexpr size_t PRINTF_BUFFER_SIZE = 512;
|
||||
|
||||
// These stubs are essentially dead code at runtime — ESPHome replaces the
|
||||
// ESP-IDF log handler, and the SDK's printf/fprintf calls only exist in
|
||||
// debug/assert paths that are never reached in normal operation.
|
||||
// The buffer overflow check is purely defensive and should never trigger.
|
||||
static int write_printf_buffer(FILE *stream, char *buf, int len) {
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
size_t write_len = len;
|
||||
if (write_len >= PRINTF_BUFFER_SIZE) {
|
||||
fwrite(buf, 1, PRINTF_BUFFER_SIZE - 1, stream);
|
||||
esp_system_abort("printf buffer overflow; set enable_full_printf: true in esp32 framework advanced config");
|
||||
}
|
||||
if (fwrite(buf, 1, write_len, stream) < write_len || ferror(stream)) {
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
extern "C" {
|
||||
|
||||
int __wrap_vprintf(const char *fmt, va_list ap) {
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
return write_printf_buffer(stdout, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
}
|
||||
|
||||
int __wrap_printf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int len = __wrap_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __wrap_fprintf(FILE *stream, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
int len = write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
|
||||
#endif // USE_ESP_IDF && !USE_FULL_PRINTF
|
||||
@@ -16,7 +16,7 @@ GT911Touchscreen = gt911_ns.class_(
|
||||
CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GT911Touchscreen),
|
||||
cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_INTERRUPT_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x5D))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/gpio.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace gt911 {
|
||||
@@ -26,15 +27,17 @@ static const size_t MAX_BUTTONS = 4; // max number of buttons scanned
|
||||
|
||||
void GT911Touchscreen::setup() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
// temporarily set the interrupt pin to output to control address selection
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(false);
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
// temporarily set the interrupt pin to output to control address selection
|
||||
this->interrupt_pin_->setup();
|
||||
this->interrupt_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
this->interrupt_pin_->digital_write(false);
|
||||
}
|
||||
delay(2);
|
||||
this->reset_pin_->digital_write(true); // wait at least T3+T4 ms as per the datasheet
|
||||
this->reset_pin_->digital_write(true);
|
||||
// wait at least T3+T4 ms as per the datasheet
|
||||
this->set_timeout(5 + 50 + 1, [this] { this->setup_internal_(); });
|
||||
return;
|
||||
}
|
||||
@@ -43,11 +46,10 @@ void GT911Touchscreen::setup() {
|
||||
|
||||
void GT911Touchscreen::setup_internal_() {
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
// set pre-configured input mode
|
||||
this->interrupt_pin_->setup();
|
||||
if (this->interrupt_pin_->is_internal())
|
||||
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
}
|
||||
|
||||
// check the configuration of the int line.
|
||||
uint8_t data[4];
|
||||
i2c::ErrorCode err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
|
||||
if (err != i2c::ERROR_OK && this->address_ == PRIMARY_ADDRESS) {
|
||||
@@ -58,12 +60,25 @@ void GT911Touchscreen::setup_internal_() {
|
||||
err = this->read(data, 1);
|
||||
if (err == i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Switches ADDR: 0x%02X DATA: 0x%02X", this->address_, data[0]);
|
||||
|
||||
// data[0] & 1 == 1 => controller uses falling edge => active-low
|
||||
// data[0] & 1 == 0 => controller uses rising edge => active-high
|
||||
bool active_high = !(data[0] & 1);
|
||||
|
||||
if (this->interrupt_pin_ != nullptr) {
|
||||
this->attach_interrupt_(this->interrupt_pin_,
|
||||
(data[0] & 1) ? gpio::INTERRUPT_FALLING_EDGE : gpio::INTERRUPT_RISING_EDGE);
|
||||
if (this->interrupt_pin_->is_internal()) {
|
||||
// Direct MCU pin: attach a hardware interrupt, no polling needed.
|
||||
this->attach_interrupt_(static_cast<InternalGPIOPin *>(this->interrupt_pin_),
|
||||
active_high ? gpio::INTERRUPT_RISING_EDGE : gpio::INTERRUPT_FALLING_EDGE);
|
||||
ESP_LOGD(TAG, "Interrupt pin: hardware interrupt, active %s", active_high ? "HIGH" : "LOW");
|
||||
} else {
|
||||
// IO expander pin: leave as output for configuration only.
|
||||
ESP_LOGD(TAG, "Interrupt pin: IO expander polling mode, active %s", active_high ? "HIGH" : "LOW");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
|
||||
// no calibration? Attempt to read the max values from the touchscreen.
|
||||
if (err == i2c::ERROR_OK) {
|
||||
|
||||
@@ -30,7 +30,9 @@ class GT911Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
|
||||
void dump_config() override;
|
||||
bool can_proceed() override { return this->setup_done_; }
|
||||
|
||||
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
|
||||
/// Set a interrupt pin (supports hardware interrupts or expander connected).
|
||||
void set_interrupt_pin(GPIOPin *pin) { this->interrupt_pin_ = pin; }
|
||||
|
||||
void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; }
|
||||
void register_button_listener(GT911ButtonListener *listener) { this->button_listeners_.push_back(listener); }
|
||||
|
||||
@@ -49,7 +51,7 @@ class GT911Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
|
||||
/// @brief True if the touchscreen setup has completed successfully.
|
||||
bool setup_done_{false};
|
||||
|
||||
InternalGPIOPin *interrupt_pin_{nullptr};
|
||||
GPIOPin *interrupt_pin_{nullptr};
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
std::vector<GT911ButtonListener *> button_listeners_;
|
||||
uint8_t button_state_{0xFF}; // last button state. Initial FF guarantees first update.
|
||||
|
||||
@@ -173,8 +173,6 @@ static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0xF8, 0xF7, 0x
|
||||
// MAC address the module uses when Bluetooth is disabled
|
||||
static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01};
|
||||
|
||||
static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
|
||||
|
||||
static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
|
||||
return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0;
|
||||
}
|
||||
@@ -361,17 +359,14 @@ void LD2410Component::handle_periodic_data_() {
|
||||
Detect distance: 16~17th bytes
|
||||
*/
|
||||
#ifdef USE_SENSOR
|
||||
SAFE_PUBLISH_SENSOR(
|
||||
this->moving_target_distance_sensor_,
|
||||
ld2410::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]))
|
||||
SAFE_PUBLISH_SENSOR(this->moving_target_distance_sensor_,
|
||||
encode_uint16(this->buffer_data_[MOVING_TARGET_HIGH], this->buffer_data_[MOVING_TARGET_LOW]))
|
||||
SAFE_PUBLISH_SENSOR(this->moving_target_energy_sensor_, this->buffer_data_[MOVING_ENERGY])
|
||||
SAFE_PUBLISH_SENSOR(
|
||||
this->still_target_distance_sensor_,
|
||||
ld2410::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]));
|
||||
SAFE_PUBLISH_SENSOR(this->still_target_distance_sensor_,
|
||||
encode_uint16(this->buffer_data_[STILL_TARGET_HIGH], this->buffer_data_[STILL_TARGET_LOW]));
|
||||
SAFE_PUBLISH_SENSOR(this->still_target_energy_sensor_, this->buffer_data_[STILL_ENERGY]);
|
||||
SAFE_PUBLISH_SENSOR(
|
||||
this->detection_distance_sensor_,
|
||||
ld2410::two_byte_to_int(this->buffer_data_[DETECT_DISTANCE_LOW], this->buffer_data_[DETECT_DISTANCE_HIGH]));
|
||||
SAFE_PUBLISH_SENSOR(this->detection_distance_sensor_,
|
||||
encode_uint16(this->buffer_data_[DETECT_DISTANCE_HIGH], this->buffer_data_[DETECT_DISTANCE_LOW]));
|
||||
|
||||
if (engineering_mode) {
|
||||
/*
|
||||
@@ -578,8 +573,8 @@ bool LD2410Component::handle_ack_data_() {
|
||||
/*
|
||||
None Duration: 33~34th bytes
|
||||
*/
|
||||
updates.push_back(set_number_value(this->timeout_number_,
|
||||
ld2410::two_byte_to_int(this->buffer_data_[32], this->buffer_data_[33])));
|
||||
updates.push_back(
|
||||
set_number_value(this->timeout_number_, encode_uint16(this->buffer_data_[33], this->buffer_data_[32])));
|
||||
for (auto &update : updates) {
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -192,8 +192,6 @@ static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0xF8, 0xF7, 0x
|
||||
// MAC address the module uses when Bluetooth is disabled
|
||||
static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01};
|
||||
|
||||
static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
|
||||
|
||||
static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
|
||||
return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0;
|
||||
}
|
||||
@@ -398,22 +396,19 @@ void LD2412Component::handle_periodic_data_() {
|
||||
Detect distance: 16~17th bytes
|
||||
*/
|
||||
#ifdef USE_SENSOR
|
||||
SAFE_PUBLISH_SENSOR(
|
||||
this->moving_target_distance_sensor_,
|
||||
ld2412::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]))
|
||||
SAFE_PUBLISH_SENSOR(this->moving_target_distance_sensor_,
|
||||
encode_uint16(this->buffer_data_[MOVING_TARGET_HIGH], this->buffer_data_[MOVING_TARGET_LOW]))
|
||||
SAFE_PUBLISH_SENSOR(this->moving_target_energy_sensor_, this->buffer_data_[MOVING_ENERGY])
|
||||
SAFE_PUBLISH_SENSOR(
|
||||
this->still_target_distance_sensor_,
|
||||
ld2412::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]))
|
||||
SAFE_PUBLISH_SENSOR(this->still_target_distance_sensor_,
|
||||
encode_uint16(this->buffer_data_[STILL_TARGET_HIGH], this->buffer_data_[STILL_TARGET_LOW]))
|
||||
SAFE_PUBLISH_SENSOR(this->still_target_energy_sensor_, this->buffer_data_[STILL_ENERGY])
|
||||
if (this->detection_distance_sensor_ != nullptr) {
|
||||
int new_detect_distance = 0;
|
||||
if (target_state != 0x00 && (target_state & MOVE_BITMASK)) {
|
||||
new_detect_distance =
|
||||
ld2412::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]);
|
||||
encode_uint16(this->buffer_data_[MOVING_TARGET_HIGH], this->buffer_data_[MOVING_TARGET_LOW]);
|
||||
} else if (target_state != 0x00) {
|
||||
new_detect_distance =
|
||||
ld2412::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]);
|
||||
new_detect_distance = encode_uint16(this->buffer_data_[STILL_TARGET_HIGH], this->buffer_data_[STILL_TARGET_LOW]);
|
||||
}
|
||||
this->detection_distance_sensor_->publish_state_if_not_dup(new_detect_distance);
|
||||
}
|
||||
@@ -637,9 +632,9 @@ bool LD2412Component::handle_ack_data_() {
|
||||
/*
|
||||
None Duration: 11~12th bytes
|
||||
*/
|
||||
updates.push_back(set_number_value(this->timeout_number_,
|
||||
ld2412::two_byte_to_int(this->buffer_data_[12], this->buffer_data_[13])));
|
||||
ESP_LOGV(TAG, "timeout_number_: %u", ld2412::two_byte_to_int(this->buffer_data_[12], this->buffer_data_[13]));
|
||||
updates.push_back(
|
||||
set_number_value(this->timeout_number_, encode_uint16(this->buffer_data_[13], this->buffer_data_[12])));
|
||||
ESP_LOGV(TAG, "timeout_number_: %u", encode_uint16(this->buffer_data_[13], this->buffer_data_[12]));
|
||||
/*
|
||||
Output pin configuration: 13th bytes
|
||||
*/
|
||||
|
||||
@@ -103,8 +103,6 @@ DriverChip(
|
||||
(0xE9, 0xC8, 0x10, 0x0A, 0x00, 0x00, 0x80, 0x81, 0x12, 0x31, 0x23, 0x4F, 0x86, 0xA0, 0x00, 0x47, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x98, 0x02, 0x8B, 0xAF, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88, 0x88, 0x98, 0x13, 0x8B, 0xAF, 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xEA, 0x97, 0x0C, 0x09, 0x09, 0x09, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x31, 0x8B, 0xA8, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9F, 0x20, 0x8B, 0xA8, 0x20, 0x64, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x81, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xEF, 0xFF, 0xFF, 0x01),
|
||||
(0x11, 0x00),
|
||||
(0x29, 0x00),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -125,6 +123,7 @@ DriverChip(
|
||||
lane_bit_rate="900Mbps",
|
||||
no_transform=True,
|
||||
color_order="RGB",
|
||||
reset_pin=33,
|
||||
initsequence=[
|
||||
(0x80, 0x8B),
|
||||
(0x81, 0x78),
|
||||
|
||||
@@ -240,6 +240,23 @@ def number_schema(
|
||||
return _NUMBER_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_number_automations(var, config):
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await cg.register_component(trigger, conf)
|
||||
if CONF_ABOVE in conf:
|
||||
template_ = await cg.templatable(conf[CONF_ABOVE], [(float, "x")], float)
|
||||
cg.add(trigger.set_min(template_))
|
||||
if CONF_BELOW in conf:
|
||||
template_ = await cg.templatable(conf[CONF_BELOW], [(float, "x")], float)
|
||||
cg.add(trigger.set_max(template_))
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
|
||||
|
||||
async def setup_number_core_(
|
||||
var, config, *, min_value: float, max_value: float, step: float
|
||||
):
|
||||
@@ -254,19 +271,7 @@ async def setup_number_core_(
|
||||
if config[CONF_MODE] != NumberMode.NUMBER_MODE_AUTO:
|
||||
cg.add(var.traits.set_mode(config[CONF_MODE]))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await cg.register_component(trigger, conf)
|
||||
if CONF_ABOVE in conf:
|
||||
template_ = await cg.templatable(conf[CONF_ABOVE], [(float, "x")], float)
|
||||
cg.add(trigger.set_min(template_))
|
||||
if CONF_BELOW in conf:
|
||||
template_ = await cg.templatable(conf[CONF_BELOW], [(float, "x")], float)
|
||||
cg.add(trigger.set_max(template_))
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
CORE.add_job(_build_number_automations, var, config)
|
||||
|
||||
if (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)) is not None:
|
||||
cg.add(var.traits.set_unit_of_measurement(unit_of_measurement))
|
||||
|
||||
@@ -888,6 +888,26 @@ async def build_filters(config):
|
||||
return await cg.build_registry_list(FILTER_REGISTRY, config)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_sensor_automations(var, config):
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
for conf in config.get(CONF_ON_RAW_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await cg.register_component(trigger, conf)
|
||||
if (above := conf.get(CONF_ABOVE)) is not None:
|
||||
template_ = await cg.templatable(above, [(float, "x")], float)
|
||||
cg.add(trigger.set_min(template_))
|
||||
if (below := conf.get(CONF_BELOW)) is not None:
|
||||
template_ = await cg.templatable(below, [(float, "x")], float)
|
||||
cg.add(trigger.set_max(template_))
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
|
||||
|
||||
async def setup_sensor_core_(var, config):
|
||||
await setup_entity(var, config, "sensor")
|
||||
|
||||
@@ -907,22 +927,7 @@ async def setup_sensor_core_(var, config):
|
||||
filters = await build_filters(config[CONF_FILTERS])
|
||||
cg.add(var.set_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
for conf in config.get(CONF_ON_RAW_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
for conf in config.get(CONF_ON_VALUE_RANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await cg.register_component(trigger, conf)
|
||||
if (above := conf.get(CONF_ABOVE)) is not None:
|
||||
template_ = await cg.templatable(above, [(float, "x")], float)
|
||||
cg.add(trigger.set_min(template_))
|
||||
if (below := conf.get(CONF_BELOW)) is not None:
|
||||
template_ = await cg.templatable(below, [(float, "x")], float)
|
||||
cg.add(trigger.set_max(template_))
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
CORE.add_job(_build_sensor_automations, var, config)
|
||||
|
||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||
|
||||
@@ -141,11 +141,8 @@ def switch_schema(
|
||||
return _SWITCH_SCHEMA.extend(schema)
|
||||
|
||||
|
||||
async def setup_switch_core_(var, config):
|
||||
await setup_entity(var, config, "switch")
|
||||
|
||||
if (inverted := config.get(CONF_INVERTED)) is not None:
|
||||
cg.add(var.set_inverted(inverted))
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_switch_automations(var, config):
|
||||
for conf in config.get(CONF_ON_STATE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(bool, "x")], conf)
|
||||
@@ -156,6 +153,15 @@ async def setup_switch_core_(var, config):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
|
||||
async def setup_switch_core_(var, config):
|
||||
await setup_entity(var, config, "switch")
|
||||
|
||||
if (inverted := config.get(CONF_INVERTED)) is not None:
|
||||
cg.add(var.set_inverted(inverted))
|
||||
|
||||
CORE.add_job(_build_switch_automations, var, config)
|
||||
|
||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
@@ -197,6 +197,17 @@ async def build_filters(config):
|
||||
return await cg.build_registry_list(FILTER_REGISTRY, config)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.AUTOMATION)
|
||||
async def _build_text_sensor_automations(var, config):
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_RAW_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
|
||||
async def setup_text_sensor_core_(var, config):
|
||||
await setup_entity(var, config, "text_sensor")
|
||||
|
||||
@@ -208,13 +219,7 @@ async def setup_text_sensor_core_(var, config):
|
||||
filters = await build_filters(config[CONF_FILTERS])
|
||||
cg.add(var.set_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_RAW_VALUE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
CORE.add_job(_build_text_sensor_automations, var, config)
|
||||
|
||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||
|
||||
@@ -12,6 +12,7 @@ from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BYTES,
|
||||
CONF_DATA,
|
||||
CONF_DATA_BITS,
|
||||
CONF_DEBUG,
|
||||
CONF_DELIMITER,
|
||||
CONF_DIRECTION,
|
||||
@@ -21,10 +22,12 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_NUMBER,
|
||||
CONF_PARITY,
|
||||
CONF_PORT,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_RX_PIN,
|
||||
CONF_SEQUENCE,
|
||||
CONF_STOP_BITS,
|
||||
CONF_TIMEOUT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TX_PIN,
|
||||
@@ -215,9 +218,6 @@ UART_PARITY_OPTIONS = {
|
||||
"ODD": UARTParityOptions.UART_CONFIG_PARITY_ODD,
|
||||
}
|
||||
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_RX_FULL_THRESHOLD = "rx_full_threshold"
|
||||
CONF_RX_TIMEOUT = "rx_timeout"
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "esphome/core/gpio.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "soc/gpio_num.h"
|
||||
#include "soc/uart_pins.h"
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
@@ -19,13 +18,6 @@ namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.idf";
|
||||
|
||||
/// Check if a pin number matches one of the default UART0 GPIO pins.
|
||||
/// These pins may have residual state from the boot console that requires
|
||||
/// explicit reset before UART reconfiguration (ESP-IDF issue #17459).
|
||||
static constexpr bool is_default_uart0_pin(int8_t pin_num) {
|
||||
return pin_num == U0TXD_GPIO_NUM || pin_num == U0RXD_GPIO_NUM;
|
||||
}
|
||||
|
||||
uart_config_t IDFUARTComponent::get_config_() {
|
||||
uart_parity_t parity = UART_PARITY_DISABLE;
|
||||
if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
|
||||
@@ -149,34 +141,12 @@ void IDFUARTComponent::load_settings(bool dump_config) {
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
|
||||
int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
|
||||
int8_t flow_control = this->flow_control_pin_ != nullptr ? this->flow_control_pin_->get_pin() : -1;
|
||||
|
||||
// Workaround for ESP-IDF issue: https://github.com/espressif/esp-idf/issues/17459
|
||||
// Commit 9ed617fb17 removed gpio_func_sel() calls from uart_set_pin(), which breaks
|
||||
// UART on default UART0 pins that may have residual state from boot console.
|
||||
// Reset these pins before configuring UART to ensure they're in a clean state.
|
||||
if (is_default_uart0_pin(tx)) {
|
||||
gpio_reset_pin(static_cast<gpio_num_t>(tx));
|
||||
}
|
||||
if (is_default_uart0_pin(rx)) {
|
||||
gpio_reset_pin(static_cast<gpio_num_t>(rx));
|
||||
}
|
||||
|
||||
// Setup pins after reset to configure GPIO direction and pull resistors.
|
||||
// For UART0 default pins, setup() must always be called because gpio_reset_pin()
|
||||
// above sets GPIO_MODE_DISABLE which disables the input buffer. Without setup(),
|
||||
// uart_set_pin() on ESP-IDF 5.4.2+ does not re-enable the input buffer for
|
||||
// IOMUX-connected pins, so the RX pin cannot receive data (see issue #10132).
|
||||
// For other pins, only call setup() if pull or open-drain flags are set to avoid
|
||||
// disturbing the default pin state which breaks some external components (#11823).
|
||||
auto setup_pin_if_needed = [](InternalGPIOPin *pin) {
|
||||
if (!pin) {
|
||||
return;
|
||||
}
|
||||
const auto mask = gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN;
|
||||
if (is_default_uart0_pin(pin->get_pin()) || (pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
|
||||
if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
|
||||
pin->setup();
|
||||
}
|
||||
};
|
||||
@@ -186,6 +156,10 @@ void IDFUARTComponent::load_settings(bool dump_config) {
|
||||
setup_pin_if_needed(this->tx_pin_);
|
||||
}
|
||||
|
||||
int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
|
||||
int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
|
||||
int8_t flow_control = this->flow_control_pin_ != nullptr ? this->flow_control_pin_->get_pin() : -1;
|
||||
|
||||
uint32_t invert = 0;
|
||||
if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted()) {
|
||||
invert |= UART_SIGNAL_TXD_INV;
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import socket
|
||||
from esphome.components.uart import (
|
||||
CONF_DATA_BITS,
|
||||
CONF_PARITY,
|
||||
CONF_STOP_BITS,
|
||||
UARTComponent,
|
||||
)
|
||||
from esphome.components.uart import UARTComponent
|
||||
from esphome.components.usb_host import register_usb_client, usb_device_schema
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BUFFER_SIZE,
|
||||
CONF_CHANNELS,
|
||||
CONF_DATA_BITS,
|
||||
CONF_DEBUG,
|
||||
CONF_DUMMY_RECEIVER,
|
||||
CONF_ID,
|
||||
CONF_PARITY,
|
||||
CONF_STOP_BITS,
|
||||
)
|
||||
from esphome.cpp_types import Component
|
||||
|
||||
|
||||
@@ -4,21 +4,21 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_CHANNEL,
|
||||
CONF_DATA_BITS,
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OUTPUT,
|
||||
CONF_PARITY,
|
||||
CONF_STOP_BITS,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@DrCoolZic"]
|
||||
AUTO_LOAD = ["uart"]
|
||||
|
||||
MULTI_CONF = True
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_CRYSTAL = "crystal"
|
||||
CONF_UART = "uart"
|
||||
CONF_TEST_MODE = "test_mode"
|
||||
|
||||
@@ -280,6 +280,7 @@ CONF_CUSTOM_PRESETS = "custom_presets"
|
||||
CONF_CYCLE = "cycle"
|
||||
CONF_DALLAS_ID = "dallas_id"
|
||||
CONF_DATA = "data"
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
CONF_DATA_PIN = "data_pin"
|
||||
CONF_DATA_PINS = "data_pins"
|
||||
CONF_DATA_RATE = "data_rate"
|
||||
@@ -759,6 +760,7 @@ CONF_PAGE_ID = "page_id"
|
||||
CONF_PAGES = "pages"
|
||||
CONF_PANASONIC = "panasonic"
|
||||
CONF_PARAMETERS = "parameters"
|
||||
CONF_PARITY = "parity"
|
||||
CONF_PASSWORD = "password"
|
||||
CONF_PATH = "path"
|
||||
CONF_PATTERN = "pattern"
|
||||
@@ -961,6 +963,7 @@ CONF_STEP_PIN = "step_pin"
|
||||
CONF_STILL_THRESHOLD = "still_threshold"
|
||||
CONF_STOP = "stop"
|
||||
CONF_STOP_ACTION = "stop_action"
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_STORE_BASELINE = "store_baseline"
|
||||
CONF_SUBNET = "subnet"
|
||||
CONF_SUBSCRIBE_QOS = "subscribe_qos"
|
||||
|
||||
@@ -79,24 +79,7 @@ static void insertion_sort_by_priority(Iterator first, Iterator last) {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::register_component_(Component *comp) {
|
||||
if (comp == nullptr) {
|
||||
ESP_LOGW(TAG, "Tried to register null component!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto *c : this->components_) {
|
||||
if (comp == c) {
|
||||
ESP_LOGW(TAG, "Component %s already registered! (%p)", LOG_STR_ARG(c->get_component_log_str()), c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this->components_.size() >= ESPHOME_COMPONENT_COUNT) {
|
||||
ESP_LOGE(TAG, "Cannot register component %s - at capacity!", LOG_STR_ARG(comp->get_component_log_str()));
|
||||
return;
|
||||
}
|
||||
this->components_.push_back(comp);
|
||||
}
|
||||
void Application::register_component_(Component *comp) { this->components_.push_back(comp); }
|
||||
void Application::setup() {
|
||||
ESP_LOGI(TAG, "Running through setup()");
|
||||
ESP_LOGV(TAG, "Sorting components by setup priority");
|
||||
@@ -525,8 +508,7 @@ void Application::enable_pending_loops_() {
|
||||
// Clear the pending flag and enable the loop
|
||||
component->pending_enable_loop_ = false;
|
||||
ESP_LOGVV(TAG, "%s loop enabled from ISR", LOG_STR_ARG(component->get_component_log_str()));
|
||||
component->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
component->component_state_ |= COMPONENT_STATE_LOOP;
|
||||
component->set_component_state_(COMPONENT_STATE_LOOP);
|
||||
|
||||
// Move to active section
|
||||
this->activate_looping_component_(i);
|
||||
|
||||
@@ -108,6 +108,10 @@ namespace esphome::socket {
|
||||
class Socket;
|
||||
} // namespace esphome::socket
|
||||
|
||||
// Forward declarations for friend access from codegen-generated setup()
|
||||
void setup(); // NOLINT(readability-redundant-declaration) - may be declared in Arduino.h
|
||||
void original_setup(); // NOLINT(readability-redundant-declaration) - used by cpp unit tests
|
||||
|
||||
namespace esphome {
|
||||
|
||||
// Teardown timeout constant (in milliseconds)
|
||||
@@ -247,13 +251,6 @@ class Application {
|
||||
|
||||
/// Reserve space for components to avoid memory fragmentation
|
||||
|
||||
/// Register the component in this Application instance.
|
||||
template<class C> C *register_component(C *c) {
|
||||
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
||||
this->register_component_((Component *) c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/// Set up all the registered components. Call this at the end of your setup() function.
|
||||
void setup();
|
||||
|
||||
@@ -508,6 +505,8 @@ class Application {
|
||||
protected:
|
||||
friend Component;
|
||||
friend class socket::Socket;
|
||||
friend void ::setup();
|
||||
friend void ::original_setup();
|
||||
|
||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||
/// Fast path for Socket::ready() via friendship - skips negative fd check.
|
||||
|
||||
@@ -297,10 +297,6 @@ void Component::mark_failed() {
|
||||
// Also remove from loop since failed components shouldn't loop
|
||||
App.disable_component_loop_(this);
|
||||
}
|
||||
void Component::set_component_state_(uint8_t state) {
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= state;
|
||||
}
|
||||
void Component::disable_loop() {
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) {
|
||||
ESP_LOGVV(TAG, "%s loop disabled", LOG_STR_ARG(this->get_component_log_str()));
|
||||
|
||||
@@ -294,7 +294,10 @@ class Component {
|
||||
void call_dump_config_();
|
||||
|
||||
/// Helper to set component state (clears state bits and sets new state)
|
||||
void set_component_state_(uint8_t state);
|
||||
inline void set_component_state_(uint8_t state) {
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= state;
|
||||
}
|
||||
|
||||
/** Set an interval function with a unique name. Empty name means no cancelling possible.
|
||||
*
|
||||
|
||||
@@ -79,7 +79,7 @@ async def register_component(var, config):
|
||||
if name is not None:
|
||||
add(var.set_component_source(LogStringLiteral(name)))
|
||||
|
||||
add(App.register_component(var))
|
||||
add(App.register_component_(var))
|
||||
return var
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main):
|
||||
main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml")
|
||||
|
||||
assert "deepsleep = new deep_sleep::DeepSleepComponent();" in main_cpp
|
||||
assert "App.register_component(deepsleep);" in main_cpp
|
||||
assert "App.register_component_(deepsleep);" in main_cpp
|
||||
|
||||
|
||||
def test_deep_sleep_sleep_duration(generate_main):
|
||||
|
||||
@@ -27,7 +27,7 @@ def test_web_server_ota_generated(generate_main: Callable[[str], str]) -> None:
|
||||
assert "global_web_server_base" in main_cpp
|
||||
|
||||
# Check component is registered
|
||||
assert "App.register_component(web_server_webserverotacomponent_id)" in main_cpp
|
||||
assert "App.register_component_(web_server_webserverotacomponent_id)" in main_cpp
|
||||
|
||||
|
||||
def test_web_server_ota_with_callbacks(generate_main: Callable[[str], str]) -> None:
|
||||
|
||||
@@ -10,6 +10,7 @@ esp32:
|
||||
use_full_certificate_bundle: false # Test CMN bundle (default)
|
||||
include_builtin_idf_components:
|
||||
- freertos # Test escape hatch (freertos is always included anyway)
|
||||
enable_full_printf: false
|
||||
disable_debug_stubs: true
|
||||
disable_ocd_aware: true
|
||||
disable_usb_serial_jtag_secondary: true
|
||||
|
||||
@@ -16,10 +16,10 @@ void setup() {
|
||||
auto *log = new logger::Logger(115200); // NOLINT
|
||||
log->pre_setup();
|
||||
log->set_uart_selection(logger::UART_SELECTION_UART0);
|
||||
App.register_component(log);
|
||||
App.register_component_(log);
|
||||
|
||||
auto *wifi = new wifi::WiFiComponent(); // NOLINT
|
||||
App.register_component(wifi);
|
||||
App.register_component_(wifi);
|
||||
wifi::WiFiAP ap;
|
||||
ap.set_ssid("Test SSID");
|
||||
ap.set_password("password1");
|
||||
|
||||
@@ -16,7 +16,7 @@ async def test_gpio_pin_expression__conf_is_none(monkeypatch):
|
||||
async def test_register_component(monkeypatch):
|
||||
var = Mock(base="foo.bar")
|
||||
|
||||
app_mock = Mock(register_component=Mock(return_value=var))
|
||||
app_mock = Mock(register_component_=Mock(return_value=var))
|
||||
monkeypatch.setattr(ch, "App", app_mock)
|
||||
|
||||
core_mock = Mock(component_ids=["foo.bar"])
|
||||
@@ -29,7 +29,7 @@ async def test_register_component(monkeypatch):
|
||||
|
||||
assert actual is var
|
||||
assert add_mock.call_count == 2
|
||||
app_mock.register_component.assert_called_with(var)
|
||||
app_mock.register_component_.assert_called_with(var)
|
||||
assert core_mock.component_ids == []
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ async def test_register_component__no_component_id(monkeypatch):
|
||||
async def test_register_component__with_setup_priority(monkeypatch):
|
||||
var = Mock(base="foo.bar")
|
||||
|
||||
app_mock = Mock(register_component=Mock(return_value=var))
|
||||
app_mock = Mock(register_component_=Mock(return_value=var))
|
||||
monkeypatch.setattr(ch, "App", app_mock)
|
||||
|
||||
core_mock = Mock(component_ids=["foo.bar"])
|
||||
@@ -68,5 +68,5 @@ async def test_register_component__with_setup_priority(monkeypatch):
|
||||
assert actual is var
|
||||
add_mock.assert_called()
|
||||
assert add_mock.call_count == 4
|
||||
app_mock.register_component.assert_called_with(var)
|
||||
app_mock.register_component_.assert_called_with(var)
|
||||
assert core_mock.component_ids == []
|
||||
|
||||
Reference in New Issue
Block a user