mirror of
https://github.com/esphome/esphome.git
synced 2025-06-16 07:16:58 +02:00
Merge branch 'dev' into missing_force
This commit is contained in:
commit
a65542cd47
@ -22,6 +22,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
||||
TemplateArguments,
|
||||
add,
|
||||
add_build_flag,
|
||||
add_build_unflag,
|
||||
add_define,
|
||||
add_global,
|
||||
add_library,
|
||||
@ -34,6 +35,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
||||
process_lambda,
|
||||
progmem_array,
|
||||
safe_exp,
|
||||
set_cpp_standard,
|
||||
statement,
|
||||
static_const_array,
|
||||
templatable,
|
||||
|
@ -248,25 +248,41 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
||||
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
|
||||
uint32_t remaining_size, bool is_single) {
|
||||
// Calculate size
|
||||
uint32_t size = 0;
|
||||
msg.calculate_size(size);
|
||||
uint32_t calculated_size = 0;
|
||||
msg.calculate_size(calculated_size);
|
||||
|
||||
// Cache frame sizes to avoid repeated virtual calls
|
||||
const uint8_t header_padding = conn->helper_->frame_header_padding();
|
||||
const uint8_t footer_size = conn->helper_->frame_footer_size();
|
||||
|
||||
// Calculate total size with padding for buffer allocation
|
||||
uint16_t total_size =
|
||||
static_cast<uint16_t>(size) + conn->helper_->frame_header_padding() + conn->helper_->frame_footer_size();
|
||||
size_t total_calculated_size = calculated_size + header_padding + footer_size;
|
||||
|
||||
// Check if it fits
|
||||
if (total_size > remaining_size) {
|
||||
if (total_calculated_size > remaining_size) {
|
||||
return 0; // Doesn't fit
|
||||
}
|
||||
|
||||
// Allocate buffer space - pass payload size, allocation functions add header/footer space
|
||||
ProtoWriteBuffer buffer =
|
||||
is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size);
|
||||
ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
|
||||
: conn->allocate_batch_message_buffer(calculated_size);
|
||||
|
||||
// Get buffer size after allocation (which includes header padding)
|
||||
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
|
||||
size_t size_before_encode = shared_buf.size();
|
||||
|
||||
// Encode directly into buffer
|
||||
msg.encode(buffer);
|
||||
return total_size;
|
||||
|
||||
// Calculate actual encoded size (not including header that was already added)
|
||||
size_t actual_payload_size = shared_buf.size() - size_before_encode;
|
||||
|
||||
// Return actual total size (header + actual payload + footer)
|
||||
size_t actual_total_size = header_padding + actual_payload_size + footer_size;
|
||||
|
||||
// Verify that calculate_size() returned the correct value
|
||||
assert(calculated_size == actual_payload_size);
|
||||
return static_cast<uint16_t>(actual_total_size);
|
||||
}
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
|
@ -240,8 +240,8 @@ class APIConnection : public APIServerConnection {
|
||||
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
||||
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
||||
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
|
||||
// Insert header padding bytes so message encoding starts at the correct position
|
||||
shared_buf.insert(shared_buf.begin(), header_padding, 0);
|
||||
// Resize to add header padding so message encoding starts at the correct position
|
||||
shared_buf.resize(header_padding);
|
||||
return {&shared_buf};
|
||||
}
|
||||
|
||||
@ -249,32 +249,26 @@ class APIConnection : public APIServerConnection {
|
||||
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
|
||||
// Get reference to shared buffer (it maintains state between batch messages)
|
||||
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||
size_t current_size = shared_buf.size();
|
||||
|
||||
if (is_first_message) {
|
||||
// For first message, initialize buffer with header padding
|
||||
uint8_t header_padding = this->helper_->frame_header_padding();
|
||||
shared_buf.clear();
|
||||
shared_buf.reserve(message_size + header_padding);
|
||||
shared_buf.resize(header_padding);
|
||||
// Fill header padding with zeros
|
||||
std::fill(shared_buf.begin(), shared_buf.end(), 0);
|
||||
} else {
|
||||
// For subsequent messages, add footer space for previous message and header for this message
|
||||
uint8_t footer_size = this->helper_->frame_footer_size();
|
||||
uint8_t header_padding = this->helper_->frame_header_padding();
|
||||
|
||||
// Reserve additional space for everything
|
||||
shared_buf.reserve(current_size + footer_size + header_padding + message_size);
|
||||
|
||||
// Single resize to add both footer and header padding
|
||||
size_t new_size = current_size + footer_size + header_padding;
|
||||
shared_buf.resize(new_size);
|
||||
|
||||
// Fill the newly added bytes with zeros (footer + header padding)
|
||||
std::fill(shared_buf.begin() + current_size, shared_buf.end(), 0);
|
||||
}
|
||||
|
||||
size_t current_size = shared_buf.size();
|
||||
|
||||
// Calculate padding to add:
|
||||
// - First message: just header padding
|
||||
// - Subsequent messages: footer for previous message + header padding for this message
|
||||
size_t padding_to_add = is_first_message
|
||||
? this->helper_->frame_header_padding()
|
||||
: this->helper_->frame_header_padding() + this->helper_->frame_footer_size();
|
||||
|
||||
// Reserve space for padding + message
|
||||
shared_buf.reserve(current_size + padding_to_add + message_size);
|
||||
|
||||
// Resize to add the padding bytes
|
||||
shared_buf.resize(current_size + padding_to_add);
|
||||
|
||||
return {&shared_buf};
|
||||
}
|
||||
|
||||
|
@ -93,9 +93,8 @@ void BME280Component::setup() {
|
||||
|
||||
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
||||
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
|
||||
if (this->is_failed()) {
|
||||
this->reset_to_construction_state();
|
||||
}
|
||||
|
||||
if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
|
||||
|
@ -11,25 +11,25 @@ static const char *const TAG = "datetime.date_entity";
|
||||
|
||||
void DateEntity::publish_state() {
|
||||
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
return;
|
||||
}
|
||||
if (this->year_ < 1970 || this->year_ > 3000) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||
return;
|
||||
}
|
||||
if (this->month_ < 1 || this->month_ > 12) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||
return;
|
||||
}
|
||||
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
||||
return;
|
||||
}
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||
this->state_callback_.call();
|
||||
}
|
||||
|
@ -13,9 +13,6 @@ namespace datetime {
|
||||
|
||||
class DateTimeBase : public EntityBase {
|
||||
public:
|
||||
/// Return whether this Datetime has gotten a full state yet.
|
||||
bool has_state() const { return this->has_state_; }
|
||||
|
||||
virtual ESPTime state_as_esptime() const = 0;
|
||||
|
||||
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
||||
@ -31,8 +28,6 @@ class DateTimeBase : public EntityBase {
|
||||
#ifdef USE_TIME
|
||||
time::RealTimeClock *rtc_;
|
||||
#endif
|
||||
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
#ifdef USE_TIME
|
||||
|
@ -11,40 +11,40 @@ static const char *const TAG = "datetime.datetime_entity";
|
||||
|
||||
void DateTimeEntity::publish_state() {
|
||||
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
return;
|
||||
}
|
||||
if (this->year_ < 1970 || this->year_ > 3000) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||
return;
|
||||
}
|
||||
if (this->month_ < 1 || this->month_ > 12) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||
return;
|
||||
}
|
||||
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
||||
return;
|
||||
}
|
||||
if (this->hour_ > 23) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||
return;
|
||||
}
|
||||
if (this->minute_ > 59) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||
return;
|
||||
}
|
||||
if (this->second_ > 59) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||
return;
|
||||
}
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
||||
this->state_callback_.call();
|
||||
|
@ -11,21 +11,21 @@ static const char *const TAG = "datetime.time_entity";
|
||||
|
||||
void TimeEntity::publish_state() {
|
||||
if (this->hour_ > 23) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||
return;
|
||||
}
|
||||
if (this->minute_ > 59) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||
return;
|
||||
}
|
||||
if (this->second_ > 59) {
|
||||
this->has_state_ = false;
|
||||
this->set_has_state(false);
|
||||
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||
return;
|
||||
}
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
||||
this->second_);
|
||||
this->state_callback_.call();
|
||||
|
@ -695,6 +695,7 @@ FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)
|
||||
async def to_code(config):
|
||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
||||
cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
|
||||
cg.set_cpp_standard("gnu++17")
|
||||
cg.add_build_flag("-DUSE_ESP32")
|
||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
|
||||
|
@ -57,7 +57,7 @@ void ESP32Camera::dump_config() {
|
||||
" External Clock: Pin:%d Frequency:%u\n"
|
||||
" I2C Pins: SDA:%d SCL:%d\n"
|
||||
" Reset Pin: %d",
|
||||
this->name_.c_str(), YESNO(this->internal_), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
|
||||
this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
|
||||
conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
|
||||
conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
|
||||
switch (this->config_.frame_size) {
|
||||
|
@ -183,6 +183,7 @@ async def to_code(config):
|
||||
|
||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
||||
cg.add_build_flag("-DUSE_ESP8266")
|
||||
cg.set_cpp_standard("gnu++17")
|
||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||
cg.add_define("ESPHOME_VARIANT", "ESP8266")
|
||||
|
||||
|
@ -19,9 +19,8 @@ void KMeterISOComponent::setup() {
|
||||
|
||||
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
||||
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
|
||||
if (this->is_failed()) {
|
||||
this->reset_to_construction_state();
|
||||
}
|
||||
|
||||
auto err = this->bus_->writev(this->address_, nullptr, 0);
|
||||
|
@ -3,6 +3,8 @@ from esphome.components import number
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MOVE_THRESHOLD,
|
||||
CONF_STILL_THRESHOLD,
|
||||
CONF_TIMEOUT,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
@ -24,8 +26,6 @@ MaxDistanceTimeoutNumber = ld2410_ns.class_("MaxDistanceTimeoutNumber", number.N
|
||||
CONF_MAX_MOVE_DISTANCE_GATE = "max_move_distance_gate"
|
||||
CONF_MAX_STILL_DISTANCE_GATE = "max_still_distance_gate"
|
||||
CONF_LIGHT_THRESHOLD = "light_threshold"
|
||||
CONF_STILL_THRESHOLD = "still_threshold"
|
||||
CONF_MOVE_THRESHOLD = "move_threshold"
|
||||
|
||||
TIMEOUT_GROUP = "timeout"
|
||||
|
||||
|
@ -3,6 +3,7 @@ from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_LIGHT,
|
||||
CONF_MOVING_DISTANCE,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
@ -17,7 +18,6 @@ from esphome.const import (
|
||||
from . import CONF_LD2410_ID, LD2410Component
|
||||
|
||||
DEPENDENCIES = ["ld2410"]
|
||||
CONF_MOVING_DISTANCE = "moving_distance"
|
||||
CONF_STILL_DISTANCE = "still_distance"
|
||||
CONF_MOVING_ENERGY = "moving_energy"
|
||||
CONF_STILL_ENERGY = "still_energy"
|
||||
|
@ -2,6 +2,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BLUETOOTH,
|
||||
DEVICE_CLASS_SWITCH,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_BLUETOOTH,
|
||||
@ -14,7 +15,6 @@ BluetoothSwitch = ld2410_ns.class_("BluetoothSwitch", switch.Switch)
|
||||
EngineeringModeSwitch = ld2410_ns.class_("EngineeringModeSwitch", switch.Switch)
|
||||
|
||||
CONF_ENGINEERING_MODE = "engineering_mode"
|
||||
CONF_BLUETOOTH = "bluetooth"
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
||||
|
@ -3,6 +3,8 @@ from esphome.components import number
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MOVE_THRESHOLD,
|
||||
CONF_STILL_THRESHOLD,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_MOTION_SENSOR,
|
||||
@ -31,8 +33,6 @@ LD2420StillThresholdNumbers = ld2420_ns.class_(
|
||||
)
|
||||
CONF_MIN_GATE_DISTANCE = "min_gate_distance"
|
||||
CONF_MAX_GATE_DISTANCE = "max_gate_distance"
|
||||
CONF_STILL_THRESHOLD = "still_threshold"
|
||||
CONF_MOVE_THRESHOLD = "move_threshold"
|
||||
CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity"
|
||||
CONF_GATE_STILL_SENSITIVITY = "gate_still_sensitivity"
|
||||
CONF_GATE_SELECT = "gate_select"
|
||||
|
@ -1,13 +1,17 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, DEVICE_CLASS_DISTANCE, UNIT_CENTIMETER
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MOVING_DISTANCE,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
UNIT_CENTIMETER,
|
||||
)
|
||||
|
||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||
|
||||
LD2420Sensor = ld2420_ns.class_("LD2420Sensor", sensor.Sensor, cg.Component)
|
||||
|
||||
CONF_MOVING_DISTANCE = "moving_distance"
|
||||
CONF_GATE_ENERGY = "gate_energy"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
|
@ -2,6 +2,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import switch
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BLUETOOTH,
|
||||
DEVICE_CLASS_SWITCH,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_BLUETOOTH,
|
||||
@ -13,7 +14,6 @@ from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch)
|
||||
MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch)
|
||||
|
||||
CONF_BLUETOOTH = "bluetooth"
|
||||
CONF_MULTI_TARGET = "multi_target"
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
|
@ -264,6 +264,7 @@ async def component_to_code(config):
|
||||
# force using arduino framework
|
||||
cg.add_platformio_option("framework", "arduino")
|
||||
cg.add_build_flag("-DUSE_ARDUINO")
|
||||
cg.set_cpp_standard("gnu++17")
|
||||
|
||||
# disable library compatibility checks
|
||||
cg.add_platformio_option("lib_ldf_mode", "off")
|
||||
|
@ -153,7 +153,7 @@ bool MQTTComponent::send_discovery_() {
|
||||
if (node_friendly_name.empty()) {
|
||||
node_friendly_name = node_name;
|
||||
}
|
||||
const std::string &node_area = App.get_area();
|
||||
std::string node_area = App.get_area();
|
||||
|
||||
JsonObject device_info = root.createNestedObject(MQTT_DEVICE);
|
||||
const auto mac = get_mac_address();
|
||||
|
@ -56,7 +56,7 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
}
|
||||
|
||||
this->update_component_settings();
|
||||
|
@ -88,7 +88,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
|
||||
} else {
|
||||
this->raw_state = state;
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
}
|
||||
}
|
||||
this->update_component_settings();
|
||||
|
@ -37,7 +37,7 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s
|
||||
this->publish_state(state);
|
||||
} else {
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
}
|
||||
|
||||
this->update_component_settings();
|
||||
|
@ -7,7 +7,7 @@ namespace number {
|
||||
static const char *const TAG = "number";
|
||||
|
||||
void Number::publish_state(float state) {
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state);
|
||||
this->state_callback_.call(state);
|
||||
|
@ -48,9 +48,6 @@ class Number : public EntityBase {
|
||||
|
||||
NumberTraits traits;
|
||||
|
||||
/// Return whether this number has gotten a full state yet.
|
||||
bool has_state() const { return has_state_; }
|
||||
|
||||
protected:
|
||||
friend class NumberCall;
|
||||
|
||||
@ -63,7 +60,6 @@ class Number : public EntityBase {
|
||||
virtual void control(float value) = 0;
|
||||
|
||||
CallbackManager<void(float)> state_callback_;
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
} // namespace number
|
||||
|
@ -167,6 +167,7 @@ async def to_code(config):
|
||||
cg.add_platformio_option("lib_ldf_mode", "chain+")
|
||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
||||
cg.add_build_flag("-DUSE_RP2040")
|
||||
cg.set_cpp_standard("gnu++17")
|
||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||
cg.add_define("ESPHOME_VARIANT", "RP2040")
|
||||
|
||||
|
@ -10,7 +10,7 @@ void Select::publish_state(const std::string &state) {
|
||||
auto index = this->index_of(state);
|
||||
const auto *name = this->get_name().c_str();
|
||||
if (index.has_value()) {
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", name, state.c_str(), index.value());
|
||||
this->state_callback_.call(state, index.value());
|
||||
|
@ -35,9 +35,6 @@ class Select : public EntityBase {
|
||||
|
||||
void publish_state(const std::string &state);
|
||||
|
||||
/// Return whether this select component has gotten a full state yet.
|
||||
bool has_state() const { return has_state_; }
|
||||
|
||||
/// Instantiate a SelectCall object to modify this select component's state.
|
||||
SelectCall make_call() { return SelectCall(this); }
|
||||
|
||||
@ -73,7 +70,6 @@ class Select : public EntityBase {
|
||||
virtual void control(const std::string &value) = 0;
|
||||
|
||||
CallbackManager<void(std::string, size_t)> state_callback_;
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
} // namespace select
|
||||
|
@ -38,7 +38,9 @@ StateClass Sensor::get_state_class() {
|
||||
|
||||
void Sensor::publish_state(float state) {
|
||||
this->raw_state = state;
|
||||
this->raw_callback_.call(state);
|
||||
if (this->raw_callback_) {
|
||||
this->raw_callback_->call(state);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state);
|
||||
|
||||
@ -51,7 +53,10 @@ void Sensor::publish_state(float state) {
|
||||
|
||||
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
|
||||
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
|
||||
this->raw_callback_.add(std::move(callback));
|
||||
if (!this->raw_callback_) {
|
||||
this->raw_callback_ = std::make_unique<CallbackManager<void(float)>>();
|
||||
}
|
||||
this->raw_callback_->add(std::move(callback));
|
||||
}
|
||||
|
||||
void Sensor::add_filter(Filter *filter) {
|
||||
@ -88,13 +93,12 @@ float Sensor::get_raw_state() const { return this->raw_state; }
|
||||
std::string Sensor::unique_id() { return ""; }
|
||||
|
||||
void Sensor::internal_send_state_to_frontend(float state) {
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
|
||||
this->get_unit_of_measurement().c_str(), this->get_accuracy_decimals());
|
||||
this->callback_.call(state);
|
||||
}
|
||||
bool Sensor::has_state() const { return this->has_state_; }
|
||||
|
||||
} // namespace sensor
|
||||
} // namespace esphome
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "esphome/components/sensor/filter.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace esphome {
|
||||
namespace sensor {
|
||||
@ -140,9 +141,6 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
|
||||
*/
|
||||
float raw_state;
|
||||
|
||||
/// Return whether this sensor has gotten a full state (that passed through all filters) yet.
|
||||
bool has_state() const;
|
||||
|
||||
/** Override this method to set the unique ID of this sensor.
|
||||
*
|
||||
* @deprecated Do not use for new sensors, a suitable unique ID is automatically generated (2023.4).
|
||||
@ -152,15 +150,14 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
|
||||
void internal_send_state_to_frontend(float state);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
|
||||
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
||||
std::unique_ptr<CallbackManager<void(float)>> raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
|
||||
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
||||
|
||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||
|
||||
optional<int8_t> accuracy_decimals_; ///< Accuracy in decimals override
|
||||
optional<StateClass> state_class_{STATE_CLASS_NONE}; ///< State class override
|
||||
bool force_update_{false}; ///< Force update mode
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
} // namespace sensor
|
||||
|
@ -9,10 +9,10 @@ namespace status_led {
|
||||
static const char *const TAG = "status_led";
|
||||
|
||||
void StatusLEDLightOutput::loop() {
|
||||
uint32_t new_state = App.get_app_state() & STATUS_LED_MASK;
|
||||
uint8_t new_state = App.get_app_state() & STATUS_LED_MASK;
|
||||
|
||||
if (new_state != this->last_app_state_) {
|
||||
ESP_LOGV(TAG, "New app state 0x%08" PRIX32, new_state);
|
||||
ESP_LOGV(TAG, "New app state 0x%02X", new_state);
|
||||
}
|
||||
|
||||
if ((new_state & STATUS_LED_ERROR) != 0u) {
|
||||
|
@ -36,7 +36,7 @@ class StatusLEDLightOutput : public light::LightOutput, public Component {
|
||||
GPIOPin *pin_{nullptr};
|
||||
output::BinaryOutput *output_{nullptr};
|
||||
light::LightState *lightstate_{};
|
||||
uint32_t last_app_state_{0xFFFF};
|
||||
uint8_t last_app_state_{0xFF};
|
||||
void output_state_(bool state);
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ namespace text {
|
||||
static const char *const TAG = "text";
|
||||
|
||||
void Text::publish_state(const std::string &state) {
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
if (this->traits.get_mode() == TEXT_MODE_PASSWORD) {
|
||||
ESP_LOGD(TAG, "'%s': Sending state " LOG_SECRET("'%s'"), this->get_name().c_str(), state.c_str());
|
||||
|
@ -28,9 +28,6 @@ class Text : public EntityBase {
|
||||
|
||||
void publish_state(const std::string &state);
|
||||
|
||||
/// Return whether this text input has gotten a full state yet.
|
||||
bool has_state() const { return has_state_; }
|
||||
|
||||
/// Instantiate a TextCall object to modify this text component's state.
|
||||
TextCall make_call() { return TextCall(this); }
|
||||
|
||||
@ -48,7 +45,6 @@ class Text : public EntityBase {
|
||||
virtual void control(const std::string &value) = 0;
|
||||
|
||||
CallbackManager<void(std::string)> state_callback_;
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
} // namespace text
|
||||
|
@ -8,7 +8,9 @@ static const char *const TAG = "text_sensor";
|
||||
|
||||
void TextSensor::publish_state(const std::string &state) {
|
||||
this->raw_state = state;
|
||||
this->raw_callback_.call(state);
|
||||
if (this->raw_callback_) {
|
||||
this->raw_callback_->call(state);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str());
|
||||
|
||||
@ -53,20 +55,22 @@ void TextSensor::add_on_state_callback(std::function<void(std::string)> callback
|
||||
this->callback_.add(std::move(callback));
|
||||
}
|
||||
void TextSensor::add_on_raw_state_callback(std::function<void(std::string)> callback) {
|
||||
this->raw_callback_.add(std::move(callback));
|
||||
if (!this->raw_callback_) {
|
||||
this->raw_callback_ = std::make_unique<CallbackManager<void(std::string)>>();
|
||||
}
|
||||
this->raw_callback_->add(std::move(callback));
|
||||
}
|
||||
|
||||
std::string TextSensor::get_state() const { return this->state; }
|
||||
std::string TextSensor::get_raw_state() const { return this->raw_state; }
|
||||
void TextSensor::internal_send_state_to_frontend(const std::string &state) {
|
||||
this->state = state;
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str());
|
||||
this->callback_.call(state);
|
||||
}
|
||||
|
||||
std::string TextSensor::unique_id() { return ""; }
|
||||
bool TextSensor::has_state() { return this->has_state_; }
|
||||
|
||||
} // namespace text_sensor
|
||||
} // namespace esphome
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "esphome/components/text_sensor/filter.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace esphome {
|
||||
namespace text_sensor {
|
||||
@ -33,6 +34,8 @@ namespace text_sensor {
|
||||
|
||||
class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
TextSensor() = default;
|
||||
|
||||
/// Getter-syntax for .state.
|
||||
std::string get_state() const;
|
||||
/// Getter-syntax for .raw_state
|
||||
@ -67,17 +70,14 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
*/
|
||||
virtual std::string unique_id();
|
||||
|
||||
bool has_state();
|
||||
|
||||
void internal_send_state_to_frontend(const std::string &state);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(std::string)> raw_callback_; ///< Storage for raw state callbacks.
|
||||
CallbackManager<void(std::string)> callback_; ///< Storage for filtered state callbacks.
|
||||
std::unique_ptr<CallbackManager<void(std::string)>>
|
||||
raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
|
||||
CallbackManager<void(std::string)> callback_; ///< Storage for filtered state callbacks.
|
||||
|
||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||
|
||||
bool has_state_{false};
|
||||
};
|
||||
|
||||
} // namespace text_sensor
|
||||
|
@ -30,7 +30,7 @@ void UpdateEntity::publish_state() {
|
||||
ESP_LOGD(TAG, " Progress: %.0f%%", this->update_info_.progress);
|
||||
}
|
||||
|
||||
this->has_state_ = true;
|
||||
this->set_has_state(true);
|
||||
this->state_callback_.call();
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@ enum UpdateState : uint8_t {
|
||||
|
||||
class UpdateEntity : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
bool has_state() const { return this->has_state_; }
|
||||
|
||||
void publish_state();
|
||||
|
||||
void perform() { this->perform(false); }
|
||||
@ -44,7 +42,6 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass {
|
||||
protected:
|
||||
UpdateState state_{UPDATE_STATE_UNKNOWN};
|
||||
UpdateInfo update_info_;
|
||||
bool has_state_{false};
|
||||
|
||||
CallbackManager<void()> state_callback_{};
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ static const char *const TAG = "uptime.sensor";
|
||||
|
||||
void UptimeTimestampSensor::setup() {
|
||||
this->time_->add_on_time_sync_callback([this]() {
|
||||
if (this->has_state_)
|
||||
if (this->has_state())
|
||||
return; // No need to update the timestamp if it's already set
|
||||
|
||||
auto now = this->time_->now();
|
||||
|
@ -8,8 +8,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.only_with_esp_idf,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["web_server"]
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
# Increase the maximum supported size of headers section in HTTP request packet to be processed by the server
|
||||
|
@ -9,10 +9,12 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "web_server_idf.h"
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
#include "esphome/components/web_server/web_server.h"
|
||||
#include "esphome/components/web_server/list_entities.h"
|
||||
|
||||
#include "web_server_idf.h"
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
namespace esphome {
|
||||
namespace web_server_idf {
|
||||
@ -273,6 +275,7 @@ void AsyncResponseStream::printf(const char *fmt, ...) {
|
||||
this->print(str);
|
||||
}
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
AsyncEventSource::~AsyncEventSource() {
|
||||
for (auto *ses : this->sessions_) {
|
||||
delete ses; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
@ -511,6 +514,7 @@ void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *e
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace web_server_idf
|
||||
} // namespace esphome
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#include <functional>
|
||||
@ -12,10 +13,12 @@
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
#ifdef USE_WEBSERVER
|
||||
namespace web_server {
|
||||
class WebServer;
|
||||
class ListEntitiesIterator;
|
||||
}; // namespace web_server
|
||||
#endif
|
||||
namespace web_server_idf {
|
||||
|
||||
#define F(string_literal) (string_literal)
|
||||
@ -220,6 +223,7 @@ class AsyncWebHandler {
|
||||
virtual bool isRequestHandlerTrivial() { return true; }
|
||||
};
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
class AsyncEventSource;
|
||||
class AsyncEventSourceResponse;
|
||||
|
||||
@ -307,10 +311,13 @@ class AsyncEventSource : public AsyncWebHandler {
|
||||
connect_handler_t on_connect_{};
|
||||
esphome::web_server::WebServer *web_server_;
|
||||
};
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
class DefaultHeaders {
|
||||
friend class AsyncWebServerRequest;
|
||||
#ifdef USE_WEBSERVER
|
||||
friend class AsyncEventSourceResponse;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
|
@ -102,7 +102,7 @@ WeikaiRegister &WeikaiRegister::operator|=(uint8_t value) {
|
||||
// The WeikaiComponent methods
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void WeikaiComponent::loop() {
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP)
|
||||
if (!this->is_in_loop_state())
|
||||
return;
|
||||
|
||||
// If there are some bytes in the receive FIFO we transfers them to the ring buffers
|
||||
|
@ -89,6 +89,7 @@ CONF_BIT_DEPTH = "bit_depth"
|
||||
CONF_BITS_PER_SAMPLE = "bits_per_sample"
|
||||
CONF_BLOCK = "block"
|
||||
CONF_BLUE = "blue"
|
||||
CONF_BLUETOOTH = "bluetooth"
|
||||
CONF_BOARD = "board"
|
||||
CONF_BOARD_FLASH_MODE = "board_flash_mode"
|
||||
CONF_BORDER = "border"
|
||||
@ -527,7 +528,9 @@ CONF_MONTH = "month"
|
||||
CONF_MONTHS = "months"
|
||||
CONF_MOSI_PIN = "mosi_pin"
|
||||
CONF_MOTION = "motion"
|
||||
CONF_MOVE_THRESHOLD = "move_threshold"
|
||||
CONF_MOVEMENT_COUNTER = "movement_counter"
|
||||
CONF_MOVING_DISTANCE = "moving_distance"
|
||||
CONF_MQTT = "mqtt"
|
||||
CONF_MQTT_ID = "mqtt_id"
|
||||
CONF_MULTIPLE = "multiple"
|
||||
@ -835,6 +838,7 @@ CONF_STEP = "step"
|
||||
CONF_STEP_DELAY = "step_delay"
|
||||
CONF_STEP_MODE = "step_mode"
|
||||
CONF_STEP_PIN = "step_pin"
|
||||
CONF_STILL_THRESHOLD = "still_threshold"
|
||||
CONF_STOP = "stop"
|
||||
CONF_STOP_ACTION = "stop_action"
|
||||
CONF_STORE_BASELINE = "store_baseline"
|
||||
|
@ -507,6 +507,8 @@ class EsphomeCore:
|
||||
self.libraries: list[Library] = []
|
||||
# A set of build flags to set in the platformio project
|
||||
self.build_flags: set[str] = set()
|
||||
# A set of build unflags to set in the platformio project
|
||||
self.build_unflags: set[str] = set()
|
||||
# A set of defines to set for the compile process in esphome/core/defines.h
|
||||
self.defines: set[Define] = set()
|
||||
# A map of all platformio options to apply
|
||||
@ -545,6 +547,7 @@ class EsphomeCore:
|
||||
self.global_statements = []
|
||||
self.libraries = []
|
||||
self.build_flags = set()
|
||||
self.build_unflags = set()
|
||||
self.defines = set()
|
||||
self.platformio_options = {}
|
||||
self.loaded_integrations = set()
|
||||
@ -766,11 +769,15 @@ class EsphomeCore:
|
||||
self.libraries.append(library)
|
||||
return library
|
||||
|
||||
def add_build_flag(self, build_flag):
|
||||
def add_build_flag(self, build_flag: str) -> str:
|
||||
self.build_flags.add(build_flag)
|
||||
_LOGGER.debug("Adding build flag: %s", build_flag)
|
||||
return build_flag
|
||||
|
||||
def add_build_unflag(self, build_unflag: str) -> None:
|
||||
self.build_unflags.add(build_unflag)
|
||||
_LOGGER.debug("Adding build unflag: %s", build_unflag)
|
||||
|
||||
def add_define(self, define):
|
||||
if isinstance(define, str):
|
||||
define = Define(define)
|
||||
|
@ -66,7 +66,7 @@ void Application::setup() {
|
||||
[](Component *a, Component *b) { return a->get_loop_priority() > b->get_loop_priority(); });
|
||||
|
||||
do {
|
||||
uint32_t new_app_state = STATUS_LED_WARNING;
|
||||
uint8_t new_app_state = STATUS_LED_WARNING;
|
||||
this->scheduler.call();
|
||||
this->feed_wdt();
|
||||
for (uint32_t j = 0; j <= i; j++) {
|
||||
@ -87,7 +87,7 @@ void Application::setup() {
|
||||
this->calculate_looping_components_();
|
||||
}
|
||||
void Application::loop() {
|
||||
uint32_t new_app_state = 0;
|
||||
uint8_t new_app_state = 0;
|
||||
|
||||
this->scheduler.call();
|
||||
|
||||
|
@ -87,8 +87,8 @@ static const uint32_t TEARDOWN_TIMEOUT_REBOOT_MS = 1000; // 1 second for quick
|
||||
|
||||
class Application {
|
||||
public:
|
||||
void pre_setup(const std::string &name, const std::string &friendly_name, const std::string &area,
|
||||
const char *comment, const char *compilation_time, bool name_add_mac_suffix) {
|
||||
void pre_setup(const std::string &name, const std::string &friendly_name, const char *area, const char *comment,
|
||||
const char *compilation_time, bool name_add_mac_suffix) {
|
||||
arch_init();
|
||||
this->name_add_mac_suffix_ = name_add_mac_suffix;
|
||||
if (name_add_mac_suffix) {
|
||||
@ -285,7 +285,7 @@ class Application {
|
||||
const std::string &get_friendly_name() const { return this->friendly_name_; }
|
||||
|
||||
/// Get the area of this Application set by pre_setup().
|
||||
const std::string &get_area() const { return this->area_; }
|
||||
std::string get_area() const { return this->area_ == nullptr ? "" : this->area_; }
|
||||
|
||||
/// Get the comment of this Application set by pre_setup().
|
||||
std::string get_comment() const { return this->comment_; }
|
||||
@ -332,7 +332,7 @@ class Application {
|
||||
*/
|
||||
void teardown_components(uint32_t timeout_ms);
|
||||
|
||||
uint32_t get_app_state() const { return this->app_state_; }
|
||||
uint8_t get_app_state() const { return this->app_state_; }
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
const std::vector<binary_sensor::BinarySensor *> &get_binary_sensors() { return this->binary_sensors_; }
|
||||
@ -646,14 +646,14 @@ class Application {
|
||||
|
||||
std::string name_;
|
||||
std::string friendly_name_;
|
||||
std::string area_;
|
||||
const char *area_{nullptr};
|
||||
const char *comment_{nullptr};
|
||||
const char *compilation_time_{nullptr};
|
||||
bool name_add_mac_suffix_;
|
||||
uint32_t last_loop_{0};
|
||||
uint32_t loop_interval_{16};
|
||||
size_t dump_config_at_{SIZE_MAX};
|
||||
uint32_t app_state_{0};
|
||||
uint8_t app_state_{0};
|
||||
Component *current_component_{nullptr};
|
||||
uint32_t loop_component_start_time_{0};
|
||||
|
||||
|
@ -29,15 +29,17 @@ const float LATE = -100.0f;
|
||||
|
||||
} // namespace setup_priority
|
||||
|
||||
const uint32_t COMPONENT_STATE_MASK = 0xFF;
|
||||
const uint32_t COMPONENT_STATE_CONSTRUCTION = 0x00;
|
||||
const uint32_t COMPONENT_STATE_SETUP = 0x01;
|
||||
const uint32_t COMPONENT_STATE_LOOP = 0x02;
|
||||
const uint32_t COMPONENT_STATE_FAILED = 0x03;
|
||||
const uint32_t STATUS_LED_MASK = 0xFF00;
|
||||
const uint32_t STATUS_LED_OK = 0x0000;
|
||||
const uint32_t STATUS_LED_WARNING = 0x0100;
|
||||
const uint32_t STATUS_LED_ERROR = 0x0200;
|
||||
// Component state uses bits 0-1 (4 states)
|
||||
const uint8_t COMPONENT_STATE_MASK = 0x03;
|
||||
const uint8_t COMPONENT_STATE_CONSTRUCTION = 0x00;
|
||||
const uint8_t COMPONENT_STATE_SETUP = 0x01;
|
||||
const uint8_t COMPONENT_STATE_LOOP = 0x02;
|
||||
const uint8_t COMPONENT_STATE_FAILED = 0x03;
|
||||
// Status LED uses bits 2-3
|
||||
const uint8_t STATUS_LED_MASK = 0x0C;
|
||||
const uint8_t STATUS_LED_OK = 0x00;
|
||||
const uint8_t STATUS_LED_WARNING = 0x04; // Bit 2
|
||||
const uint8_t STATUS_LED_ERROR = 0x08; // Bit 3
|
||||
|
||||
const uint32_t WARN_IF_BLOCKING_OVER_MS = 50U; ///< Initial blocking time allowed without warning
|
||||
const uint32_t WARN_IF_BLOCKING_INCREMENT_MS = 10U; ///< How long the blocking time must be larger to warn again
|
||||
@ -86,9 +88,9 @@ void Component::call_dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Component::get_component_state() const { return this->component_state_; }
|
||||
uint8_t Component::get_component_state() const { return this->component_state_; }
|
||||
void Component::call() {
|
||||
uint32_t state = this->component_state_ & COMPONENT_STATE_MASK;
|
||||
uint8_t state = this->component_state_ & COMPONENT_STATE_MASK;
|
||||
switch (state) {
|
||||
case COMPONENT_STATE_CONSTRUCTION:
|
||||
// State Construction: Call setup and set state to setup
|
||||
@ -131,6 +133,18 @@ void Component::mark_failed() {
|
||||
this->component_state_ |= COMPONENT_STATE_FAILED;
|
||||
this->status_set_error();
|
||||
}
|
||||
void Component::reset_to_construction_state() {
|
||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
||||
ESP_LOGI(TAG, "Component %s is being reset to construction state.", this->get_component_source());
|
||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
|
||||
// Clear error status when resetting
|
||||
this->status_clear_error();
|
||||
}
|
||||
}
|
||||
bool Component::is_in_loop_state() const {
|
||||
return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP;
|
||||
}
|
||||
void Component::defer(std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_timeout(this, "", 0, std::move(f));
|
||||
}
|
||||
|
@ -53,15 +53,15 @@ static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL;
|
||||
ESP_LOGCONFIG(TAG, " Update Interval: %.1fs", this->get_update_interval() / 1000.0f); \
|
||||
}
|
||||
|
||||
extern const uint32_t COMPONENT_STATE_MASK;
|
||||
extern const uint32_t COMPONENT_STATE_CONSTRUCTION;
|
||||
extern const uint32_t COMPONENT_STATE_SETUP;
|
||||
extern const uint32_t COMPONENT_STATE_LOOP;
|
||||
extern const uint32_t COMPONENT_STATE_FAILED;
|
||||
extern const uint32_t STATUS_LED_MASK;
|
||||
extern const uint32_t STATUS_LED_OK;
|
||||
extern const uint32_t STATUS_LED_WARNING;
|
||||
extern const uint32_t STATUS_LED_ERROR;
|
||||
extern const uint8_t COMPONENT_STATE_MASK;
|
||||
extern const uint8_t COMPONENT_STATE_CONSTRUCTION;
|
||||
extern const uint8_t COMPONENT_STATE_SETUP;
|
||||
extern const uint8_t COMPONENT_STATE_LOOP;
|
||||
extern const uint8_t COMPONENT_STATE_FAILED;
|
||||
extern const uint8_t STATUS_LED_MASK;
|
||||
extern const uint8_t STATUS_LED_OK;
|
||||
extern const uint8_t STATUS_LED_WARNING;
|
||||
extern const uint8_t STATUS_LED_ERROR;
|
||||
|
||||
enum class RetryResult { DONE, RETRY };
|
||||
|
||||
@ -123,7 +123,19 @@ class Component {
|
||||
*/
|
||||
virtual void on_powerdown() {}
|
||||
|
||||
uint32_t get_component_state() const;
|
||||
uint8_t get_component_state() const;
|
||||
|
||||
/** Reset this component back to the construction state to allow setup to run again.
|
||||
*
|
||||
* This can be used by components that have recoverable failures to attempt setup again.
|
||||
*/
|
||||
void reset_to_construction_state();
|
||||
|
||||
/** Check if this component has completed setup and is in the loop state.
|
||||
*
|
||||
* @return True if in loop state, false otherwise.
|
||||
*/
|
||||
bool is_in_loop_state() const;
|
||||
|
||||
/** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called.
|
||||
*
|
||||
@ -298,7 +310,12 @@ class Component {
|
||||
/// Cancel a defer callback using the specified name, name must not be empty.
|
||||
bool cancel_defer(const std::string &name); // NOLINT
|
||||
|
||||
uint32_t component_state_{0x0000}; ///< State of this component.
|
||||
/// State of this component - each bit has a purpose:
|
||||
/// Bits 0-1: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED)
|
||||
/// Bit 2: STATUS_LED_WARNING
|
||||
/// Bit 3: STATUS_LED_ERROR
|
||||
/// Bits 4-7: Unused - reserved for future expansion (50% of the bits are free)
|
||||
uint8_t component_state_{0x00};
|
||||
float setup_priority_override_{NAN};
|
||||
const char *component_source_{nullptr};
|
||||
uint32_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS};
|
||||
|
@ -12,20 +12,12 @@ void EntityBase::set_name(const char *name) {
|
||||
this->name_ = StringRef(name);
|
||||
if (this->name_.empty()) {
|
||||
this->name_ = StringRef(App.get_friendly_name());
|
||||
this->has_own_name_ = false;
|
||||
this->flags_.has_own_name = false;
|
||||
} else {
|
||||
this->has_own_name_ = true;
|
||||
this->flags_.has_own_name = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Entity Internal
|
||||
bool EntityBase::is_internal() const { return this->internal_; }
|
||||
void EntityBase::set_internal(bool internal) { this->internal_ = internal; }
|
||||
|
||||
// Entity Disabled by Default
|
||||
bool EntityBase::is_disabled_by_default() const { return this->disabled_by_default_; }
|
||||
void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; }
|
||||
|
||||
// Entity Icon
|
||||
std::string EntityBase::get_icon() const {
|
||||
if (this->icon_c_str_ == nullptr) {
|
||||
@ -35,14 +27,10 @@ std::string EntityBase::get_icon() const {
|
||||
}
|
||||
void EntityBase::set_icon(const char *icon) { this->icon_c_str_ = icon; }
|
||||
|
||||
// Entity Category
|
||||
EntityCategory EntityBase::get_entity_category() const { return this->entity_category_; }
|
||||
void EntityBase::set_entity_category(EntityCategory entity_category) { this->entity_category_ = entity_category; }
|
||||
|
||||
// Entity Object ID
|
||||
std::string EntityBase::get_object_id() const {
|
||||
// Check if `App.get_friendly_name()` is constant or dynamic.
|
||||
if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) {
|
||||
if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) {
|
||||
// `App.get_friendly_name()` is dynamic.
|
||||
return str_sanitize(str_snake_case(App.get_friendly_name()));
|
||||
} else {
|
||||
@ -61,7 +49,7 @@ void EntityBase::set_object_id(const char *object_id) {
|
||||
// Calculate Object ID Hash from Entity Name
|
||||
void EntityBase::calc_object_id_() {
|
||||
// Check if `App.get_friendly_name()` is constant or dynamic.
|
||||
if (!this->has_own_name_ && App.is_name_add_mac_suffix_enabled()) {
|
||||
if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) {
|
||||
// `App.get_friendly_name()` is dynamic.
|
||||
const auto object_id = str_sanitize(str_snake_case(App.get_friendly_name()));
|
||||
// FNV-1 hash
|
||||
|
@ -22,7 +22,7 @@ class EntityBase {
|
||||
void set_name(const char *name);
|
||||
|
||||
// Get whether this Entity has its own name or it should use the device friendly_name.
|
||||
bool has_own_name() const { return this->has_own_name_; }
|
||||
bool has_own_name() const { return this->flags_.has_own_name; }
|
||||
|
||||
// Get the sanitized name of this Entity as an ID.
|
||||
std::string get_object_id() const;
|
||||
@ -32,23 +32,31 @@ class EntityBase {
|
||||
uint32_t get_object_id_hash();
|
||||
|
||||
// Get/set whether this Entity should be hidden outside ESPHome
|
||||
bool is_internal() const;
|
||||
void set_internal(bool internal);
|
||||
bool is_internal() const { return this->flags_.internal; }
|
||||
void set_internal(bool internal) { this->flags_.internal = internal; }
|
||||
|
||||
// Check if this object is declared to be disabled by default.
|
||||
// That means that when the device gets added to Home Assistant (or other clients) it should
|
||||
// not be added to the default view by default, and a user action is necessary to manually add it.
|
||||
bool is_disabled_by_default() const;
|
||||
void set_disabled_by_default(bool disabled_by_default);
|
||||
bool is_disabled_by_default() const { return this->flags_.disabled_by_default; }
|
||||
void set_disabled_by_default(bool disabled_by_default) { this->flags_.disabled_by_default = disabled_by_default; }
|
||||
|
||||
// Get/set the entity category.
|
||||
EntityCategory get_entity_category() const;
|
||||
void set_entity_category(EntityCategory entity_category);
|
||||
EntityCategory get_entity_category() const { return static_cast<EntityCategory>(this->flags_.entity_category); }
|
||||
void set_entity_category(EntityCategory entity_category) {
|
||||
this->flags_.entity_category = static_cast<uint8_t>(entity_category);
|
||||
}
|
||||
|
||||
// Get/set this entity's icon
|
||||
std::string get_icon() const;
|
||||
void set_icon(const char *icon);
|
||||
|
||||
// Check if this entity has state
|
||||
bool has_state() const { return this->flags_.has_state; }
|
||||
|
||||
// Set has_state - for components that need to manually set this
|
||||
void set_has_state(bool state) { this->flags_.has_state = state; }
|
||||
|
||||
protected:
|
||||
/// The hash_base() function has been deprecated. It is kept in this
|
||||
/// class for now, to prevent external components from not compiling.
|
||||
@ -59,11 +67,16 @@ class EntityBase {
|
||||
const char *object_id_c_str_{nullptr};
|
||||
const char *icon_c_str_{nullptr};
|
||||
uint32_t object_id_hash_{};
|
||||
bool has_own_name_{false};
|
||||
bool internal_{false};
|
||||
bool disabled_by_default_{false};
|
||||
EntityCategory entity_category_{ENTITY_CATEGORY_NONE};
|
||||
bool has_state_{};
|
||||
|
||||
// Bit-packed flags to save memory (1 byte instead of 5)
|
||||
struct EntityFlags {
|
||||
uint8_t has_own_name : 1;
|
||||
uint8_t internal : 1;
|
||||
uint8_t disabled_by_default : 1;
|
||||
uint8_t has_state : 1;
|
||||
uint8_t entity_category : 2; // Supports up to 4 categories
|
||||
uint8_t reserved : 2; // Reserved for future use
|
||||
} flags_{};
|
||||
};
|
||||
|
||||
class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming)
|
||||
|
@ -438,7 +438,7 @@ template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::stri
|
||||
}
|
||||
|
||||
/// Return values for parse_on_off().
|
||||
enum ParseOnOffState {
|
||||
enum ParseOnOffState : uint8_t {
|
||||
PARSE_NONE = 0,
|
||||
PARSE_ON,
|
||||
PARSE_OFF,
|
||||
|
@ -608,6 +608,17 @@ def add_build_flag(build_flag: str):
|
||||
CORE.add_build_flag(build_flag)
|
||||
|
||||
|
||||
def add_build_unflag(build_unflag: str) -> None:
|
||||
"""Add a global build unflag to the compiler flags."""
|
||||
CORE.add_build_unflag(build_unflag)
|
||||
|
||||
|
||||
def set_cpp_standard(standard: str) -> None:
|
||||
"""Set C++ standard with compiler flag `-std={standard}`."""
|
||||
CORE.add_build_unflag("-std=gnu++11")
|
||||
CORE.add_build_flag(f"-std={standard}")
|
||||
|
||||
|
||||
def add_define(name: str, value: SafeExpType = None):
|
||||
"""Add a global define to the auto-generated defines.h file.
|
||||
|
||||
|
@ -153,6 +153,9 @@ def get_ini_content():
|
||||
# Sort to avoid changing build flags order
|
||||
CORE.add_platformio_option("build_flags", sorted(CORE.build_flags))
|
||||
|
||||
# Sort to avoid changing build unflags order
|
||||
CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
|
||||
|
||||
content = "[platformio]\n"
|
||||
content += f"description = ESPHome {__version__}\n"
|
||||
|
||||
|
@ -48,6 +48,9 @@ lib_deps =
|
||||
lvgl/lvgl@8.4.0 ; lvgl
|
||||
build_flags =
|
||||
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
-std=gnu++17
|
||||
build_unflags =
|
||||
-std=gnu++11
|
||||
src_filter =
|
||||
+<./>
|
||||
+<../tests/dummy_main.cpp>
|
||||
@ -73,6 +76,8 @@ lib_deps =
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_ARDUINO
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
; This are common settings for all IDF-framework based environments.
|
||||
[common:idf]
|
||||
@ -80,6 +85,8 @@ extends = common
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_ESP_IDF
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
; This are common settings for the ESP8266 using Arduino.
|
||||
[common:esp8266-arduino]
|
||||
@ -104,6 +111,8 @@ build_flags =
|
||||
-Wno-nonnull-compare
|
||||
-DUSE_ESP8266
|
||||
-DUSE_ESP8266_FRAMEWORK_ARDUINO
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
extra_scripts = post:esphome/components/esp8266/post_build.py.script
|
||||
|
||||
; This are common settings for the ESP32 (all variants) using Arduino.
|
||||
@ -135,6 +144,8 @@ build_flags =
|
||||
-DUSE_ESP32
|
||||
-DUSE_ESP32_FRAMEWORK_ARDUINO
|
||||
-DAUDIO_NO_SD_FS ; i2s_audio
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
extra_scripts = post:esphome/components/esp32/post_build.py.script
|
||||
|
||||
; This are common settings for the ESP32 (all variants) using IDF.
|
||||
@ -155,6 +166,8 @@ build_flags =
|
||||
-Wno-nonnull-compare
|
||||
-DUSE_ESP32
|
||||
-DUSE_ESP32_FRAMEWORK_ESP_IDF
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
extra_scripts = post:esphome/components/esp32/post_build.py.script
|
||||
|
||||
; This are common settings for the ESP32 using the latest ESP-IDF version.
|
||||
@ -181,6 +194,8 @@ build_flags =
|
||||
${common:arduino.build_flags}
|
||||
-DUSE_RP2040
|
||||
-DUSE_RP2040_FRAMEWORK_ARDUINO
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
; This are common settings for the LibreTiny (all variants) using Arduino.
|
||||
[common:libretiny-arduino]
|
||||
@ -192,6 +207,8 @@ lib_deps =
|
||||
build_flags =
|
||||
${common:arduino.build_flags}
|
||||
-DUSE_LIBRETINY
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
build_src_flags = -include Arduino.h
|
||||
|
||||
; This is the common settings for the nRF52 using Zephyr.
|
||||
@ -224,6 +241,8 @@ board = nodemcuv2
|
||||
build_flags =
|
||||
${common:esp8266-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp8266-arduino-tidy]
|
||||
extends = common:esp8266-arduino
|
||||
@ -231,6 +250,8 @@ board = nodemcuv2
|
||||
build_flags =
|
||||
${common:esp8266-arduino.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; ESP32 ;;;;;;;;
|
||||
|
||||
@ -242,6 +263,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32-arduino-tidy]
|
||||
extends = common:esp32-arduino
|
||||
@ -250,6 +273,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32-idf]
|
||||
extends = common:esp32-idf
|
||||
@ -259,6 +284,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32-idf-5_3]
|
||||
extends = common:esp32-idf-5_3
|
||||
@ -268,6 +295,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32-idf-tidy]
|
||||
extends = common:esp32-idf
|
||||
@ -277,6 +306,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; ESP32-C3 ;;;;;;;;
|
||||
|
||||
@ -287,6 +318,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32C3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32c3-arduino-tidy]
|
||||
extends = common:esp32-arduino
|
||||
@ -295,6 +328,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32C3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32c3-idf]
|
||||
extends = common:esp32-idf
|
||||
@ -304,6 +339,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32C3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32c3-idf-5_3]
|
||||
extends = common:esp32-idf-5_3
|
||||
@ -313,6 +350,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32C3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32c3-idf-tidy]
|
||||
extends = common:esp32-idf
|
||||
@ -322,6 +361,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32C3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; ESP32-C6 ;;;;;;;;
|
||||
|
||||
@ -343,6 +384,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S2
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s2-arduino-tidy]
|
||||
extends = common:esp32-arduino
|
||||
@ -351,6 +394,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S2
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s2-idf]
|
||||
extends = common:esp32-idf
|
||||
@ -360,6 +405,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S2
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s2-idf-5_3]
|
||||
extends = common:esp32-idf-5_3
|
||||
@ -369,6 +416,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S2
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s2-idf-tidy]
|
||||
extends = common:esp32-idf
|
||||
@ -378,6 +427,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S2
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; ESP32-S3 ;;;;;;;;
|
||||
|
||||
@ -388,6 +439,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s3-arduino-tidy]
|
||||
extends = common:esp32-arduino
|
||||
@ -396,6 +449,8 @@ build_flags =
|
||||
${common:esp32-arduino.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s3-idf]
|
||||
extends = common:esp32-idf
|
||||
@ -405,6 +460,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s3-idf-5_3]
|
||||
extends = common:esp32-idf-5_3
|
||||
@ -414,6 +471,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:esp32s3-idf-tidy]
|
||||
extends = common:esp32-idf
|
||||
@ -423,6 +482,8 @@ build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
-DUSE_ESP32_VARIANT_ESP32S3
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; ESP32-P4 ;;;;;;;;
|
||||
|
||||
@ -444,6 +505,8 @@ board = rpipico
|
||||
build_flags =
|
||||
${common:rp2040-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; LibreTiny ;;;;;;;;
|
||||
|
||||
@ -455,6 +518,8 @@ build_flags =
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_BK72XX
|
||||
-DUSE_LIBRETINY_VARIANT_BK7231N
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:rtl87xxb-arduino]
|
||||
extends = common:libretiny-arduino
|
||||
@ -464,6 +529,8 @@ build_flags =
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_RTL87XX
|
||||
-DUSE_LIBRETINY_VARIANT_RTL8710B
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:rtl87xxc-arduino]
|
||||
extends = common:libretiny-arduino
|
||||
@ -473,6 +540,8 @@ build_flags =
|
||||
${flags:runtime.build_flags}
|
||||
-DUSE_RTL87XX
|
||||
-DUSE_LIBRETINY_VARIANT_RTL8720C
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:host]
|
||||
extends = common
|
||||
@ -483,6 +552,8 @@ build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_HOST
|
||||
-std=c++17
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
;;;;;;;; nRF52 ;;;;;;;;
|
||||
|
||||
@ -492,6 +563,8 @@ board = adafruit_feather_nrf52840
|
||||
build_flags =
|
||||
${common:nrf52-zephyr.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
||||
[env:nrf52-tidy]
|
||||
extends = common:nrf52-zephyr
|
||||
@ -499,3 +572,5 @@ board = adafruit_feather_nrf52840
|
||||
build_flags =
|
||||
${common:nrf52-zephyr.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
build_unflags =
|
||||
${common.build_unflags}
|
||||
|
@ -119,6 +119,21 @@ async def yaml_config(request: pytest.FixtureRequest, unused_tcp_port: int) -> s
|
||||
# Add port configuration after api:
|
||||
content = content.replace("api:", f"api:\n port: {unused_tcp_port}")
|
||||
|
||||
# Add debug build flags for integration tests to enable assertions
|
||||
if "esphome:" in content:
|
||||
# Check if platformio_options already exists
|
||||
if "platformio_options:" not in content:
|
||||
# Add platformio_options with debug flags after esphome:
|
||||
content = content.replace(
|
||||
"esphome:",
|
||||
"esphome:\n"
|
||||
" # Enable assertions for integration tests\n"
|
||||
" platformio_options:\n"
|
||||
" build_flags:\n"
|
||||
' - "-DDEBUG" # Enable assert() statements\n'
|
||||
' - "-g" # Add debug symbols',
|
||||
)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
|
108
tests/integration/fixtures/host_mode_entity_fields.yaml
Normal file
108
tests/integration/fixtures/host_mode_entity_fields.yaml
Normal file
@ -0,0 +1,108 @@
|
||||
esphome:
|
||||
name: host-test
|
||||
|
||||
host:
|
||||
|
||||
api:
|
||||
|
||||
logger:
|
||||
|
||||
# Test various entity types with different flag combinations
|
||||
sensor:
|
||||
- platform: template
|
||||
name: "Test Normal Sensor"
|
||||
id: normal_sensor
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
return 42.0;
|
||||
|
||||
- platform: template
|
||||
name: "Test Internal Sensor"
|
||||
id: internal_sensor
|
||||
internal: true
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
return 43.0;
|
||||
|
||||
- platform: template
|
||||
name: "Test Disabled Sensor"
|
||||
id: disabled_sensor
|
||||
disabled_by_default: true
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
return 44.0;
|
||||
|
||||
- platform: template
|
||||
name: "Test Mixed Flags Sensor"
|
||||
id: mixed_flags_sensor
|
||||
internal: true
|
||||
entity_category: diagnostic
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
return 45.0;
|
||||
|
||||
- platform: template
|
||||
name: "Test Diagnostic Sensor"
|
||||
id: diagnostic_sensor
|
||||
entity_category: diagnostic
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
return 46.0;
|
||||
|
||||
- platform: template
|
||||
name: "Test All Flags Sensor"
|
||||
id: all_flags_sensor
|
||||
internal: true
|
||||
disabled_by_default: true
|
||||
entity_category: diagnostic
|
||||
update_interval: 1s
|
||||
lambda: |-
|
||||
return 47.0;
|
||||
|
||||
# Also test other entity types to ensure bit-packing works across all
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: "Test Binary Sensor"
|
||||
entity_category: config
|
||||
lambda: |-
|
||||
return true;
|
||||
|
||||
text_sensor:
|
||||
- platform: template
|
||||
name: "Test Text Sensor"
|
||||
disabled_by_default: true
|
||||
lambda: |-
|
||||
return {"Hello"};
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
name: "Test Number"
|
||||
initial_value: 50
|
||||
min_value: 0
|
||||
max_value: 100
|
||||
step: 1
|
||||
optimistic: true
|
||||
entity_category: diagnostic
|
||||
|
||||
select:
|
||||
- platform: template
|
||||
name: "Test Select"
|
||||
options:
|
||||
- "Option 1"
|
||||
- "Option 2"
|
||||
initial_option: "Option 1"
|
||||
optimistic: true
|
||||
internal: true
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Test Switch"
|
||||
optimistic: true
|
||||
disabled_by_default: true
|
||||
entity_category: config
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: "Test Button"
|
||||
on_press:
|
||||
- logger.log: "Button pressed"
|
93
tests/integration/test_host_mode_entity_fields.py
Normal file
93
tests/integration/test_host_mode_entity_fields.py
Normal file
@ -0,0 +1,93 @@
|
||||
"""Integration test for entity bit-packed fields."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
from aioesphomeapi import EntityCategory, EntityState
|
||||
import pytest
|
||||
|
||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_host_mode_entity_fields(
|
||||
yaml_config: str,
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected: APIClientConnectedFactory,
|
||||
) -> None:
|
||||
"""Test entity bit-packed fields work correctly with all possible values."""
|
||||
# Write, compile and run the ESPHome device, then connect to API
|
||||
async with run_compiled(yaml_config), api_client_connected() as client:
|
||||
# Get all entities
|
||||
entities = await client.list_entities_services()
|
||||
|
||||
# Create a map of entity names to entity info
|
||||
entity_map = {}
|
||||
for entity in entities[0]:
|
||||
if hasattr(entity, "name"):
|
||||
entity_map[entity.name] = entity
|
||||
|
||||
# Test entities that should be visible via API (non-internal)
|
||||
visible_test_cases = [
|
||||
# (entity_name, expected_disabled_by_default, expected_entity_category)
|
||||
("Test Normal Sensor", False, EntityCategory.NONE),
|
||||
("Test Disabled Sensor", True, EntityCategory.NONE),
|
||||
("Test Diagnostic Sensor", False, EntityCategory.DIAGNOSTIC),
|
||||
("Test Switch", True, EntityCategory.CONFIG),
|
||||
("Test Binary Sensor", False, EntityCategory.CONFIG),
|
||||
("Test Number", False, EntityCategory.DIAGNOSTIC),
|
||||
]
|
||||
|
||||
# Test entities that should NOT be visible via API (internal)
|
||||
internal_entities = [
|
||||
"Test Internal Sensor",
|
||||
"Test Mixed Flags Sensor",
|
||||
"Test All Flags Sensor",
|
||||
"Test Select",
|
||||
]
|
||||
|
||||
# Verify visible entities
|
||||
for entity_name, expected_disabled, expected_category in visible_test_cases:
|
||||
assert entity_name in entity_map, (
|
||||
f"Entity '{entity_name}' not found - it should be visible via API"
|
||||
)
|
||||
entity = entity_map[entity_name]
|
||||
|
||||
# Check disabled_by_default flag
|
||||
assert entity.disabled_by_default == expected_disabled, (
|
||||
f"{entity_name}: disabled_by_default flag mismatch - "
|
||||
f"expected {expected_disabled}, got {entity.disabled_by_default}"
|
||||
)
|
||||
|
||||
# Check entity_category
|
||||
assert entity.entity_category == expected_category, (
|
||||
f"{entity_name}: entity_category mismatch - "
|
||||
f"expected {expected_category}, got {entity.entity_category}"
|
||||
)
|
||||
|
||||
# Verify internal entities are NOT visible
|
||||
for entity_name in internal_entities:
|
||||
assert entity_name not in entity_map, (
|
||||
f"Entity '{entity_name}' found in API response - "
|
||||
f"internal entities should not be exposed via API"
|
||||
)
|
||||
|
||||
# Subscribe to states to verify has_state flag works
|
||||
states: dict[int, EntityState] = {}
|
||||
state_received = asyncio.Event()
|
||||
|
||||
def on_state(state: EntityState) -> None:
|
||||
states[state.key] = state
|
||||
state_received.set()
|
||||
|
||||
client.subscribe_states(on_state)
|
||||
|
||||
# Wait for at least one state
|
||||
try:
|
||||
await asyncio.wait_for(state_received.wait(), timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
pytest.fail("No states received within 5 seconds")
|
||||
|
||||
# Verify we received states (which means has_state flag is working)
|
||||
assert len(states) > 0, "No states received - has_state flag may not be working"
|
Loading…
x
Reference in New Issue
Block a user