From 7edf45889806811c31295755300779b102110907 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:34:43 -0400 Subject: [PATCH 01/14] [esp32] Allow pioarduino version 5.3.3 and 5.5.0 (#8526) --- esphome/components/esp32/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 912a8bf94b..0ae4ab3464 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -274,8 +274,10 @@ SUPPORTED_PLATFORMIO_ESP_IDF_5X = [ # pioarduino versions that don't require a release number # List based on https://github.com/pioarduino/esp-idf/releases SUPPORTED_PIOARDUINO_ESP_IDF_5X = [ + cv.Version(5, 5, 0), cv.Version(5, 4, 1), cv.Version(5, 4, 0), + cv.Version(5, 3, 3), cv.Version(5, 3, 2), cv.Version(5, 3, 1), cv.Version(5, 3, 0), From ca78dd44b5382187739ad837fdc47fa195fa88a0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 12 Apr 2025 09:16:14 -1000 Subject: [PATCH 02/14] Include MAC address in noise hello (#8551) --- esphome/components/api/api_frame_helper.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 9e1b1521dd..3d6bc95163 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -311,6 +311,10 @@ APIError APINoiseFrameHelper::state_action_() { const std::string &name = App.get_name(); const uint8_t *name_ptr = reinterpret_cast(name.c_str()); msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1); + // node mac, terminated by null byte + const std::string &mac = get_mac_address(); + const uint8_t *mac_ptr = reinterpret_cast(mac.c_str()); + msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1); aerr = write_frame_(msg.data(), msg.size()); if (aerr != APIError::OK) From 264e234efceb772f38e78358a6cf2bc6e6845225 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Apr 2025 08:41:11 +1000 Subject: [PATCH 03/14] [axs15231] Don't overwrite manual dimensions (#8553) --- .../axs15231/touchscreen/axs15231_touchscreen.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp index 54b39a6bb9..c7646bcf2f 100644 --- a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +++ b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp @@ -30,8 +30,12 @@ void AXS15231Touchscreen::setup() { this->interrupt_pin_->setup(); this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); } - this->x_raw_max_ = this->display_->get_native_width(); - this->y_raw_max_ = this->display_->get_native_height(); + if (this->x_raw_max_ == 0) { + this->x_raw_max_ = this->display_->get_native_width(); + } + if (this->y_raw_max_ == 0) { + this->y_raw_max_ = this->display_->get_native_height(); + } ESP_LOGCONFIG(TAG, "AXS15231 Touchscreen setup complete"); } @@ -44,7 +48,7 @@ void AXS15231Touchscreen::update_touches() { err = this->read(data, sizeof(data)); ERROR_CHECK(err); this->status_clear_warning(); - if (data[0] != 0) // no touches + if (data[0] != 0 || data[1] == 0) // no touches return; uint16_t x = encode_uint16(data[2] & 0xF, data[3]); uint16_t y = encode_uint16(data[4] & 0xF, data[5]); From df4642208efefb0f60339fed735e8ac422b63990 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Apr 2025 08:41:42 +1000 Subject: [PATCH 04/14] [lvgl] Fix initial focus on roller (#8547) --- esphome/components/lvgl/encoders.py | 5 +++-- esphome/components/lvgl/schemas.py | 4 +++- tests/components/lvgl/test.esp32-idf.yaml | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/encoders.py b/esphome/components/lvgl/encoders.py index 952572df43..259c344030 100644 --- a/esphome/components/lvgl/encoders.py +++ b/esphome/components/lvgl/encoders.py @@ -18,6 +18,7 @@ from .helpers import lvgl_components_required, requires_component from .lvcode import lv, lv_add, lv_assign, lv_expr, lv_Pvariable from .schemas import ENCODER_SCHEMA from .types import lv_group_t, lv_indev_type_t, lv_key_t +from .widgets import get_widgets ENCODERS_CONFIG = cv.ensure_list( ENCODER_SCHEMA.extend( @@ -76,5 +77,5 @@ async def encoders_to_code(var, config, default_group): async def initial_focus_to_code(config): for enc_conf in config[CONF_ENCODERS]: if default_focus := enc_conf.get(CONF_INITIAL_FOCUS): - obj = await cg.get_variable(default_focus) - lv.group_focus_obj(obj) + widget = await get_widgets(default_focus) + lv.group_focus_obj(widget[0].obj) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index c05dfae8c7..051dbe5e0e 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -81,7 +81,9 @@ ENCODER_SCHEMA = cv.Schema( cv.declare_id(LVEncoderListener), requires_component("binary_sensor") ), cv.Optional(CONF_GROUP): cv.declare_id(lv_group_t), - cv.Optional(df.CONF_INITIAL_FOCUS): cv.use_id(lv_obj_t), + cv.Optional(df.CONF_INITIAL_FOCUS): cv.All( + LIST_ACTION_SCHEMA, cv.Length(min=1, max=1) + ), cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME, cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME, } diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index 05a1f243ed..eacace1d4b 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -71,5 +71,6 @@ lvgl: sensor: encoder enter_button: pushbutton group: general + initial_focus: lv_roller <<: !include common.yaml From fb5d697c2238edacf50f282b403bc255e6ff5b21 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Apr 2025 08:45:30 +1000 Subject: [PATCH 05/14] [lvgl] Add restore_value to select and number (#8494) --- esphome/components/lvgl/lvcode.py | 3 +- esphome/components/lvgl/number/__init__.py | 43 +++++++++++--------- esphome/components/lvgl/number/lvgl_number.h | 41 ++++++++++++------- esphome/components/lvgl/select/__init__.py | 24 +++++------ esphome/components/lvgl/select/lvgl_select.h | 43 ++++++++++++-------- tests/components/lvgl/common.yaml | 1 + tests/components/lvgl/lvgl-package.yaml | 10 +++++ 7 files changed, 99 insertions(+), 66 deletions(-) diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index c8d744dfc8..67a87d24bf 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -173,7 +173,8 @@ class LambdaContext(CodeContext): class LvContext(LambdaContext): """ - Code generation into the LVGL initialisation code (called in `setup()`) + Code generation into the LVGL initialisation code, called before setup() and loop() + Basically just does cg.add, so now fairly redundant. """ added_lambda_count = 0 diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py index b41a36bc0f..98f8423b7c 100644 --- a/esphome/components/lvgl/number/__init__.py +++ b/esphome/components/lvgl/number/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg from esphome.components import number import esphome.config_validation as cv +from esphome.const import CONF_RESTORE_VALUE from esphome.cpp_generator import MockObj from ..defines import CONF_ANIMATED, CONF_UPDATE_ON_RELEASE, CONF_WIDGET @@ -10,21 +11,21 @@ from ..lvcode import ( EVENT_ARG, UPDATE_EVENT, LambdaContext, - LvContext, + ReturnStatement, lv, - lv_add, lvgl_static, ) from ..types import LV_EVENT, LvNumber, lvgl_ns from ..widgets import get_widgets, wait_for_widgets -LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) +LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number, cg.Component) CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend( { cv.Required(CONF_WIDGET): cv.use_id(LvNumber), cv.Optional(CONF_ANIMATED, default=True): animated, cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean, + cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, } ) @@ -32,32 +33,34 @@ CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend( async def to_code(config): widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] - var = await number.new_number( - config, - max_value=widget.get_max(), - min_value=widget.get_min(), - step=widget.get_step(), - ) - await wait_for_widgets() + async with LambdaContext([], return_type=cg.float_) as value: + value.add(ReturnStatement(widget.get_value())) async with LambdaContext([(cg.float_, "v")]) as control: await widget.set_property( "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] ) lv.event_send(widget.obj, API_EVENT, cg.nullptr) - control.add(var.publish_state(widget.get_value())) - async with LambdaContext(EVENT_ARG) as event: - event.add(var.publish_state(widget.get_value())) event_code = ( LV_EVENT.VALUE_CHANGED if not config[CONF_UPDATE_ON_RELEASE] else LV_EVENT.RELEASED ) - async with LvContext(): - lv_add(var.set_control_lambda(await control.get_lambda())) - lv_add( - lvgl_static.add_event_cb( - widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code - ) + var = await number.new_number( + config, + await control.get_lambda(), + await value.get_lambda(), + event_code, + config[CONF_RESTORE_VALUE], + max_value=widget.get_max(), + min_value=widget.get_min(), + step=widget.get_step(), + ) + async with LambdaContext(EVENT_ARG) as event: + event.add(var.on_value()) + await cg.register_component(var, config) + cg.add( + lvgl_static.add_event_cb( + widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code ) - lv_add(var.publish_state(widget.get_value())) + ) diff --git a/esphome/components/lvgl/number/lvgl_number.h b/esphome/components/lvgl/number/lvgl_number.h index 77fadd2a29..277494673b 100644 --- a/esphome/components/lvgl/number/lvgl_number.h +++ b/esphome/components/lvgl/number/lvgl_number.h @@ -3,33 +3,46 @@ #include #include "esphome/components/number/number.h" -#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" namespace esphome { namespace lvgl { -class LVGLNumber : public number::Number { +class LVGLNumber : public number::Number, public Component { public: - void set_control_lambda(std::function control_lambda) { - this->control_lambda_ = std::move(control_lambda); - if (this->initial_state_.has_value()) { - this->control_lambda_(this->initial_state_.value()); - this->initial_state_.reset(); + LVGLNumber(std::function control_lambda, std::function value_lambda, lv_event_code_t event, + bool restore) + : control_lambda_(std::move(control_lambda)), + value_lambda_(std::move(value_lambda)), + event_(event), + restore_(restore) {} + + void setup() override { + float value = this->value_lambda_(); + if (this->restore_) { + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + if (this->pref_.load(&value)) { + this->control_lambda_(value); + } } + this->publish_state(value); } + void on_value() { this->publish_state(this->value_lambda_()); } + protected: void control(float value) override { - if (this->control_lambda_ != nullptr) { - this->control_lambda_(value); - } else { - this->initial_state_ = value; - } + this->control_lambda_(value); + this->publish_state(value); + if (this->restore_) + this->pref_.save(&value); } - std::function control_lambda_{}; - optional initial_state_{}; + std::function control_lambda_; + std::function value_lambda_; + lv_event_code_t event_; + bool restore_; + ESPPreferenceObject pref_{}; }; } // namespace lvgl diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index bd5ef8f237..4f9d12266e 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -1,18 +1,19 @@ +import esphome.codegen as cg from esphome.components import select import esphome.config_validation as cv -from esphome.const import CONF_OPTIONS +from esphome.const import CONF_ID, CONF_OPTIONS, CONF_RESTORE_VALUE from ..defines import CONF_ANIMATED, CONF_WIDGET, literal -from ..lvcode import LvContext from ..types import LvSelect, lvgl_ns -from ..widgets import get_widgets, wait_for_widgets +from ..widgets import get_widgets -LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) +LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select, cg.Component) CONFIG_SCHEMA = select.select_schema(LVGLSelect).extend( { cv.Required(CONF_WIDGET): cv.use_id(LvSelect), cv.Optional(CONF_ANIMATED, default=False): cv.boolean, + cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, } ) @@ -21,12 +22,9 @@ async def to_code(config): widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] options = widget.config.get(CONF_OPTIONS, []) - selector = await select.new_select(config, options=options) - await wait_for_widgets() - async with LvContext() as ctx: - ctx.add( - selector.set_widget( - widget.var, - literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF"), - ) - ) + animated = literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF") + selector = cg.new_Pvariable( + config[CONF_ID], widget.var, animated, config[CONF_RESTORE_VALUE] + ) + await select.register_select(selector, config, options=options) + await cg.register_component(selector, config) diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index 4538e339c3..5b43209a5f 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -11,12 +11,20 @@ namespace esphome { namespace lvgl { -class LVGLSelect : public select::Select { +class LVGLSelect : public select::Select, public Component { public: - void set_widget(LvSelectable *widget, lv_anim_enable_t anim = LV_ANIM_OFF) { - this->widget_ = widget; - this->anim_ = anim; + LVGLSelect(LvSelectable *widget, lv_anim_enable_t anim, bool restore) + : widget_(widget), anim_(anim), restore_(restore) {} + + void setup() override { this->set_options_(); + if (this->restore_) { + size_t index; + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + if (this->pref_.load(&index)) + this->widget_->set_selected_index(index, LV_ANIM_OFF); + } + this->publish(); lv_obj_add_event_cb( this->widget_->obj, [](lv_event_t *e) { @@ -24,11 +32,6 @@ class LVGLSelect : public select::Select { it->set_options_(); }, LV_EVENT_REFRESH, this); - if (this->initial_state_.has_value()) { - this->control(this->initial_state_.value()); - this->initial_state_.reset(); - } - this->publish(); auto lamb = [](lv_event_t *e) { auto *self = static_cast(e->user_data); self->publish(); @@ -37,21 +40,25 @@ class LVGLSelect : public select::Select { lv_obj_add_event_cb(this->widget_->obj, lamb, lv_update_event, this); } - void publish() { this->publish_state(this->widget_->get_selected_text()); } + void publish() { + this->publish_state(this->widget_->get_selected_text()); + if (this->restore_) { + auto index = this->widget_->get_selected_index(); + this->pref_.save(&index); + } + } protected: void control(const std::string &value) override { - if (this->widget_ != nullptr) { - this->widget_->set_selected_text(value, this->anim_); - } else { - this->initial_state_ = value; - } + this->widget_->set_selected_text(value, this->anim_); + this->publish(); } void set_options_() { this->traits.set_options(this->widget_->get_options()); } - LvSelectable *widget_{}; - optional initial_state_{}; - lv_anim_enable_t anim_{LV_ANIM_OFF}; + LvSelectable *widget_; + lv_anim_enable_t anim_; + bool restore_; + ESPPreferenceObject pref_{}; }; } // namespace lvgl diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index c7d635db1c..174df56749 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -38,6 +38,7 @@ number: widget: slider_id name: LVGL Slider update_on_release: true + restore_value: true - platform: lvgl widget: lv_arc id: lvgl_arc_number diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 78c261c01d..a0b7dd096f 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -990,3 +990,13 @@ color: green_int: 123 blue_int: 64 white_int: 255 + +select: + - platform: lvgl + id: lv_roller_select + widget: lv_roller + restore_value: true + - platform: lvgl + id: lv_dropdown_select + widget: lv_dropdown + restore_value: false From 34a4e70cc5169b322de6298e4a81220b66d754a4 Mon Sep 17 00:00:00 2001 From: Calum McConnell Date: Sun, 13 Apr 2025 18:48:45 -0400 Subject: [PATCH 06/14] Update setup to make .temp directory (#8558) --- script/setup | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/setup b/script/setup index 824840c392..3ebf75387f 100755 --- a/script/setup +++ b/script/setup @@ -21,6 +21,8 @@ pre-commit install script/platformio_install_deps.py platformio.ini --libraries --tools --platforms +mkdir .temp + echo echo echo "Virtual environment created. Run 'source $location' to use it." From 4a9f323d92904acf5c35d3d54473a94c58a3436f Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 14 Apr 2025 00:59:40 +0200 Subject: [PATCH 07/14] [core] make require_framework_version generic (#8412) --- esphome/components/network/__init__.py | 2 +- esphome/config_validation.py | 73 ++++-------------- tests/unit_tests/test_config_validation.py | 90 ++++++++++++++++++++++ 3 files changed, 106 insertions(+), 59 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index be4e102930..129b1ced06 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.Schema( esp32_arduino=cv.Version(0, 0, 0), esp8266_arduino=cv.Version(0, 0, 0), rp2040_arduino=cv.Version(0, 0, 0), - bk72xx_libretiny=cv.Version(1, 7, 0), + bk72xx_arduino=cv.Version(1, 7, 0), ), cv.boolean_false, ), diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 858c6e197c..bb1cb1ac2f 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -56,7 +56,6 @@ from esphome.const import ( KEY_CORE, KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, - KEY_TARGET_PLATFORM, PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, @@ -1984,70 +1983,28 @@ def platformio_version_constraint(value): def require_framework_version( *, - esp_idf=None, - esp32_arduino=None, - esp8266_arduino=None, - rp2040_arduino=None, - bk72xx_libretiny=None, - host=None, max_version=False, extra_message=None, + **kwargs, ): def validator(value): core_data = CORE.data[KEY_CORE] framework = core_data[KEY_TARGET_FRAMEWORK] - if framework == "esp-idf": - if esp_idf is None: - msg = "This feature is incompatible with esp-idf" - if extra_message: - msg += f". {extra_message}" - raise Invalid(msg) - required = esp_idf - elif CORE.is_bk72xx and framework == "arduino": - if bk72xx_libretiny is None: - msg = "This feature is incompatible with BK72XX" - if extra_message: - msg += f". {extra_message}" - raise Invalid(msg) - required = bk72xx_libretiny - elif CORE.is_esp32 and framework == "arduino": - if esp32_arduino is None: - msg = "This feature is incompatible with ESP32 using arduino framework" - if extra_message: - msg += f". {extra_message}" - raise Invalid(msg) - required = esp32_arduino - elif CORE.is_esp8266 and framework == "arduino": - if esp8266_arduino is None: - msg = "This feature is incompatible with ESP8266" - if extra_message: - msg += f". {extra_message}" - raise Invalid(msg) - required = esp8266_arduino - elif CORE.is_rp2040 and framework == "arduino": - if rp2040_arduino is None: - msg = "This feature is incompatible with RP2040" - if extra_message: - msg += f". {extra_message}" - raise Invalid(msg) - required = rp2040_arduino - elif CORE.is_host and framework == "host": - if host is None: - msg = "This feature is incompatible with host platform" - if extra_message: - msg += f". {extra_message}" - raise Invalid(msg) - required = host - else: - raise Invalid( - f""" - Internal Error: require_framework_version does not support this platform configuration - platform: {core_data[KEY_TARGET_PLATFORM]} - framework: {framework} - Please report this issue on GitHub -> https://github.com/esphome/issues/issues/new?template=bug_report.yml. - """ - ) + if CORE.is_host and framework == "host": + key = "host" + elif framework == "esp-idf": + key = "esp_idf" + else: + key = CORE.target_platform + "_" + framework + + if key not in kwargs: + msg = f"This feature is incompatible with {CORE.target_platform.upper()} using {framework} framework" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) + + required = kwargs[key] if max_version: if core_data[KEY_FRAMEWORK_VERSION] > required: diff --git a/tests/unit_tests/test_config_validation.py b/tests/unit_tests/test_config_validation.py index 3b2c72af2c..7a1354589c 100644 --- a/tests/unit_tests/test_config_validation.py +++ b/tests/unit_tests/test_config_validation.py @@ -284,3 +284,93 @@ def test_split_default(framework, platform, variant, full, idf, arduino, simple) assert schema({}).get("idf") == idf assert schema({}).get("arduino") == arduino assert schema({}).get("simple") == simple + + +@pytest.mark.parametrize( + "framework, platform, message", + [ + ("esp-idf", PLATFORM_ESP32, "ESP32 using esp-idf framework"), + ("arduino", PLATFORM_ESP32, "ESP32 using arduino framework"), + ("arduino", PLATFORM_ESP8266, "ESP8266 using arduino framework"), + ("arduino", PLATFORM_RP2040, "RP2040 using arduino framework"), + ("arduino", PLATFORM_BK72XX, "BK72XX using arduino framework"), + ("host", PLATFORM_HOST, "HOST using host framework"), + ], +) +def test_require_framework_version(framework, platform, message): + import voluptuous as vol + + from esphome.const import ( + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + ) + + CORE.data[KEY_CORE] = {} + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = platform + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = framework + CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = config_validation.Version(1, 0, 0) + + assert ( + config_validation.require_framework_version( + esp_idf=config_validation.Version(0, 5, 0), + esp32_arduino=config_validation.Version(0, 5, 0), + esp8266_arduino=config_validation.Version(0, 5, 0), + rp2040_arduino=config_validation.Version(0, 5, 0), + bk72xx_arduino=config_validation.Version(0, 5, 0), + host=config_validation.Version(0, 5, 0), + extra_message="test 1", + )("test") + == "test" + ) + + with pytest.raises( + vol.error.Invalid, + match="This feature requires at least framework version 2.0.0. test 2", + ): + config_validation.require_framework_version( + esp_idf=config_validation.Version(2, 0, 0), + esp32_arduino=config_validation.Version(2, 0, 0), + esp8266_arduino=config_validation.Version(2, 0, 0), + rp2040_arduino=config_validation.Version(2, 0, 0), + bk72xx_arduino=config_validation.Version(2, 0, 0), + host=config_validation.Version(2, 0, 0), + extra_message="test 2", + )("test") + + assert ( + config_validation.require_framework_version( + esp_idf=config_validation.Version(1, 5, 0), + esp32_arduino=config_validation.Version(1, 5, 0), + esp8266_arduino=config_validation.Version(1, 5, 0), + rp2040_arduino=config_validation.Version(1, 5, 0), + bk72xx_arduino=config_validation.Version(1, 5, 0), + host=config_validation.Version(1, 5, 0), + max_version=True, + extra_message="test 3", + )("test") + == "test" + ) + + with pytest.raises( + vol.error.Invalid, + match="This feature requires framework version 0.5.0 or lower. test 4", + ): + config_validation.require_framework_version( + esp_idf=config_validation.Version(0, 5, 0), + esp32_arduino=config_validation.Version(0, 5, 0), + esp8266_arduino=config_validation.Version(0, 5, 0), + rp2040_arduino=config_validation.Version(0, 5, 0), + bk72xx_arduino=config_validation.Version(0, 5, 0), + host=config_validation.Version(0, 5, 0), + max_version=True, + extra_message="test 4", + )("test") + + with pytest.raises( + vol.error.Invalid, match=f"This feature is incompatible with {message}. test 5" + ): + config_validation.require_framework_version( + extra_message="test 5", + )("test") From abcc656a6ff48ae0d1c4e7fa55f38193725dad05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:41:33 -1000 Subject: [PATCH 08/14] Bump ruff from 0.11.4 to 0.11.5 (#8546) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 9b75c73710..7e8ce5cc34 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==3.3.6 flake8==7.2.0 # also change in .pre-commit-config.yaml when updating -ruff==0.11.4 # also change in .pre-commit-config.yaml when updating +ruff==0.11.5 # also change in .pre-commit-config.yaml when updating pyupgrade==3.19.1 # also change in .pre-commit-config.yaml when updating pre-commit From 995db1f961161ada2a4827e55267746379a640e7 Mon Sep 17 00:00:00 2001 From: Norbert Schulz Date: Mon, 14 Apr 2025 04:02:40 +0200 Subject: [PATCH 09/14] Add support for Waveshare 5.65" ACeP 7-Color display (#8557) --- .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_epaper.cpp | 247 +++++++++++++++--- .../waveshare_epaper/waveshare_epaper.h | 37 ++- tests/components/waveshare_epaper/common.yaml | 20 ++ 4 files changed, 266 insertions(+), 42 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 8acb6ac68f..afce83d553 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -70,6 +70,9 @@ WaveshareEPaper4P2InBV2 = waveshare_epaper_ns.class_( WaveshareEPaper4P2InBV2BWR = waveshare_epaper_ns.class_( "WaveshareEPaper4P2InBV2BWR", WaveshareEPaperBWR ) +WaveshareEPaper5P65InF = waveshare_epaper_ns.class_( + "WaveshareEPaper5P65InF", WaveshareEPaper7C +) WaveshareEPaper5P8In = waveshare_epaper_ns.class_( "WaveshareEPaper5P8In", WaveshareEPaper ) @@ -150,6 +153,7 @@ MODELS = { "4.20in": ("b", WaveshareEPaper4P2In), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), "4.20in-bv2-bwr": ("b", WaveshareEPaper4P2InBV2BWR), + "5.65in-f": ("b", WaveshareEPaper5P65InF), "5.83in": ("b", WaveshareEPaper5P8In), "5.83inv2": ("b", WaveshareEPaper5P8InV2), "7.30in-f": ("b", WaveshareEPaper7P3InF), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 96fc82fcdd..8e30fc4c32 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -258,6 +258,47 @@ void WaveshareEPaper7C::fill(Color color) { } } } +void WaveshareEPaper7C::send_buffers_() { + if (this->buffers_[0] == nullptr) { + ESP_LOGE(TAG, "Buffer unavailable!"); + return; + } + + uint32_t small_buffer_length = this->get_buffer_length_() / NUM_BUFFERS; + uint8_t byte_to_send; + for (auto &buffer : this->buffers_) { + for (uint32_t buffer_pos = 0; buffer_pos < small_buffer_length; buffer_pos += 3) { + std::bitset<24> triplet = + buffer[buffer_pos + 0] << 16 | buffer[buffer_pos + 1] << 8 | buffer[buffer_pos + 2] << 0; + // 8 bitset<3> are stored in 3 bytes + // |aaabbbaa|abbbaaab|bbaaabbb| + // | byte 1 | byte 2 | byte 3 | + byte_to_send = ((triplet >> 17).to_ulong() & 0b01110000) | ((triplet >> 18).to_ulong() & 0b00000111); + this->data(byte_to_send); + + byte_to_send = ((triplet >> 11).to_ulong() & 0b01110000) | ((triplet >> 12).to_ulong() & 0b00000111); + this->data(byte_to_send); + + byte_to_send = ((triplet >> 5).to_ulong() & 0b01110000) | ((triplet >> 6).to_ulong() & 0b00000111); + this->data(byte_to_send); + + byte_to_send = ((triplet << 1).to_ulong() & 0b01110000) | ((triplet << 0).to_ulong() & 0b00000111); + this->data(byte_to_send); + } + App.feed_wdt(); + } +} +void WaveshareEPaper7C::reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(true); + delay(20); + this->reset_pin_->digital_write(false); + delay(1); + this->reset_pin_->digital_write(true); + delay(20); + } +} + void HOT WaveshareEPaper::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) return; @@ -3307,6 +3348,175 @@ void WaveshareEPaper7P5In::dump_config() { LOG_PIN(" Busy Pin: ", this->busy_pin_); LOG_UPDATE_INTERVAL(this); } + +// Waveshare 5.65F ======================================================== + +namespace cmddata_5P65InF { +// WaveshareEPaper5P65InF commands +// https://www.waveshare.com/wiki/5.65inch_e-Paper_Module_(F) + +// R00H (PSR): Panel setting Register +// UD(1): scan up +// SHL(1) shift right +// SHD_N(1) DC-DC on +// RST_N(1) no reset +static const uint8_t R00_CMD_PSR[] = {0x00, 0xEF, 0x08}; + +// R01H (PWR): Power setting Register +// internal DC-DC power generation +static const uint8_t R01_CMD_PWR[] = {0x01, 0x07, 0x00, 0x00, 0x00}; + +// R02H (POF): Power OFF Command +static const uint8_t R02_CMD_POF[] = {0x02}; + +// R03H (PFS): Power off sequence setting Register +// T_VDS_OFF (00) = 1 frame +static const uint8_t R03_CMD_PFS[] = {0x03, 0x00}; + +// R04H (PON): Power ON Command +static const uint8_t R04_CMD_PON[] = {0x04}; + +// R06h (BTST): Booster Soft Start +static const uint8_t R06_CMD_BTST[] = {0x06, 0xC7, 0xC7, 0x1D}; + +// R07H (DSLP): Deep sleep# +// Note Documentation @ Waveshare shows cmd code as 0x10 in table, but +// 0x10 is DTM1. +static const uint8_t R07_CMD_DSLP[] = {0x07, 0xA5}; + +// R10H (DTM1): Data Start Transmission 1 + +static const uint8_t R10_CMD_DTM1[] = {0x10}; + +// R11H (DSP): Data Stop +static const uint8_t R11_CMD_DSP[] = {0x11}; + +// R12H (DRF): Display Refresh +static const uint8_t R12_CMD_DRF[] = {0x12}; + +// R13H (IPC): Image Process Command +static const uint8_t R13_CMD_IPC[] = {0x13, 0x00}; + +// R30H (PLL): PLL Control +// 0x3C = 50Hz +static const uint8_t R30_CMD_PLL[] = {0x30, 0x3C}; + +// R41H (TSE): Temperature Sensor Enable +// TSE(0) enable, TO(0000) +0 degree offset +static const uint8_t R41_CMD_TSE[] = {0x41, 0x00}; + +// R50H (CDI) VCOM and Data interval setting +// CDI(0111) 10 +// DDX(1), VBD(001) Border output "White" +static const uint8_t R50_CMD_CDI[] = {0x50, 0x37}; + +// R60H (TCON) Gate and Source non overlap period command +// S2G(10) 12 units +// G2S(10) 12 units +static const uint8_t R60_CMD_TCON[] = {0x60, 0x22}; + +// R61H (TRES) Resolution Setting +// 0x258 = 600 +// 0x1C0 = 448 +static const uint8_t R61_CMD_TRES[] = {0x61, 0x02, 0x58, 0x01, 0xC0}; + +// RE3H (PWS) Power Savings +static const uint8_t RE3_CMD_PWS[] = {0xE3, 0xAA}; +} // namespace cmddata_5P65InF + +void WaveshareEPaper5P65InF::initialize() { + if (this->buffers_[0] == nullptr) { + ESP_LOGE(TAG, "Buffer unavailable!"); + return; + } + + this->reset_(); + delay(20); + this->wait_until_(IDLE); + + using namespace cmddata_5P65InF; + + this->cmd_data(R00_CMD_PSR, sizeof(R00_CMD_PSR)); + this->cmd_data(R01_CMD_PWR, sizeof(R01_CMD_PWR)); + this->cmd_data(R03_CMD_PFS, sizeof(R03_CMD_PFS)); + this->cmd_data(R06_CMD_BTST, sizeof(R06_CMD_BTST)); + this->cmd_data(R30_CMD_PLL, sizeof(R30_CMD_PLL)); + this->cmd_data(R41_CMD_TSE, sizeof(R41_CMD_TSE)); + this->cmd_data(R50_CMD_CDI, sizeof(R50_CMD_CDI)); + this->cmd_data(R60_CMD_TCON, sizeof(R60_CMD_TCON)); + this->cmd_data(R61_CMD_TRES, sizeof(R61_CMD_TRES)); + this->cmd_data(RE3_CMD_PWS, sizeof(RE3_CMD_PWS)); + + delay(100); // NOLINT + this->cmd_data(R50_CMD_CDI, sizeof(R50_CMD_CDI)); + + ESP_LOGI(TAG, "Display initialized successfully"); +} + +void HOT WaveshareEPaper5P65InF::display() { + // INITIALIZATION + ESP_LOGI(TAG, "Initialise the display"); + this->initialize(); + + using namespace cmddata_5P65InF; + + // COMMAND DATA START TRANSMISSION + ESP_LOGI(TAG, "Sending data to the display"); + this->cmd_data(R61_CMD_TRES, sizeof(R61_CMD_TRES)); + this->cmd_data(R10_CMD_DTM1, sizeof(R10_CMD_DTM1)); + this->send_buffers_(); + + // COMMAND POWER ON + ESP_LOGI(TAG, "Power on the display"); + this->cmd_data(R04_CMD_PON, sizeof(R04_CMD_PON)); + this->wait_until_(IDLE); + + // COMMAND REFRESH SCREEN + ESP_LOGI(TAG, "Refresh the display"); + this->cmd_data(R12_CMD_DRF, sizeof(R12_CMD_DRF)); + this->wait_until_(IDLE); + + // COMMAND POWER OFF + ESP_LOGI(TAG, "Power off the display"); + this->cmd_data(R02_CMD_POF, sizeof(R02_CMD_POF)); + this->wait_until_(BUSY); + + if (this->deep_sleep_between_updates_) { + ESP_LOGI(TAG, "Set the display to deep sleep"); + this->cmd_data(R07_CMD_DSLP, sizeof(R07_CMD_DSLP)); + } +} + +int WaveshareEPaper5P65InF::get_width_internal() { return 600; } +int WaveshareEPaper5P65InF::get_height_internal() { return 448; } +uint32_t WaveshareEPaper5P65InF::idle_timeout_() { return 35000; } + +void WaveshareEPaper5P65InF::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 5.65in-F"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + +bool WaveshareEPaper5P65InF::wait_until_(WaitForState busy_state) { + if (this->busy_pin_ == nullptr) { + return true; + } + + const uint32_t start = millis(); + while (busy_state != this->busy_pin_->digital_read()) { + if (millis() - start > this->idle_timeout_()) { + ESP_LOGE(TAG, "Timeout while displaying image!"); + return false; + } + App.feed_wdt(); + delay(10); + } + return true; +} + void WaveshareEPaper7P3InF::initialize() { if (this->buffers_[0] == nullptr) { ESP_LOGE(TAG, "Buffer unavailable!"); @@ -3411,11 +3621,6 @@ void WaveshareEPaper7P3InF::initialize() { ESP_LOGI(TAG, "Display initialized successfully"); } void HOT WaveshareEPaper7P3InF::display() { - if (this->buffers_[0] == nullptr) { - ESP_LOGE(TAG, "Buffer unavailable!"); - return; - } - // INITIALIZATION ESP_LOGI(TAG, "Initialise the display"); this->initialize(); @@ -3423,29 +3628,7 @@ void HOT WaveshareEPaper7P3InF::display() { // COMMAND DATA START TRANSMISSION ESP_LOGI(TAG, "Sending data to the display"); this->command(0x10); - uint32_t small_buffer_length = this->get_buffer_length_() / NUM_BUFFERS; - uint8_t byte_to_send; - for (auto &buffer : this->buffers_) { - for (uint32_t buffer_pos = 0; buffer_pos < small_buffer_length; buffer_pos += 3) { - std::bitset<24> triplet = - buffer[buffer_pos + 0] << 16 | buffer[buffer_pos + 1] << 8 | buffer[buffer_pos + 2] << 0; - // 8 bitset<3> are stored in 3 bytes - // |aaabbbaa|abbbaaab|bbaaabbb| - // | byte 1 | byte 2 | byte 3 | - byte_to_send = ((triplet >> 17).to_ulong() & 0b01110000) | ((triplet >> 18).to_ulong() & 0b00000111); - this->data(byte_to_send); - - byte_to_send = ((triplet >> 11).to_ulong() & 0b01110000) | ((triplet >> 12).to_ulong() & 0b00000111); - this->data(byte_to_send); - - byte_to_send = ((triplet >> 5).to_ulong() & 0b01110000) | ((triplet >> 6).to_ulong() & 0b00000111); - this->data(byte_to_send); - - byte_to_send = ((triplet << 1).to_ulong() & 0b01110000) | ((triplet << 0).to_ulong() & 0b00000111); - this->data(byte_to_send); - } - App.feed_wdt(); - } + this->send_buffers_(); // COMMAND POWER ON ESP_LOGI(TAG, "Power on the display"); @@ -3464,9 +3647,11 @@ void HOT WaveshareEPaper7P3InF::display() { this->data(0x00); this->wait_until_idle_(); - ESP_LOGI(TAG, "Set the display to deep sleep"); - this->command(0x07); - this->data(0xA5); + if (this->deep_sleep_between_updates_) { + ESP_LOGI(TAG, "Set the display to deep sleep"); + this->command(0x07); + this->data(0xA5); + } } int WaveshareEPaper7P3InF::get_width_internal() { return 800; } int WaveshareEPaper7P3InF::get_height_internal() { return 480; } diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index d6387cd643..9fff1ea6b5 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -94,7 +94,10 @@ class WaveshareEPaper7C : public WaveshareEPaperBase { void draw_absolute_pixel_internal(int x, int y, Color color) override; uint32_t get_buffer_length_() override; void setup() override; + void init_internal_7c_(uint32_t buffer_length); + void send_buffers_(); + void reset_(); static const int NUM_BUFFERS = 10; uint8_t *buffers_[NUM_BUFFERS]; @@ -683,6 +686,29 @@ class WaveshareEPaper5P8InV2 : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper5P65InF : public WaveshareEPaper7C { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + protected: + int get_width_internal() override; + + int get_height_internal() override; + + uint32_t idle_timeout_() override; + + void deep_sleep() override { ; } + + enum WaitForState { BUSY = true, IDLE = false }; + bool wait_until_(WaitForState state); + + bool deep_sleep_between_updates_{true}; +}; + class WaveshareEPaper7P3InF : public WaveshareEPaper7C { public: void initialize() override; @@ -703,17 +729,6 @@ class WaveshareEPaper7P3InF : public WaveshareEPaper7C { bool wait_until_idle_(); bool deep_sleep_between_updates_{true}; - - void reset_() { - if (this->reset_pin_ != nullptr) { - this->reset_pin_->digital_write(true); - delay(20); - this->reset_pin_->digital_write(false); - delay(1); - this->reset_pin_->digital_write(true); - delay(20); - } - }; }; class WaveshareEPaper7P5In : public WaveshareEPaper { diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index 09ba1af778..a2aa3134b5 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -541,6 +541,26 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + # 5.65 inch displays + - platform: waveshare_epaper + id: epd_5_65 + model: 5.65in-f + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + # 5.83 inch displays - platform: waveshare_epaper id: epd_5_83 From 5908b93e829c66b2a7f7df95fcf1c40db0c01bad Mon Sep 17 00:00:00 2001 From: Mischa Siekmann <45062894+gnumpi@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:51:10 +0200 Subject: [PATCH 10/14] Speaker-Media-Player: Fix potential deadlock in audio pipeline (#8548) --- esphome/components/speaker/media_player/audio_pipeline.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/speaker/media_player/audio_pipeline.cpp b/esphome/components/speaker/media_player/audio_pipeline.cpp index b49cf3ddda..60f562cc2c 100644 --- a/esphome/components/speaker/media_player/audio_pipeline.cpp +++ b/esphome/components/speaker/media_player/audio_pipeline.cpp @@ -441,9 +441,10 @@ void AudioPipeline::decode_task(void *params) { pdFALSE, // Wait for all the bits, portMAX_DELAY); // Block indefinitely until bit is set + xEventGroupClearBits(this_pipeline->event_group_, + EventGroupBits::DECODER_MESSAGE_FINISHED | EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE); + if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) { - xEventGroupClearBits(this_pipeline->event_group_, - EventGroupBits::DECODER_MESSAGE_FINISHED | EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE); InfoErrorEvent event; event.source = InfoErrorSource::DECODER; From 2dfcf950fa9aee52ea957e3be9c8c2a710b12acd Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Apr 2025 05:39:56 +1000 Subject: [PATCH 11/14] [lvgl] Ensure captured lambdas are in correct order (#8560) --- esphome/components/lvgl/automation.py | 16 ++++++++++++++-- esphome/components/lvgl/widgets/buttonmatrix.py | 4 ++-- esphome/components/lvgl/widgets/canvas.py | 10 +++++++--- esphome/components/lvgl/widgets/meter.py | 4 +++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index b0979b2848..4a71872022 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -4,6 +4,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT +from esphome.core import Lambda from esphome.cpp_generator import TemplateArguments, get_variable from esphome.cpp_types import nullptr @@ -64,7 +65,14 @@ async def action_to_code( action_id, template_arg, args, + config=None, ): + # Ensure all required ids have been processed, so our LambdaContext doesn't get context-switched. + if config: + for lamb in config.values(): + if isinstance(lamb, Lambda): + for id_ in lamb.requires_ids: + await get_variable(id_) await wait_for_widgets() async with LambdaContext(parameters=args, where=action_id) as context: for widget in widgets: @@ -84,7 +92,9 @@ async def update_to_code(config, action_id, template_arg, args): lv.event_send(widget.obj, UPDATE_EVENT, nullptr) widgets = await get_widgets(config[CONF_ID]) - return await action_to_code(widgets, do_update, action_id, template_arg, args) + return await action_to_code( + widgets, do_update, action_id, template_arg, args, config + ) @automation.register_condition( @@ -348,4 +358,6 @@ async def obj_update_to_code(config, action_id, template_arg, args): await set_obj_properties(widget, config) widgets = await get_widgets(config[CONF_ID]) - return await action_to_code(widgets, do_update, action_id, template_arg, args) + return await action_to_code( + widgets, do_update, action_id, template_arg, args, config + ) diff --git a/esphome/components/lvgl/widgets/buttonmatrix.py b/esphome/components/lvgl/widgets/buttonmatrix.py index c65bb4b354..0ba1fe4ae1 100644 --- a/esphome/components/lvgl/widgets/buttonmatrix.py +++ b/esphome/components/lvgl/widgets/buttonmatrix.py @@ -250,7 +250,7 @@ async def button_update_to_code(config, action_id, template_arg, args): widgets = await get_widgets(config[CONF_ID]) assert all(isinstance(w, MatrixButton) for w in widgets) - async def do_button_update(w: MatrixButton): + async def do_button_update(w): if (width := config.get(CONF_WIDTH)) is not None: lv.btnmatrix_set_btn_width(w.obj, w.index, width) if config.get(CONF_SELECTED): @@ -275,5 +275,5 @@ async def button_update_to_code(config, action_id, template_arg, args): ) return await action_to_code( - widgets, do_button_update, action_id, template_arg, args + widgets, do_button_update, action_id, template_arg, args, config ) diff --git a/esphome/components/lvgl/widgets/canvas.py b/esphome/components/lvgl/widgets/canvas.py index bc26558624..60812093d5 100644 --- a/esphome/components/lvgl/widgets/canvas.py +++ b/esphome/components/lvgl/widgets/canvas.py @@ -97,7 +97,7 @@ async def canvas_fill(config, action_id, template_arg, args): async def do_fill(w: Widget): lv.canvas_fill_bg(w.obj, color, opa) - return await action_to_code(widget, do_fill, action_id, template_arg, args) + return await action_to_code(widget, do_fill, action_id, template_arg, args, config) @automation.register_action( @@ -145,7 +145,9 @@ async def canvas_set_pixel(config, action_id, template_arg, args): x, y = point lv.canvas_set_px_opa(w.obj, x, y, opa_var) - return await action_to_code(widget, do_set_pixels, action_id, template_arg, args) + return await action_to_code( + widget, do_set_pixels, action_id, template_arg, args, config + ) DRAW_SCHEMA = cv.Schema( @@ -181,7 +183,9 @@ async def draw_to_code(config, dsc_type, props, do_draw, action_id, template_arg lv_assign(getattr(dsc, mapped_prop), value) await do_draw(w, x, y, dsc_addr) - return await action_to_code(widget, action_func, action_id, template_arg, args) + return await action_to_code( + widget, action_func, action_id, template_arg, args, config + ) RECT_PROPS = { diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index 29a382f7cf..840511da69 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -297,7 +297,9 @@ async def indicator_update_to_code(config, action_id, template_arg, args): async def set_value(w: Widget): await set_indicator_values(w.var, w.obj, config) - return await action_to_code(widget, set_value, action_id, template_arg, args) + return await action_to_code( + widget, set_value, action_id, template_arg, args, config + ) async def set_indicator_values(meter, indicator, config): From 9b86cc37f026dfcda7d668f83658d1d0cdeaddeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 08:06:48 +1200 Subject: [PATCH 12/14] Bump aioesphomeapi from 29.9.0 to 29.10.0 (#8562) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0ee928569b..3c57fa9cc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==4.8.1 click==8.1.7 esphome-dashboard==20250212.0 -aioesphomeapi==29.9.0 +aioesphomeapi==29.10.0 zeroconf==0.146.3 puremagic==1.28 ruamel.yaml==0.18.10 # dashboard_import From a007a8237a6f1be732ac0654bbbe4439a6921a8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 08:07:29 +1200 Subject: [PATCH 13/14] Bump zeroconf from 0.146.3 to 0.146.4 (#8563) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3c57fa9cc5..06fa6445ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.8.1 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==29.10.0 -zeroconf==0.146.3 +zeroconf==0.146.4 puremagic==1.28 ruamel.yaml==0.18.10 # dashboard_import esphome-glyphsets==0.2.0 From 00dd5b833970f00fc1c3884191baa78c76688cf9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Apr 2025 08:09:51 +1200 Subject: [PATCH 14/14] [ci] Pin codecov action to v5.4.0 (#8564) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91c40d37c4..f69659d183 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -220,7 +220,7 @@ jobs: . venv/bin/activate pytest -vv --cov-report=xml --tb=native tests - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v5.4.0 with: token: ${{ secrets.CODECOV_TOKEN }}