mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 23:37:34 +00:00
Merge branch 'dev' into esp8266-crash-handler
This commit is contained in:
@@ -16,7 +16,7 @@ void log_button(const char *tag, const char *prefix, const char *type, Button *o
|
||||
}
|
||||
|
||||
void Button::press() {
|
||||
ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
|
||||
ESP_LOGV(TAG, "'%s' Pressed.", this->get_name().c_str());
|
||||
this->press_action();
|
||||
this->press_callback_.call();
|
||||
}
|
||||
|
||||
@@ -399,8 +399,17 @@ void ESP32BLE::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32_BLE_ADVERTISING
|
||||
if (this->advertising_ != nullptr) {
|
||||
this->advertising_->loop();
|
||||
}
|
||||
#endif
|
||||
|
||||
BLEEvent *ble_event = this->ble_events_.pop();
|
||||
while (ble_event != nullptr) {
|
||||
if (ble_event == nullptr)
|
||||
return;
|
||||
|
||||
do {
|
||||
switch (ble_event->type_) {
|
||||
#if defined(USE_ESP32_BLE_SERVER) && defined(ESPHOME_ESP32_BLE_GATTS_EVENT_HANDLER_COUNT)
|
||||
case BLEEvent::GATTS: {
|
||||
@@ -488,15 +497,11 @@ void ESP32BLE::loop() {
|
||||
}
|
||||
// Return the event to the pool
|
||||
this->ble_event_pool_.release(ble_event);
|
||||
ble_event = this->ble_events_.pop();
|
||||
}
|
||||
#ifdef USE_ESP32_BLE_ADVERTISING
|
||||
if (this->advertising_ != nullptr) {
|
||||
this->advertising_->loop();
|
||||
}
|
||||
#endif
|
||||
} while ((ble_event = this->ble_events_.pop()) != nullptr);
|
||||
|
||||
// Log dropped events periodically
|
||||
// Log dropped events - only reachable when events were processed.
|
||||
// Drops only occur when the queue is full, and only this loop drains it,
|
||||
// so if pop() returned nullptr above we can skip this check (saves a memw).
|
||||
uint16_t dropped = this->ble_events_.get_and_reset_dropped_count();
|
||||
if (dropped > 0) {
|
||||
ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped);
|
||||
|
||||
@@ -392,6 +392,9 @@ async def to_code(configs):
|
||||
} & styles_used:
|
||||
df.add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
||||
|
||||
if configs[0].get(df.CONF_THEME, {}).get(df.CONF_DARK_MODE):
|
||||
df.add_define("LV_THEME_DEFAULT_DARK", "1")
|
||||
|
||||
# Currently always need RGB565 for the display buffer, and ARGB8888 is used for layer blending
|
||||
lv_image_formats = {"RGB565", "ARGB8888"}
|
||||
if {
|
||||
@@ -459,8 +462,11 @@ def add_hello_world(config):
|
||||
def _theme_schema(value):
|
||||
return cv.Schema(
|
||||
{
|
||||
cv.Optional(name): obj_schema(w).extend(FULL_STYLE_SCHEMA)
|
||||
for name, w in WIDGET_TYPES.items()
|
||||
cv.Optional(df.CONF_DARK_MODE, default=False): cv.boolean,
|
||||
**{
|
||||
cv.Optional(name): obj_schema(w).extend(FULL_STYLE_SCHEMA)
|
||||
for name, w in WIDGET_TYPES.items()
|
||||
},
|
||||
}
|
||||
)(value)
|
||||
|
||||
|
||||
@@ -598,6 +598,7 @@ CONF_FLEX_ALIGN_CROSS = "flex_align_cross"
|
||||
CONF_FLEX_ALIGN_TRACK = "flex_align_track"
|
||||
CONF_FLEX_GROW = "flex_grow"
|
||||
CONF_FREEZE = "freeze"
|
||||
CONF_DARK_MODE = "dark_mode"
|
||||
CONF_FULL_REFRESH = "full_refresh"
|
||||
CONF_GRADIENTS = "gradients"
|
||||
CONF_GRID_CELL_ROW_POS = "grid_cell_row_pos"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- obj:
|
||||
id: hello_world_card_
|
||||
pad_all: 12
|
||||
bg_color: white
|
||||
bg_opa: cover
|
||||
height: 100%
|
||||
width: 100%
|
||||
scrollable: false
|
||||
|
||||
@@ -7,7 +7,7 @@ from esphome.core import ID
|
||||
from .defines import CONF_STYLE_DEFINITIONS, CONF_THEME, LValidator, literal
|
||||
from .helpers import add_lv_use
|
||||
from .lvcode import LambdaContext, lv
|
||||
from .schemas import ALL_STYLES, FULL_STYLE_SCHEMA, remap_property
|
||||
from .schemas import ALL_STYLES, FULL_STYLE_SCHEMA, WIDGET_TYPES, remap_property
|
||||
from .types import ObjUpdateAction, lv_style_t
|
||||
from .widgets import collect_parts, theme_widget_map, wait_for_widgets
|
||||
|
||||
@@ -85,7 +85,7 @@ async def style_update_to_code(config, action_id, template_arg, args):
|
||||
async def theme_to_code(config):
|
||||
if theme := config.get(CONF_THEME):
|
||||
add_lv_use(CONF_THEME)
|
||||
for w_name, style in theme.items():
|
||||
for w_name, style in ((k, v) for k, v in theme.items() if k in WIDGET_TYPES):
|
||||
# Work around Python 3.10 bug with nested async comprehensions
|
||||
# With Python 3.11 this could be simplified
|
||||
# TODO: Now that we require Python 3.11+, this can be updated to use nested comprehensions
|
||||
|
||||
@@ -37,6 +37,11 @@ void MCP23X08Base::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
if (this->interrupt_pin_ != nullptr && (flags & gpio::FLAG_INPUT)) {
|
||||
this->pin_interrupt_mode(pin, mcp23xxx_base::MCP23XXX_CHANGE);
|
||||
}
|
||||
// Enable polling loop for input pins (not needed for interrupt-driven mode
|
||||
// where the ISR handles re-enabling loop)
|
||||
if (this->interrupt_pin_ == nullptr && (flags & gpio::FLAG_INPUT)) {
|
||||
this->enable_loop();
|
||||
}
|
||||
}
|
||||
|
||||
void MCP23X08Base::pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) {
|
||||
|
||||
@@ -49,6 +49,11 @@ void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
if (this->interrupt_pin_ != nullptr && (flags & gpio::FLAG_INPUT)) {
|
||||
this->pin_interrupt_mode(pin, mcp23xxx_base::MCP23XXX_CHANGE);
|
||||
}
|
||||
// Enable polling loop for input pins (not needed for interrupt-driven mode
|
||||
// where the ISR handles re-enabling loop)
|
||||
if (this->interrupt_pin_ == nullptr && (flags & gpio::FLAG_INPUT)) {
|
||||
this->enable_loop();
|
||||
}
|
||||
}
|
||||
|
||||
void MCP23X17Base::pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) {
|
||||
|
||||
@@ -35,8 +35,11 @@ template<uint8_t N> class MCP23XXXBase : public Component, public gpio_expander:
|
||||
this->interrupt_pin_->setup();
|
||||
this->interrupt_pin_->attach_interrupt(&MCP23XXXBase::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
|
||||
this->set_invalidate_on_read_(false);
|
||||
this->disable_loop();
|
||||
}
|
||||
// Disable loop until an input pin is configured via pin_mode()
|
||||
// For interrupt-driven mode, loop is re-enabled by the ISR
|
||||
// For polling mode, loop is re-enabled when pin_mode() registers an input pin
|
||||
this->disable_loop();
|
||||
}
|
||||
static void IRAM_ATTR gpio_intr(MCP23XXXBase *arg) { arg->enable_loop_soon_any_context(); }
|
||||
|
||||
|
||||
@@ -41,8 +41,11 @@ void PI4IOE5V6408Component::setup() {
|
||||
this->interrupt_pin_->setup();
|
||||
this->interrupt_pin_->attach_interrupt(&PI4IOE5V6408Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
|
||||
this->set_invalidate_on_read_(false);
|
||||
this->disable_loop();
|
||||
}
|
||||
// Disable loop until an input pin is configured via pin_mode()
|
||||
// For interrupt-driven mode, loop is re-enabled by the ISR
|
||||
// For polling mode, loop is re-enabled when pin_mode() registers an input pin
|
||||
this->disable_loop();
|
||||
}
|
||||
void IRAM_ATTR PI4IOE5V6408Component::gpio_intr(PI4IOE5V6408Component *arg) { arg->enable_loop_soon_any_context(); }
|
||||
void PI4IOE5V6408Component::dump_config() {
|
||||
@@ -67,6 +70,11 @@ void PI4IOE5V6408Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
this->pull_up_down_mask_ &= ~(1 << pin);
|
||||
this->pull_enable_mask_ |= 1 << pin;
|
||||
}
|
||||
// Enable polling loop for input pins (not needed for interrupt-driven mode
|
||||
// where the ISR handles re-enabling loop)
|
||||
if (this->interrupt_pin_ == nullptr) {
|
||||
this->enable_loop();
|
||||
}
|
||||
}
|
||||
// Write GPIO to enable input mode
|
||||
this->write_gpio_modes_();
|
||||
|
||||
@@ -116,7 +116,7 @@ void SelectCall::perform() {
|
||||
|
||||
auto idx = target_index.value();
|
||||
// All operations use indices, call control() by index to avoid string conversion
|
||||
ESP_LOGD(TAG, "'%s' - Set selected option to: %s", name, parent->option_at(idx));
|
||||
ESP_LOGV(TAG, "'%s' - Set selected option to: %s", name, parent->option_at(idx));
|
||||
parent->control(idx);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,15 @@ void Switch::control(bool target_state) {
|
||||
}
|
||||
}
|
||||
void Switch::turn_on() {
|
||||
ESP_LOGD(TAG, "'%s' Turning ON.", this->get_name().c_str());
|
||||
ESP_LOGV(TAG, "'%s' Turning ON.", this->get_name().c_str());
|
||||
this->write_state(!this->inverted_);
|
||||
}
|
||||
void Switch::turn_off() {
|
||||
ESP_LOGD(TAG, "'%s' Turning OFF.", this->get_name().c_str());
|
||||
ESP_LOGV(TAG, "'%s' Turning OFF.", this->get_name().c_str());
|
||||
this->write_state(this->inverted_);
|
||||
}
|
||||
void Switch::toggle() {
|
||||
ESP_LOGD(TAG, "'%s' Toggling %s.", this->get_name().c_str(), this->state ? "OFF" : "ON");
|
||||
ESP_LOGV(TAG, "'%s' Toggling %s.", this->get_name().c_str(), this->state ? "OFF" : "ON");
|
||||
this->write_state(this->inverted_ == this->state);
|
||||
}
|
||||
optional<bool> Switch::get_initial_state() {
|
||||
|
||||
+1
-3
@@ -15,8 +15,6 @@ from typing import Any
|
||||
|
||||
import colorama
|
||||
|
||||
from esphome.loader import get_platform
|
||||
|
||||
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
|
||||
basepath = os.path.join(root_path, "esphome")
|
||||
temp_folder = os.path.join(root_path, ".temp")
|
||||
@@ -644,7 +642,7 @@ def get_all_dependencies(
|
||||
PLATFORM_HOST,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.loader import get_component
|
||||
from esphome.loader import get_component, get_platform
|
||||
|
||||
all_components: set[str] = set(component_names)
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
#include "esphome/components/api/api_buffer.h"
|
||||
#include "esphome/components/light/color_mode.h"
|
||||
|
||||
namespace esphome::api::benchmarks {
|
||||
|
||||
static constexpr int kInnerIterations = 2000;
|
||||
|
||||
// --- ListEntitiesSensorResponse ---
|
||||
|
||||
static ListEntitiesSensorResponse make_sensor_response() {
|
||||
ListEntitiesSensorResponse msg;
|
||||
msg.object_id = StringRef::from_lit("living_room_temperature");
|
||||
msg.key = 0x12345678;
|
||||
msg.name = StringRef::from_lit("Living Room Temperature");
|
||||
#ifdef USE_ENTITY_ICON
|
||||
msg.icon = StringRef::from_lit("mdi:thermometer");
|
||||
#endif
|
||||
msg.entity_category = enums::ENTITY_CATEGORY_NONE;
|
||||
msg.disabled_by_default = false;
|
||||
msg.unit_of_measurement = StringRef::from_lit("°C");
|
||||
msg.accuracy_decimals = 1;
|
||||
msg.force_update = false;
|
||||
msg.device_class = StringRef::from_lit("temperature");
|
||||
msg.state_class = enums::STATE_CLASS_MEASUREMENT;
|
||||
#ifdef USE_DEVICES
|
||||
msg.device_id = 1;
|
||||
#endif
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void CalculateSize_ListEntitiesSensorResponse(benchmark::State &state) {
|
||||
auto msg = make_sensor_response();
|
||||
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result += msg.calculate_size();
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CalculateSize_ListEntitiesSensorResponse);
|
||||
|
||||
static void Encode_ListEntitiesSensorResponse(benchmark::State &state) {
|
||||
auto msg = make_sensor_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Encode_ListEntitiesSensorResponse);
|
||||
|
||||
static void CalcAndEncode_ListEntitiesSensorResponse(benchmark::State &state) {
|
||||
auto msg = make_sensor_response();
|
||||
APIBuffer buffer;
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CalcAndEncode_ListEntitiesSensorResponse);
|
||||
|
||||
// --- ListEntitiesBinarySensorResponse ---
|
||||
|
||||
static ListEntitiesBinarySensorResponse make_binary_sensor_response() {
|
||||
ListEntitiesBinarySensorResponse msg;
|
||||
msg.object_id = StringRef::from_lit("front_door_contact");
|
||||
msg.key = 0xAABBCCDD;
|
||||
msg.name = StringRef::from_lit("Front Door Contact");
|
||||
#ifdef USE_ENTITY_ICON
|
||||
msg.icon = StringRef::from_lit("mdi:door");
|
||||
#endif
|
||||
msg.entity_category = enums::ENTITY_CATEGORY_NONE;
|
||||
msg.disabled_by_default = false;
|
||||
msg.device_class = StringRef::from_lit("door");
|
||||
msg.is_status_binary_sensor = false;
|
||||
#ifdef USE_DEVICES
|
||||
msg.device_id = 2;
|
||||
#endif
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void CalculateSize_ListEntitiesBinarySensorResponse(benchmark::State &state) {
|
||||
auto msg = make_binary_sensor_response();
|
||||
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result += msg.calculate_size();
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CalculateSize_ListEntitiesBinarySensorResponse);
|
||||
|
||||
static void Encode_ListEntitiesBinarySensorResponse(benchmark::State &state) {
|
||||
auto msg = make_binary_sensor_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Encode_ListEntitiesBinarySensorResponse);
|
||||
|
||||
static void CalcAndEncode_ListEntitiesBinarySensorResponse(benchmark::State &state) {
|
||||
auto msg = make_binary_sensor_response();
|
||||
APIBuffer buffer;
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CalcAndEncode_ListEntitiesBinarySensorResponse);
|
||||
|
||||
// --- ListEntitiesLightResponse ---
|
||||
|
||||
static light::ColorModeMask light_color_modes;
|
||||
static FixedVector<const char *> light_effects;
|
||||
|
||||
static ListEntitiesLightResponse make_light_response() {
|
||||
// Initialize static data on first call
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
light_color_modes.insert(light::ColorMode::RGB_WHITE);
|
||||
light_color_modes.insert(light::ColorMode::COLOR_TEMPERATURE);
|
||||
light_effects.init(3);
|
||||
light_effects.push_back("None");
|
||||
light_effects.push_back("Rainbow");
|
||||
light_effects.push_back("Strobe");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
ListEntitiesLightResponse msg;
|
||||
msg.object_id = StringRef::from_lit("kitchen_ceiling_light");
|
||||
msg.key = 0x55667788;
|
||||
msg.name = StringRef::from_lit("Kitchen Ceiling Light");
|
||||
#ifdef USE_ENTITY_ICON
|
||||
msg.icon = StringRef::from_lit("mdi:ceiling-light");
|
||||
#endif
|
||||
msg.entity_category = enums::ENTITY_CATEGORY_NONE;
|
||||
msg.disabled_by_default = false;
|
||||
msg.supported_color_modes = &light_color_modes;
|
||||
msg.min_mireds = 153.0f;
|
||||
msg.max_mireds = 500.0f;
|
||||
msg.effects = &light_effects;
|
||||
#ifdef USE_DEVICES
|
||||
msg.device_id = 3;
|
||||
#endif
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void CalculateSize_ListEntitiesLightResponse(benchmark::State &state) {
|
||||
auto msg = make_light_response();
|
||||
|
||||
for (auto _ : state) {
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
result += msg.calculate_size();
|
||||
}
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CalculateSize_ListEntitiesLightResponse);
|
||||
|
||||
static void Encode_ListEntitiesLightResponse(benchmark::State &state) {
|
||||
auto msg = make_light_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(Encode_ListEntitiesLightResponse);
|
||||
|
||||
static void CalcAndEncode_ListEntitiesLightResponse(benchmark::State &state) {
|
||||
auto msg = make_light_response();
|
||||
APIBuffer buffer;
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
}
|
||||
state.SetItemsProcessed(state.iterations() * kInnerIterations);
|
||||
}
|
||||
BENCHMARK(CalcAndEncode_ListEntitiesLightResponse);
|
||||
|
||||
} // namespace esphome::api::benchmarks
|
||||
@@ -49,6 +49,7 @@ lvgl:
|
||||
bg_color: 0x000000
|
||||
bg_opa: cover
|
||||
theme:
|
||||
dark_mode: true
|
||||
obj:
|
||||
border_width: 1
|
||||
|
||||
|
||||
@@ -1037,7 +1037,7 @@ def test_get_all_dependencies_platform_component() -> None:
|
||||
|
||||
with (
|
||||
patch("esphome.loader.get_component") as mock_get_component,
|
||||
patch("helpers.get_platform") as mock_get_platform,
|
||||
patch("esphome.loader.get_platform") as mock_get_platform,
|
||||
):
|
||||
mock_get_platform.return_value = platform_comp
|
||||
mock_get_component.return_value = None
|
||||
@@ -1061,7 +1061,7 @@ def test_get_all_dependencies_platform_component_with_dependencies() -> None:
|
||||
|
||||
with (
|
||||
patch("esphome.loader.get_component") as mock_get_component,
|
||||
patch("helpers.get_platform") as mock_get_platform,
|
||||
patch("esphome.loader.get_platform") as mock_get_platform,
|
||||
):
|
||||
mock_get_platform.return_value = platform_comp
|
||||
mock_get_component.side_effect = lambda name: (
|
||||
|
||||
Reference in New Issue
Block a user