mirror of
https://github.com/esphome/esphome.git
synced 2025-11-19 16:25:50 +00:00
Merge branch 'integration' into memory_api
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
from esphome import automation
|
from esphome import automation
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE
|
from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE
|
||||||
|
from esphome.cpp_generator import MockObj
|
||||||
|
|
||||||
from ..automation import action_to_code
|
from ..automation import action_to_code
|
||||||
from ..defines import (
|
from ..defines import (
|
||||||
@@ -114,7 +115,9 @@ class SpinboxType(WidgetType):
|
|||||||
w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
|
w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
|
||||||
)
|
)
|
||||||
if (value := config.get(CONF_VALUE)) is not None:
|
if (value := config.get(CONF_VALUE)) is not None:
|
||||||
lv.spinbox_set_value(w.obj, await lv_float.process(value))
|
lv.spinbox_set_value(
|
||||||
|
w.obj, MockObj(await lv_float.process(value)) * w.get_scale()
|
||||||
|
)
|
||||||
|
|
||||||
def get_scale(self, config):
|
def get_scale(self, config):
|
||||||
return 10 ** config[CONF_DECIMAL_PLACES]
|
return 10 ** config[CONF_DECIMAL_PLACES]
|
||||||
|
|||||||
@@ -350,6 +350,7 @@ void MipiRgb::dump_config() {
|
|||||||
"\n Width: %u"
|
"\n Width: %u"
|
||||||
"\n Height: %u"
|
"\n Height: %u"
|
||||||
"\n Rotation: %d degrees"
|
"\n Rotation: %d degrees"
|
||||||
|
"\n PCLK Inverted: %s"
|
||||||
"\n HSync Pulse Width: %u"
|
"\n HSync Pulse Width: %u"
|
||||||
"\n HSync Back Porch: %u"
|
"\n HSync Back Porch: %u"
|
||||||
"\n HSync Front Porch: %u"
|
"\n HSync Front Porch: %u"
|
||||||
@@ -357,18 +358,18 @@ void MipiRgb::dump_config() {
|
|||||||
"\n VSync Back Porch: %u"
|
"\n VSync Back Porch: %u"
|
||||||
"\n VSync Front Porch: %u"
|
"\n VSync Front Porch: %u"
|
||||||
"\n Invert Colors: %s"
|
"\n Invert Colors: %s"
|
||||||
"\n Pixel Clock: %dMHz"
|
"\n Pixel Clock: %uMHz"
|
||||||
"\n Reset Pin: %s"
|
"\n Reset Pin: %s"
|
||||||
"\n DE Pin: %s"
|
"\n DE Pin: %s"
|
||||||
"\n PCLK Pin: %s"
|
"\n PCLK Pin: %s"
|
||||||
"\n HSYNC Pin: %s"
|
"\n HSYNC Pin: %s"
|
||||||
"\n VSYNC Pin: %s",
|
"\n VSYNC Pin: %s",
|
||||||
this->model_, this->width_, this->height_, this->rotation_, this->hsync_pulse_width_,
|
this->model_, this->width_, this->height_, this->rotation_, YESNO(this->pclk_inverted_),
|
||||||
this->hsync_back_porch_, this->hsync_front_porch_, this->vsync_pulse_width_, this->vsync_back_porch_,
|
this->hsync_pulse_width_, this->hsync_back_porch_, this->hsync_front_porch_, this->vsync_pulse_width_,
|
||||||
this->vsync_front_porch_, YESNO(this->invert_colors_), this->pclk_frequency_ / 1000000,
|
this->vsync_back_porch_, this->vsync_front_porch_, YESNO(this->invert_colors_),
|
||||||
get_pin_name(this->reset_pin_).c_str(), get_pin_name(this->de_pin_).c_str(),
|
(unsigned) (this->pclk_frequency_ / 1000000), get_pin_name(this->reset_pin_).c_str(),
|
||||||
get_pin_name(this->pclk_pin_).c_str(), get_pin_name(this->hsync_pin_).c_str(),
|
get_pin_name(this->de_pin_).c_str(), get_pin_name(this->pclk_pin_).c_str(),
|
||||||
get_pin_name(this->vsync_pin_).c_str());
|
get_pin_name(this->hsync_pin_).c_str(), get_pin_name(this->vsync_pin_).c_str());
|
||||||
|
|
||||||
if (this->madctl_ & MADCTL_BGR) {
|
if (this->madctl_ & MADCTL_BGR) {
|
||||||
this->dump_pins_(8, 13, "Blue", 0);
|
this->dump_pins_(8, 13, "Blue", 0);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ st7701s.extend(
|
|||||||
vsync_pin=17,
|
vsync_pin=17,
|
||||||
pclk_pin=21,
|
pclk_pin=21,
|
||||||
pclk_frequency="12MHz",
|
pclk_frequency="12MHz",
|
||||||
|
pclk_inverted=False,
|
||||||
pixel_mode="18bit",
|
pixel_mode="18bit",
|
||||||
mirror_x=True,
|
mirror_x=True,
|
||||||
mirror_y=True,
|
mirror_y=True,
|
||||||
|
|||||||
@@ -485,11 +485,14 @@ async def to_code(config):
|
|||||||
cg.add(var.set_min_auth_mode(config[CONF_MIN_AUTH_MODE]))
|
cg.add(var.set_min_auth_mode(config[CONF_MIN_AUTH_MODE]))
|
||||||
if config[CONF_FAST_CONNECT]:
|
if config[CONF_FAST_CONNECT]:
|
||||||
cg.add_define("USE_WIFI_FAST_CONNECT")
|
cg.add_define("USE_WIFI_FAST_CONNECT")
|
||||||
cg.add(var.set_passive_scan(config[CONF_PASSIVE_SCAN]))
|
# passive_scan defaults to false in C++ - only set if true
|
||||||
|
if config[CONF_PASSIVE_SCAN]:
|
||||||
|
cg.add(var.set_passive_scan(True))
|
||||||
if CONF_OUTPUT_POWER in config:
|
if CONF_OUTPUT_POWER in config:
|
||||||
cg.add(var.set_output_power(config[CONF_OUTPUT_POWER]))
|
cg.add(var.set_output_power(config[CONF_OUTPUT_POWER]))
|
||||||
|
# enable_on_boot defaults to true in C++ - only set if false
|
||||||
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
|
if not config[CONF_ENABLE_ON_BOOT]:
|
||||||
|
cg.add(var.set_enable_on_boot(False))
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
if CORE.is_esp8266:
|
||||||
cg.add_library("ESP8266WiFi", None)
|
cg.add_library("ESP8266WiFi", None)
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ class WiFiComponent : public Component {
|
|||||||
bool btm_{false};
|
bool btm_{false};
|
||||||
bool rrm_{false};
|
bool rrm_{false};
|
||||||
#endif
|
#endif
|
||||||
bool enable_on_boot_;
|
bool enable_on_boot_{true};
|
||||||
bool got_ipv4_address_{false};
|
bool got_ipv4_address_{false};
|
||||||
bool keep_scan_results_{false};
|
bool keep_scan_results_{false};
|
||||||
bool did_scan_this_cycle_{false};
|
bool did_scan_this_cycle_{false};
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|||||||
|
|
||||||
// For retries, check if there's a cancelled timeout first
|
// For retries, check if there's a cancelled timeout first
|
||||||
if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
|
if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
|
||||||
(has_cancelled_timeout_in_container_(this->items_, component, name_cstr, /* match_retry= */ true) ||
|
(has_cancelled_timeout_in_container_locked_(this->items_, component, name_cstr, /* match_retry= */ true) ||
|
||||||
has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr, /* match_retry= */ true))) {
|
has_cancelled_timeout_in_container_locked_(this->to_add_, component, name_cstr, /* match_retry= */ true))) {
|
||||||
// Skip scheduling - the retry was cancelled
|
// Skip scheduling - the retry was cancelled
|
||||||
#ifdef ESPHOME_DEBUG_SCHEDULER
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
||||||
ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
|
ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
|
||||||
@@ -556,7 +556,8 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
#ifndef ESPHOME_THREAD_SINGLE
|
#ifndef ESPHOME_THREAD_SINGLE
|
||||||
// Mark items in defer queue as cancelled (they'll be skipped when processed)
|
// Mark items in defer queue as cancelled (they'll be skipped when processed)
|
||||||
if (type == SchedulerItem::TIMEOUT) {
|
if (type == SchedulerItem::TIMEOUT) {
|
||||||
total_cancelled += this->mark_matching_items_removed_(this->defer_queue_, component, name_cstr, type, match_retry);
|
total_cancelled +=
|
||||||
|
this->mark_matching_items_removed_locked_(this->defer_queue_, component, name_cstr, type, match_retry);
|
||||||
}
|
}
|
||||||
#endif /* not ESPHOME_THREAD_SINGLE */
|
#endif /* not ESPHOME_THREAD_SINGLE */
|
||||||
|
|
||||||
@@ -565,19 +566,20 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|||||||
// (removing the last element doesn't break heap structure)
|
// (removing the last element doesn't break heap structure)
|
||||||
if (!this->items_.empty()) {
|
if (!this->items_.empty()) {
|
||||||
auto &last_item = this->items_.back();
|
auto &last_item = this->items_.back();
|
||||||
if (this->matches_item_(last_item, component, name_cstr, type, match_retry)) {
|
if (this->matches_item_locked_(last_item, component, name_cstr, type, match_retry)) {
|
||||||
this->recycle_item_(std::move(this->items_.back()));
|
this->recycle_item_(std::move(this->items_.back()));
|
||||||
this->items_.pop_back();
|
this->items_.pop_back();
|
||||||
total_cancelled++;
|
total_cancelled++;
|
||||||
}
|
}
|
||||||
// For other items in heap, we can only mark for removal (can't remove from middle of heap)
|
// For other items in heap, we can only mark for removal (can't remove from middle of heap)
|
||||||
size_t heap_cancelled = this->mark_matching_items_removed_(this->items_, component, name_cstr, type, match_retry);
|
size_t heap_cancelled =
|
||||||
|
this->mark_matching_items_removed_locked_(this->items_, component, name_cstr, type, match_retry);
|
||||||
total_cancelled += heap_cancelled;
|
total_cancelled += heap_cancelled;
|
||||||
this->to_remove_ += heap_cancelled; // Track removals for heap items
|
this->to_remove_ += heap_cancelled; // Track removals for heap items
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel items in to_add_
|
// Cancel items in to_add_
|
||||||
total_cancelled += this->mark_matching_items_removed_(this->to_add_, component, name_cstr, type, match_retry);
|
total_cancelled += this->mark_matching_items_removed_locked_(this->to_add_, component, name_cstr, type, match_retry);
|
||||||
|
|
||||||
return total_cancelled > 0;
|
return total_cancelled > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,8 +242,18 @@ class Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to check if item matches criteria for cancellation
|
// Helper function to check if item matches criteria for cancellation
|
||||||
inline bool HOT matches_item_(const std::unique_ptr<SchedulerItem> &item, Component *component, const char *name_cstr,
|
// IMPORTANT: Must be called with scheduler lock held
|
||||||
SchedulerItem::Type type, bool match_retry, bool skip_removed = true) const {
|
inline bool HOT matches_item_locked_(const std::unique_ptr<SchedulerItem> &item, Component *component,
|
||||||
|
const char *name_cstr, SchedulerItem::Type type, bool match_retry,
|
||||||
|
bool skip_removed = true) const {
|
||||||
|
// THREAD SAFETY: Check for nullptr first to prevent LoadProhibited crashes. On multi-threaded
|
||||||
|
// platforms, items can be moved out of defer_queue_ during processing, leaving nullptr entries.
|
||||||
|
// PR #11305 added nullptr checks in callers (mark_matching_items_removed_locked_() and
|
||||||
|
// has_cancelled_timeout_in_container_locked_()), but this check provides defense-in-depth: helper
|
||||||
|
// functions should be safe regardless of caller behavior.
|
||||||
|
// Fixes: https://github.com/esphome/esphome/issues/11940
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
if (item->component != component || item->type != type || (skip_removed && item->remove) ||
|
if (item->component != component || item->type != type || (skip_removed && item->remove) ||
|
||||||
(match_retry && !item->is_retry)) {
|
(match_retry && !item->is_retry)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -303,8 +313,8 @@ class Scheduler {
|
|||||||
// SAFETY: Moving out the unique_ptr leaves a nullptr in the vector at defer_queue_front_.
|
// SAFETY: Moving out the unique_ptr leaves a nullptr in the vector at defer_queue_front_.
|
||||||
// This is intentional and safe because:
|
// This is intentional and safe because:
|
||||||
// 1. The vector is only cleaned up by cleanup_defer_queue_locked_() at the end of this function
|
// 1. The vector is only cleaned up by cleanup_defer_queue_locked_() at the end of this function
|
||||||
// 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_
|
// 2. Any code iterating defer_queue_ MUST check for nullptr items (see mark_matching_items_removed_locked_
|
||||||
// and has_cancelled_timeout_in_container_ in scheduler.h)
|
// and has_cancelled_timeout_in_container_locked_ in scheduler.h)
|
||||||
// 3. The lock protects concurrent access, but the nullptr remains until cleanup
|
// 3. The lock protects concurrent access, but the nullptr remains until cleanup
|
||||||
item = std::move(this->defer_queue_[this->defer_queue_front_]);
|
item = std::move(this->defer_queue_[this->defer_queue_front_]);
|
||||||
this->defer_queue_front_++;
|
this->defer_queue_front_++;
|
||||||
@@ -392,9 +402,9 @@ class Scheduler {
|
|||||||
|
|
||||||
// Helper to mark matching items in a container as removed
|
// Helper to mark matching items in a container as removed
|
||||||
// Returns the number of items marked for removal
|
// Returns the number of items marked for removal
|
||||||
// IMPORTANT: Caller must hold the scheduler lock before calling this function.
|
// IMPORTANT: Must be called with scheduler lock held
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
size_t mark_matching_items_removed_(Container &container, Component *component, const char *name_cstr,
|
size_t mark_matching_items_removed_locked_(Container &container, Component *component, const char *name_cstr,
|
||||||
SchedulerItem::Type type, bool match_retry) {
|
SchedulerItem::Type type, bool match_retry) {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
for (auto &item : container) {
|
for (auto &item : container) {
|
||||||
@@ -404,7 +414,7 @@ class Scheduler {
|
|||||||
// the vector can still contain nullptr items from the processing loop. This check prevents crashes.
|
// the vector can still contain nullptr items from the processing loop. This check prevents crashes.
|
||||||
if (!item)
|
if (!item)
|
||||||
continue;
|
continue;
|
||||||
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
if (this->matches_item_locked_(item, component, name_cstr, type, match_retry)) {
|
||||||
// Mark item for removal (platform-specific)
|
// Mark item for removal (platform-specific)
|
||||||
this->set_item_removed_(item.get(), true);
|
this->set_item_removed_(item.get(), true);
|
||||||
count++;
|
count++;
|
||||||
@@ -414,9 +424,10 @@ class Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Template helper to check if any item in a container matches our criteria
|
// Template helper to check if any item in a container matches our criteria
|
||||||
|
// IMPORTANT: Must be called with scheduler lock held
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
bool has_cancelled_timeout_in_container_(const Container &container, Component *component, const char *name_cstr,
|
bool has_cancelled_timeout_in_container_locked_(const Container &container, Component *component,
|
||||||
bool match_retry) const {
|
const char *name_cstr, bool match_retry) const {
|
||||||
for (const auto &item : container) {
|
for (const auto &item : container) {
|
||||||
// Skip nullptr items (can happen in defer_queue_ when items are being processed)
|
// Skip nullptr items (can happen in defer_queue_ when items are being processed)
|
||||||
// The defer_queue_ uses index-based processing: items are std::moved out but left in the
|
// The defer_queue_ uses index-based processing: items are std::moved out but left in the
|
||||||
@@ -425,7 +436,7 @@ class Scheduler {
|
|||||||
if (!item)
|
if (!item)
|
||||||
continue;
|
continue;
|
||||||
if (is_item_removed_(item.get()) &&
|
if (is_item_removed_(item.get()) &&
|
||||||
this->matches_item_(item, component, name_cstr, SchedulerItem::TIMEOUT, match_retry,
|
this->matches_item_locked_(item, component, name_cstr, SchedulerItem::TIMEOUT, match_retry,
|
||||||
/* skip_removed= */ false)) {
|
/* skip_removed= */ false)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ def update_storage_json() -> None:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_LOGGER.info("Core config or version changed, cleaning build files...")
|
_LOGGER.info("Core config or version changed, cleaning build files...")
|
||||||
clean_build()
|
clean_build(clear_pio_cache=False)
|
||||||
elif storage_should_update_cmake_cache(old, new):
|
elif storage_should_update_cmake_cache(old, new):
|
||||||
_LOGGER.info("Integrations changed, cleaning cmake cache...")
|
_LOGGER.info("Integrations changed, cleaning cmake cache...")
|
||||||
clean_cmake_cache()
|
clean_cmake_cache()
|
||||||
@@ -301,7 +301,7 @@ def clean_cmake_cache():
|
|||||||
pioenvs_cmake_path.unlink()
|
pioenvs_cmake_path.unlink()
|
||||||
|
|
||||||
|
|
||||||
def clean_build():
|
def clean_build(clear_pio_cache: bool = True):
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
# Allow skipping cache cleaning for integration tests
|
# Allow skipping cache cleaning for integration tests
|
||||||
@@ -322,6 +322,9 @@ def clean_build():
|
|||||||
_LOGGER.info("Deleting %s", dependencies_lock)
|
_LOGGER.info("Deleting %s", dependencies_lock)
|
||||||
dependencies_lock.unlink()
|
dependencies_lock.unlink()
|
||||||
|
|
||||||
|
if not clear_pio_cache:
|
||||||
|
return
|
||||||
|
|
||||||
# Clean PlatformIO cache to resolve CMake compiler detection issues
|
# Clean PlatformIO cache to resolve CMake compiler detection issues
|
||||||
# This helps when toolchain paths change or get corrupted
|
# This helps when toolchain paths change or get corrupted
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -703,7 +703,9 @@ lvgl:
|
|||||||
on_value:
|
on_value:
|
||||||
- lvgl.spinbox.update:
|
- lvgl.spinbox.update:
|
||||||
id: spinbox_id
|
id: spinbox_id
|
||||||
value: !lambda return x;
|
value: !lambda |-
|
||||||
|
static float yyy = 83.0;
|
||||||
|
return yyy + .8;
|
||||||
- button:
|
- button:
|
||||||
styles: spin_button
|
styles: spin_button
|
||||||
id: spin_up
|
id: spin_up
|
||||||
|
|||||||
Reference in New Issue
Block a user