From 0242ac56dfc6f49902dbb820cada19efb550d217 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Apr 2025 06:51:52 +1000 Subject: [PATCH 1/5] [lvgl] Ensure pages are created on the correct display (#8596) --- esphome/components/lvgl/lvgl_esphome.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 8d8380d429..4c30d14e15 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -120,6 +120,7 @@ void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_ev void LvglComponent::add_page(LvPageType *page) { this->pages_.push_back(page); page->set_parent(this); + lv_disp_set_default(this->disp_); page->setup(this->pages_.size() - 1); } void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) { From 82c6a40371a55a86ed31c9a69dbcb4a696a198ad Mon Sep 17 00:00:00 2001 From: Roving Ronin <108674933+Roving-Ronin@users.noreply.github.com> Date: Tue, 22 Apr 2025 07:13:31 +1000 Subject: [PATCH 2/5] Update const.py - Add UNIT_MEGAJOULE = "MJ" (#8594) --- esphome/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/const.py b/esphome/const.py index a21e18730f..b2437eca7e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1084,6 +1084,7 @@ UNIT_KILOWATT = "kW" UNIT_KILOWATT_HOURS = "kWh" UNIT_LITRE = "L" UNIT_LUX = "lx" +UNIT_MEGAJOULE = "MJ" UNIT_METER = "m" UNIT_METER_PER_SECOND_SQUARED = "m/s²" UNIT_MICROAMP = "µA" From fbf00f0af48981852ff617fb5969bc90cc0d7b43 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 21 Apr 2025 23:17:09 -1000 Subject: [PATCH 3/5] Fix BLE connection loop caused by timeout and pending disconnect race (#8597) --- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp | 6 ++++++ esphome/components/esp32_ble_tracker/esp32_ble_tracker.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index a263aca456..03213432cd 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -265,6 +265,12 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest connection->get_connection_index(), connection->address_str().c_str()); return; } else if (connection->state() == espbt::ClientState::CONNECTING) { + if (connection->disconnect_pending()) { + ESP_LOGW(TAG, "[%d] [%s] Connection request while pending disconnect, cancelling pending disconnect", + connection->get_connection_index(), connection->address_str().c_str()); + connection->cancel_pending_disconnect(); + return; + } ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already connecting", connection->get_connection_index(), connection->address_str().c_str()); return; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 99126f9173..8b712a01ea 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -173,6 +173,8 @@ class ESPBTClient : public ESPBTDeviceListener { virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; virtual void connect() = 0; virtual void disconnect() = 0; + bool disconnect_pending() const { return this->want_disconnect_; } + void cancel_pending_disconnect() { this->want_disconnect_ = false; } virtual void set_state(ClientState st) { this->state_ = st; if (st == ClientState::IDLE) { From dbb7cbed3ef39f4adc859bdb8693e97aac9306ef Mon Sep 17 00:00:00 2001 From: bdm310 Date: Tue, 22 Apr 2025 03:13:43 -0700 Subject: [PATCH 4/5] [lvgl] Fix unexpected widget update behavior (#8260) --- esphome/components/lvgl/widgets/arc.py | 13 +++++++------ esphome/components/lvgl/widgets/dropdown.py | 3 ++- esphome/components/lvgl/widgets/img.py | 11 ++++++----- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/esphome/components/lvgl/widgets/arc.py b/esphome/components/lvgl/widgets/arc.py index dc120e4cbb..65f0e785b6 100644 --- a/esphome/components/lvgl/widgets/arc.py +++ b/esphome/components/lvgl/widgets/arc.py @@ -67,12 +67,13 @@ class ArcType(NumberType): lv.arc_set_mode(w.obj, literal(config[CONF_MODE])) lv.arc_set_change_rate(w.obj, config[CONF_CHANGE_RATE]) - if config.get(CONF_ADJUSTABLE) is False: - lv_obj.remove_style(w.obj, nullptr, literal("LV_PART_KNOB")) - w.clear_flag("LV_OBJ_FLAG_CLICKABLE") - elif CONF_GROUP not in config: - # For some reason arc does not get automatically added to the default group - lv.group_add_obj(lv_expr.group_get_default(), w.obj) + if CONF_ADJUSTABLE in config: + if not config[CONF_ADJUSTABLE]: + lv_obj.remove_style(w.obj, nullptr, literal("LV_PART_KNOB")) + w.clear_flag("LV_OBJ_FLAG_CLICKABLE") + elif CONF_GROUP not in config: + # For some reason arc does not get automatically added to the default group + lv.group_add_obj(lv_expr.group_get_default(), w.obj) value = await get_start_value(config) if value is not None: diff --git a/esphome/components/lvgl/widgets/dropdown.py b/esphome/components/lvgl/widgets/dropdown.py index b32b5a2b2e..9ff183f3dd 100644 --- a/esphome/components/lvgl/widgets/dropdown.py +++ b/esphome/components/lvgl/widgets/dropdown.py @@ -36,7 +36,6 @@ DROPDOWN_BASE_SCHEMA = cv.Schema( cv.Optional(CONF_SYMBOL): lv_text, cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int, cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text, - cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of, cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec.parts), } ) @@ -44,12 +43,14 @@ DROPDOWN_BASE_SCHEMA = cv.Schema( DROPDOWN_SCHEMA = DROPDOWN_BASE_SCHEMA.extend( { cv.Required(CONF_OPTIONS): cv.ensure_list(option_string), + cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of, } ) DROPDOWN_UPDATE_SCHEMA = DROPDOWN_BASE_SCHEMA.extend( { cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string), + cv.Optional(CONF_DIR): DIRECTIONS.one_of, } ) diff --git a/esphome/components/lvgl/widgets/img.py b/esphome/components/lvgl/widgets/img.py index d9de8d821a..c3e0781489 100644 --- a/esphome/components/lvgl/widgets/img.py +++ b/esphome/components/lvgl/widgets/img.py @@ -20,8 +20,8 @@ CONF_IMAGE = "image" BASE_IMG_SCHEMA = cv.Schema( { - cv.Optional(CONF_PIVOT_X, default="50%"): size, - cv.Optional(CONF_PIVOT_Y, default="50%"): size, + cv.Optional(CONF_PIVOT_X): size, + cv.Optional(CONF_PIVOT_Y): size, cv.Optional(CONF_ANGLE): angle, cv.Optional(CONF_ZOOM): zoom, cv.Optional(CONF_OFFSET_X): size, @@ -63,10 +63,11 @@ class ImgType(WidgetType): async def to_code(self, w: Widget, config): if src := config.get(CONF_SRC): lv.img_set_src(w.obj, await lv_image.process(src)) - if (cf_angle := config.get(CONF_ANGLE)) is not None: - pivot_x = config[CONF_PIVOT_X] - pivot_y = config[CONF_PIVOT_Y] + if (pivot_x := config.get(CONF_PIVOT_X)) and ( + pivot_y := config.get(CONF_PIVOT_Y) + ): lv.img_set_pivot(w.obj, pivot_x, pivot_y) + if (cf_angle := config.get(CONF_ANGLE)) is not None: lv.img_set_angle(w.obj, cf_angle) if (img_zoom := config.get(CONF_ZOOM)) is not None: lv.img_set_zoom(w.obj, img_zoom) From 6ff180152abeacb8cb8a6afbc457c6d6772a970e Mon Sep 17 00:00:00 2001 From: Vasil Kalchev Date: Tue, 22 Apr 2025 13:39:21 +0300 Subject: [PATCH 5/5] Implement `min_power` for component `ac_dimmer` using method `trailing` (#8472) --- esphome/components/ac_dimmer/ac_dimmer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 16101a1c2c..4901719b32 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -114,13 +114,14 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() { // fully off, disable output immediately this->gate_pin.digital_write(false); } else { + auto min_us = this->cycle_time_us * this->min_power / 1000; if (this->method == DIM_METHOD_TRAILING) { this->enable_time_us = 1; // cannot be 0 - this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535); + // calculate time until disable in µs with integer arithmetic and take into account min_power + this->disable_time_us = std::max((uint32_t) 10, this->value * (this->cycle_time_us - min_us) / 65535 + min_us); } else { // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic // also take into account min_power - auto min_us = this->cycle_time_us * this->min_power / 1000; this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); if (this->method == DIM_METHOD_LEADING_PULSE) {