Merge remote-tracking branch 'upstream/dev' into integration

This commit is contained in:
J. Nick Koston
2026-03-25 08:47:35 -10:00
33 changed files with 288 additions and 120 deletions
+22
View File
@@ -124,6 +124,28 @@ This document provides essential context for AI models interacting with this pro
* **Indentation:** Use spaces (two per indentation level), not tabs
* **Type aliases:** Prefer `using type_t = int;` over `typedef int type_t;`
* **Line length:** Wrap lines at no more than 120 characters
* **Constructor parameters vs setters:** Component properties that are both **required** and **invariant**
(never change after construction) should be constructor parameters rather than set via setter methods.
This makes the dependency explicit and prevents use of the object in an incompletely-initialized state.
In code generation, when calling `cg.new_Pvariable()` or the relevant helper function to create the component, pass these as arguments.
```cpp
// Good - required invariant dependency as constructor parameter
class SourceTextSensor : public text_sensor::TextSensor, public Component {
public:
explicit SourceTextSensor(text::Text *source) : source_(source) {}
protected:
text::Text *source_;
};
```
```cpp
// Bad - required invariant dependency as setter
class SourceTextSensor : public text_sensor::TextSensor, public Component {
public:
void set_source(text::Text *source) { this->source_ = source; }
protected:
text::Text *source_{nullptr};
};
```
* **Component Structure:**
* **Standard Files:**
+1 -1
View File
@@ -204,7 +204,7 @@ async def to_code(config):
add_idf_component(
name="esphome/esp-audio-libs",
ref="2.0.3",
ref="2.0.4",
)
data = _get_data()
@@ -276,8 +276,8 @@ void BME68xBSEC2Component::run_() {
}
if (this->bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) {
uint32_t meas_dur = 0;
meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
bme68x_get_conf(&bme68x_conf, &this->bme68x_);
uint32_t meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);
this->trigger_time_ns_ = curr_time_ns;
this->set_timeout("read", meas_dur / 1000, [this]() { this->read_(this->trigger_time_ns_); });
+1
View File
@@ -13,6 +13,7 @@ CONF_DATA_BITS = "data_bits"
CONF_DRAW_ROUNDING = "draw_rounding"
CONF_ENABLED = "enabled"
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
CONF_LIBRETINY = "libretiny"
CONF_ON_PACKET = "on_packet"
CONF_ON_RECEIVE = "on_receive"
CONF_ON_STATE_CHANGE = "on_state_change"
@@ -14,6 +14,8 @@ _ESP32C3_SPI_PSRAM_PINS = {
17: "SPIQ",
}
_ESP32C3_USB_JTAG_PINS = {18, 19}
_ESP32C3_STRAPPING_PINS = {2, 8, 9}
_LOGGER = logging.getLogger(__name__)
@@ -26,6 +28,12 @@ def esp32_c3_validate_gpio_pin(value: int) -> int:
raise cv.Invalid(
f"This pin cannot be used on ESP32-C3s and is already used by the SPI/PSRAM interface (function: {_ESP32C3_SPI_PSRAM_PINS[value]})"
)
if value in _ESP32C3_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is used by the USB-Serial-JTAG interface."
" Using this pin as GPIO will conflict with USB-Serial-JTAG.",
value,
)
return value
+9 -1
View File
@@ -18,7 +18,9 @@ _ESP32C6_SPI_PSRAM_PINS = {
30: "SPID",
}
_ESP32C6_STRAPPING_PINS = {8, 9, 15}
_ESP32C6_USB_JTAG_PINS = {12, 13}
_ESP32C6_STRAPPING_PINS = {4, 5, 8, 9, 15}
_LOGGER = logging.getLogger(__name__)
@@ -30,6 +32,12 @@ def esp32_c6_validate_gpio_pin(value: int) -> int:
raise cv.Invalid(
f"This pin cannot be used on ESP32-C6s and is already used by the SPI/PSRAM interface (function: {_ESP32C6_SPI_PSRAM_PINS[value]})"
)
if value in _ESP32C6_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is used by the USB-Serial-JTAG interface."
" Using this pin as GPIO will conflict with USB-Serial-JTAG.",
value,
)
return value
+3 -3
View File
@@ -9,7 +9,7 @@ _ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21}
_ESP32H2_USB_JTAG_PINS = {26, 27}
_ESP32H2_STRAPPING_PINS = {2, 3, 8, 9, 25}
_ESP32H2_STRAPPING_PINS = {8, 9, 25}
_LOGGER = logging.getLogger(__name__)
@@ -26,8 +26,8 @@ def esp32_h2_validate_gpio_pin(value: int) -> int:
)
if value in _ESP32H2_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is reserved for the USB-Serial-JTAG interface.\n"
"To use this pin as GPIO, USB-Serial-JTAG will be disabled.",
"GPIO%d is used by the USB-Serial-JTAG interface."
" Using this pin as GPIO will conflict with USB-Serial-JTAG.",
value,
)
+2 -2
View File
@@ -20,8 +20,8 @@ def esp32_p4_validate_gpio_pin(value: int) -> int:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-54)")
if value in _ESP32P4_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is reserved for the USB-Serial-JTAG interface.\n"
"To use this pin as GPIO, USB-Serial-JTAG will be disabled.",
"GPIO%d is used by the USB-Serial-JTAG interface."
" Using this pin as GPIO will conflict with USB-Serial-JTAG.",
value,
)
+15 -7
View File
@@ -5,7 +5,7 @@ import esphome.config_validation as cv
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
from esphome.pins import check_strapping_pin
_ESP_32S3_SPI_PSRAM_PINS = {
_ESP32S3_SPI_PSRAM_PINS = {
26: "SPICS1",
27: "SPIHD",
28: "SPIWP",
@@ -15,7 +15,7 @@ _ESP_32S3_SPI_PSRAM_PINS = {
32: "SPID",
}
_ESP_32_ESP32_S3R8_PSRAM_PINS = {
_ESP32S3R8_PSRAM_PINS = {
33: "SPIIO4",
34: "SPIIO5",
35: "SPIIO6",
@@ -23,7 +23,9 @@ _ESP_32_ESP32_S3R8_PSRAM_PINS = {
37: "SPIDQS",
}
_ESP_32S3_STRAPPING_PINS = {0, 3, 45, 46}
_ESP32S3_USB_JTAG_PINS = {19, 20}
_ESP32S3_STRAPPING_PINS = {0, 3, 45, 46}
_LOGGER = logging.getLogger(__name__)
@@ -32,11 +34,11 @@ def esp32_s3_validate_gpio_pin(value: int) -> int:
if value < 0 or value > 48:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-48)")
if value in _ESP_32S3_SPI_PSRAM_PINS:
if value in _ESP32S3_SPI_PSRAM_PINS:
raise cv.Invalid(
f"This pin cannot be used on ESP32-S3s and is already used by the SPI/PSRAM interface(function: {_ESP_32S3_SPI_PSRAM_PINS[value]})"
f"This pin cannot be used on ESP32-S3s and is already used by the SPI/PSRAM interface(function: {_ESP32S3_SPI_PSRAM_PINS[value]})"
)
if value in _ESP_32_ESP32_S3R8_PSRAM_PINS:
if value in _ESP32S3R8_PSRAM_PINS:
_LOGGER.warning(
"GPIO%d is used by the PSRAM interface on ESP32-S3R8 / ESP32-S3R8V and should be avoided on these models",
value,
@@ -46,6 +48,12 @@ def esp32_s3_validate_gpio_pin(value: int) -> int:
# These pins are not exposed in GPIO mux (reason unknown)
# but they're missing from IO_MUX list in datasheet
raise cv.Invalid(f"The pin GPIO{value} is not usable on ESP32-S3s.")
if value in _ESP32S3_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is used by the USB-Serial-JTAG interface."
" Using this pin as GPIO will conflict with USB-Serial-JTAG.",
value,
)
return value
@@ -61,5 +69,5 @@ def esp32_s3_validate_supports(value: dict[str, Any]) -> dict[str, Any]:
# All ESP32 pins support input mode
pass
check_strapping_pin(value, _ESP_32S3_STRAPPING_PINS, _LOGGER)
check_strapping_pin(value, _ESP32S3_STRAPPING_PINS, _LOGGER)
return value
+1
View File
@@ -128,6 +128,7 @@ SCAN_WIRINGS = {
"STANDARD_TWO_SCAN": Hub75ScanWiring.STANDARD_TWO_SCAN,
"SCAN_1_4_16PX_HIGH": Hub75ScanWiring.SCAN_1_4_16PX_HIGH,
"SCAN_1_8_32PX_HIGH": Hub75ScanWiring.SCAN_1_8_32PX_HIGH,
"SCAN_1_8_32PX_FULL": Hub75ScanWiring.SCAN_1_8_32PX_FULL,
"SCAN_1_8_40PX_HIGH": Hub75ScanWiring.SCAN_1_8_40PX_HIGH,
"SCAN_1_8_64PX_HIGH": Hub75ScanWiring.SCAN_1_8_64PX_HIGH,
}
+1 -1
View File
@@ -229,7 +229,7 @@ void Inkplate::eink_off_() {
this->oe_pin_->digital_write(false);
this->gmod_pin_->digital_write(false);
GPIO.out &= ~(this->get_data_pin_mask_() | (1UL << this->cl_pin_->get_pin()) | (1UL << this->le_pin_->get_pin()));
GPIO.out_w1tc = this->get_data_pin_mask_() | (1UL << this->cl_pin_->get_pin()) | (1UL << this->le_pin_->get_pin());
this->ckv_pin_->digital_write(false);
this->sph_pin_->digital_write(false);
this->spv_pin_->digital_write(false);
-1
View File
@@ -14,7 +14,6 @@ class LibreTinyComponent:
supports_atomics: bool = False # True for Cortex-M4(F) with LDREX/STREX
CONF_LIBRETINY = "libretiny"
CONF_LOGLEVEL = "loglevel"
CONF_SDK_SILENT = "sdk_silent"
CONF_GPIO_RECOVER = "gpio_recover"
+2 -1
View File
@@ -1,5 +1,6 @@
import esphome.codegen as cg
from esphome.components import text_sensor
from esphome.components.const import CONF_LIBRETINY
import esphome.config_validation as cv
from esphome.const import (
CONF_VERSION,
@@ -7,7 +8,7 @@ from esphome.const import (
ICON_CELLPHONE_ARROW_DOWN,
)
from .const import CONF_LIBRETINY, LTComponent
from .const import LTComponent
DEPENDENCIES = ["libretiny"]
+1 -1
View File
@@ -31,7 +31,7 @@ GRADIENT_SCHEMA = cv.ensure_list(
cv.Required(CONF_DIRECTION): cv.one_of(
"HOR", "HORIZONTAL", "VER", "VERTICAL", upper=True
),
cv.Optional(CONF_DITHER, default="NONE"): LV_DITHER.one_of,
cv.Optional(CONF_DITHER): LV_DITHER.one_of,
cv.Required(CONF_STOPS): cv.All(
[
cv.Schema(
+3 -3
View File
@@ -43,14 +43,14 @@
on_boot:
lvgl.widget.refresh: hello_world_title_
hidden: !lambda |-
return lv_obj_get_width(lv_scr_act()) < 400;
return lv_obj_get_width(lv_screen_active()) < 400;
- checkbox:
text: Checkbox
id: hello_world_checkbox_
on_boot:
lvgl.widget.refresh: hello_world_checkbox_
hidden: !lambda |-
return lv_obj_get_width(lv_scr_act()) < 240;
return lv_obj_get_width(lv_screen_active()) < 240;
on_click:
lvgl.label.update:
id: hello_world_label_
@@ -94,7 +94,7 @@
outline_width: 0
border_width: 0
hidden: !lambda |-
return lv_obj_get_width(lv_scr_act()) < 300 && lv_obj_get_height(lv_scr_act()) < 400;
return lv_obj_get_width(lv_screen_active()) < 300 && lv_obj_get_height(lv_screen_active()) < 400;
widgets:
- label:
text_font: montserrat_14
+7 -7
View File
@@ -172,18 +172,18 @@ void LvglComponent::add_page(LvPageType *page) {
page->setup(this->pages_.size() - 1);
}
void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) {
void LvglComponent::show_page(size_t index, lv_screen_load_anim_t anim, uint32_t time) {
if (index >= this->pages_.size())
return;
this->current_page_ = index;
if (anim == LV_SCREEN_LOAD_ANIM_NONE) {
lv_scr_load(this->pages_[this->current_page_]->obj);
lv_screen_load(this->pages_[this->current_page_]->obj);
} else {
lv_scr_load_anim(this->pages_[this->current_page_]->obj, anim, time, 0, false);
lv_screen_load_anim(this->pages_[this->current_page_]->obj, anim, time, 0, false);
}
}
void LvglComponent::show_next_page(lv_scr_load_anim_t anim, uint32_t time) {
void LvglComponent::show_next_page(lv_screen_load_anim_t anim, uint32_t time) {
if (this->pages_.empty() || (this->current_page_ == this->pages_.size() - 1 && !this->page_wrap_))
return;
size_t start = this->current_page_;
@@ -195,7 +195,7 @@ void LvglComponent::show_next_page(lv_scr_load_anim_t anim, uint32_t time) {
this->show_page(this->current_page_, anim, time);
}
void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
void LvglComponent::show_prev_page(lv_screen_load_anim_t anim, uint32_t time) {
if (this->pages_.empty() || (this->current_page_ == 0 && !this->page_wrap_))
return;
size_t start = this->current_page_;
@@ -673,14 +673,14 @@ void LvglComponent::static_flush_cb(lv_display_t *disp_drv, const lv_area_t *are
* @param color_end The color to apply to the last tick
* @param width
*/
void lv_scale_draw_event_cb(lv_event_t *e, uint16_t range_start, uint16_t range_end, lv_color_t color_start,
void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_end, lv_color_t color_start,
lv_color_t color_end, int width, bool local) {
auto *scale = static_cast<lv_obj_t *>(lv_event_get_target(e));
lv_draw_task_t *task = lv_event_get_draw_task(e);
if (lv_draw_task_get_type(task) == LV_DRAW_TASK_TYPE_LINE) {
auto *line_dsc = static_cast<lv_draw_line_dsc_t *>(lv_draw_task_get_draw_dsc(task));
auto tick = line_dsc->base.id1;
int tick = line_dsc->base.id2;
if (tick >= range_start && tick <= range_end) {
unsigned range = range_end - range_start;
if (local) {
+5 -5
View File
@@ -52,7 +52,7 @@ extern std::string lv_event_code_name_for(lv_event_t *event);
lv_obj_t *lv_container_create(lv_obj_t *parent);
#ifdef USE_LVGL_SCALE
void lv_scale_draw_event_cb(lv_event_t *e, uint16_t range_start, uint16_t range_end, lv_color_t color_start,
void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_end, lv_color_t color_start,
lv_color_t color_end, int width, bool local);
#endif
#if LV_COLOR_DEPTH == 16
@@ -163,7 +163,7 @@ class LvglComponent : public PollingComponent {
static void render_end_cb(lv_event_t *event);
static void render_start_cb(lv_event_t *event);
void dump_config() override;
lv_disp_t *get_disp() { return this->disp_; }
lv_display_t *get_disp() { return this->disp_; }
lv_obj_t *get_screen_active() { return lv_display_get_screen_active(this->disp_); }
// Pause or resume the display.
// @param paused If true, pause the display. If false, resume the display.
@@ -189,9 +189,9 @@ class LvglComponent : public PollingComponent {
lv_event_code_t event3);
void add_page(LvPageType *page);
void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
void show_page(size_t index, lv_screen_load_anim_t anim, uint32_t time);
void show_next_page(lv_screen_load_anim_t anim, uint32_t time);
void show_prev_page(lv_screen_load_anim_t anim, uint32_t time);
void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
void set_big_endian(bool big_endian) { this->big_endian_ = big_endian; }
size_t get_current_page() const;
+10 -2
View File
@@ -250,9 +250,17 @@ STYLE_REMAP = {
}
def remap_property(prop):
def remap_property(prop, record=True):
"""
Remap an old style property to new style property.
Optionally record the use of the deprecated property.
:param prop: Name of the style property to remap.
:param record: Whether to record the use of the deprecated property.
:return: The remapped property name, or ``prop`` if no remapping exists.
"""
if prop in STYLE_REMAP:
get_remapped_uses().add(prop)
if record:
get_remapped_uses().add(prop)
return STYLE_REMAP[prop]
return prop
+1 -1
View File
@@ -72,7 +72,7 @@ async def generate_triggers():
dir = DIRECTIONS.mapper(dir)
w.clear_flag("LV_OBJ_FLAG_SCROLLABLE")
selected = literal(
f"lv_indev_get_gesture_dir(lv_indev_get_act()) == {dir}"
f"lv_indev_get_gesture_dir(lv_indev_active()) == {dir}"
)
await add_trigger(
conf, w, literal("LV_EVENT_GESTURE"), is_selected=selected
+1 -2
View File
@@ -59,7 +59,6 @@ lv_style_t = cg.global_ns.struct("lv_style_t")
lv_pseudo_button_t = lvgl_ns.class_("LvPseudoButton")
lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t)
lv_obj_t_ptr = lv_obj_base_t.operator("ptr")
lv_disp_t = cg.global_ns.struct("lv_disp_t")
lv_color_t = cg.global_ns.struct("lv_color_t")
lv_opa_t = cg.global_ns.struct("lv_opa_t")
lv_group_t = cg.global_ns.struct("lv_group_t")
@@ -67,7 +66,7 @@ LVTouchListener = lvgl_ns.class_("LVTouchListener")
LVEncoderListener = lvgl_ns.class_("LVEncoderListener")
lv_obj_t = LvType("lv_obj_t")
lv_page_t = LvType("LvPageType", parents=(LvCompound,))
lv_img_t = LvType("lv_img_t")
lv_image_t = LvType("lv_image_t")
lv_gradient_t = LvType("lv_grad_dsc_t")
lv_event_t = LvType("lv_event_t")
+1 -1
View File
@@ -158,7 +158,7 @@ class WidgetType:
await self.on_create(var, config)
w = Widget.create(wid, var, self, config)
if theme := theme_widget_map.get(self.w_type.name):
if theme := theme_widget_map.get(self.name):
for part, states in theme.items():
part = "LV_PART_" + part.upper()
for state, style in states.items():
+3 -1
View File
@@ -369,7 +369,9 @@ def _scale_map(config):
def _get_prop_validator(prop):
return STYLE_PROPS.get(f"transform_{remap_property(prop)}") or STYLE_PROPS.get(prop)
return STYLE_PROPS.get(
f"transform_{remap_property(prop, False)}"
) or STYLE_PROPS.get(prop)
def _prop_validator(prop):
+2 -2
View File
@@ -17,7 +17,7 @@ from ..defines import (
CONF_ZOOM,
)
from ..lv_validation import lv_angle, lv_bool, lv_image, scale, size
from ..types import lv_img_t
from ..types import lv_image_t
from . import Widget, WidgetType
from .label import CONF_LABEL
@@ -55,7 +55,7 @@ class ImgType(WidgetType):
def __init__(self):
super().__init__(
CONF_IMAGE,
lv_img_t,
lv_image_t,
(CONF_MAIN,),
IMG_SCHEMA,
IMG_MODIFY_SCHEMA,
+35 -14
View File
@@ -73,12 +73,13 @@ from ..types import (
LvType,
ObjUpdateAction,
lv_event_t,
lv_img_t,
lv_image_t,
lv_obj_t,
)
from . import Widget, WidgetType, get_widgets, widget_to_code
from .arc import CONF_ARC
from .img import CONF_IMAGE
from .label import CONF_LABEL
from .line import CONF_LINE
CONF_ANGLE_RANGE = "angle_range"
@@ -204,7 +205,7 @@ INDICATOR_SCHEMA = cv.Schema(
INDICATOR_IMG_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(lv_meter_indicator_image_t),
cv.GenerateID(CONF_IMAGE_ID): cv.declare_id(lv_img_t),
cv.GenerateID(CONF_IMAGE_ID): cv.declare_id(lv_image_t),
}
),
requires_component("image"),
@@ -222,12 +223,31 @@ INDICATOR_SCHEMA = cv.Schema(
}
)
def _scale_validate(config):
if indicators := config.get(CONF_INDICATORS):
style_index = next(
(
i
for i, indicator in enumerate(indicators)
if CONF_TICK_STYLE in indicator
),
-1,
)
if style_index >= 0 and CONF_TICKS not in config:
raise cv.Invalid(
"'tick_style' can't be applied if the enclosing scale has no 'ticks' configured",
path=[CONF_INDICATORS, style_index],
)
return config
SCALE_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(lv_scale_t),
cv.Optional(CONF_TICKS): cv.Schema(
{
cv.Optional(CONF_COUNT, default=12): cv.positive_int,
cv.Optional(CONF_COUNT, default=12): cv.int_range(min=2),
cv.Optional(CONF_WIDTH, default=2): cv.positive_int,
cv.Optional(CONF_LENGTH, default=10): size,
cv.Optional(CONF_RADIAL_OFFSET, default=0): size,
@@ -251,7 +271,7 @@ SCALE_SCHEMA = cv.Schema(
cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA),
cv.Optional(CONF_DRAW_TICKS_ON_TOP, default=True): bool,
}
)
).add_extra(_scale_validate)
METER_SCHEMA = {
cv.Optional(CONF_PIVOT): STATE_SCHEMA,
@@ -259,17 +279,14 @@ METER_SCHEMA = {
cv.Optional(CONF_SCALES): cv.ensure_list(SCALE_SCHEMA),
}
# Only handling light style at the moment
LIGHT_STYLE = LVStyle(
"lv_meter_light",
{
"bg_opa": 1.0,
"bg_color": 0xEEEEEE,
"line_width": 1,
"line_color": 0xEEEEEE,
"arc_width": 2,
"arc_color": 0xEEEEEE,
"bg_color": 0xFFFFFF,
"pad_all": 10,
"border_width": 2,
"border_width": 3,
"border_color": 0xEEEEEE,
"radius": "LV_RADIUS_CIRCLE",
},
@@ -329,7 +346,7 @@ class MeterType(WidgetType):
)
def get_uses(self):
return CONF_SCALE, CONF_LINE, CONF_IMAGE
return CONF_SCALE, CONF_LINE, CONF_IMAGE, CONF_LABEL
def validate(self, value):
return cv.has_at_most_one_key(CONF_INDICATOR, CONF_PIVOT)(value)
@@ -478,6 +495,8 @@ class MeterType(WidgetType):
await iw.set_property(CONF_SRC, await lv_image.process(src))
await set_indicator_values(iw, v)
# Hide the scale line
lv.obj_set_style_arc_opa(scale_var, LV_OPA.TRANSP, LV_PART.MAIN)
if ticks := scale_conf.get(CONF_TICKS):
# Set total tick count
lv.scale_set_total_tick_count(scale_var, ticks[CONF_COUNT])
@@ -503,8 +522,6 @@ class MeterType(WidgetType):
LV_PART.ITEMS,
)
# Hide the scale line
lv.obj_set_style_arc_opa(scale_var, LV_OPA.TRANSP, LV_PART.MAIN)
if CONF_MAJOR in ticks:
major = ticks[CONF_MAJOR]
# Set major tick frequency
@@ -547,7 +564,11 @@ class MeterType(WidgetType):
else:
lv.scale_set_major_tick_every(scale_var, 0)
else:
lv.scale_set_total_tick_count(scale_var, 0)
# Must have at least 2 ticks otherwise the scale isn't even drawn
lv.scale_set_total_tick_count(scale_var, 2)
# Hide the ticks by making them 0 width
lv_obj.set_style_line_width(scale_var, 0, LV_PART.ITEMS)
lv.scale_set_major_tick_every(scale_var, 0)
# Add a pivot
# Get the default style
+4 -4
View File
@@ -29,7 +29,7 @@ lv_tile_t = LvType("lv_tileview_tile_t")
lv_tileview_t = LvType(
"lv_tileview_t",
largs=[(lv_obj_t_ptr, "tile")],
lvalue=lambda w: w.get_property("tile_act"),
lvalue=lambda w: w.get_property("tile_active"),
has_on_value=True,
)
@@ -85,7 +85,7 @@ class TileviewType(WidgetType):
await add_widgets(tile, tile_conf)
if tiles:
# Set the first tile as active
lv_obj.set_tile_id(
lv.tileview_set_tile_by_index(
w.obj, tiles[0][CONF_COLUMN], tiles[0][CONF_ROW], literal("LV_ANIM_OFF")
)
@@ -122,11 +122,11 @@ async def tileview_select(config, action_id, template_arg, args):
async def do_select(w: Widget):
if tile := config.get(CONF_TILE_ID):
tile = await cg.get_variable(tile)
lv_obj.set_tile(w.obj, tile, literal(config[CONF_ANIMATED]))
lv.tileview_set_tile(w.obj, tile, literal(config[CONF_ANIMATED]))
else:
row = await lv_int.process(config[CONF_ROW])
column = await lv_int.process(config[CONF_COLUMN])
lv_obj.set_tile_id(
lv.tileview_set_tile_by_index(
widgets[0].obj, column, row, literal(config[CONF_ANIMATED])
)
lv.event_send(w.obj, LV_EVENT.VALUE_CHANGED, cg.nullptr)
+14 -14
View File
@@ -40,20 +40,20 @@ void MCP9600Component::setup() {
}
bool success = this->write_byte(MCP9600_REGISTER_STATUS, 0x00);
success |= this->write_byte(MCP9600_REGISTER_SENSOR_CONFIG, uint8_t(0x00 | thermocouple_type_ << 4));
success |= this->write_byte(MCP9600_REGISTER_CONFIG, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT1_CONFIG, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT2_CONFIG, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT3_CONFIG, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT4_CONFIG, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT1_HYSTERESIS, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT2_HYSTERESIS, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT3_HYSTERESIS, 0x00);
success |= this->write_byte(MCP9600_REGISTER_ALERT4_HYSTERESIS, 0x00);
success |= this->write_byte_16(MCP9600_REGISTER_ALERT1_LIMIT, 0x0000);
success |= this->write_byte_16(MCP9600_REGISTER_ALERT2_LIMIT, 0x0000);
success |= this->write_byte_16(MCP9600_REGISTER_ALERT3_LIMIT, 0x0000);
success |= this->write_byte_16(MCP9600_REGISTER_ALERT4_LIMIT, 0x0000);
success &= this->write_byte(MCP9600_REGISTER_SENSOR_CONFIG, uint8_t(0x00 | thermocouple_type_ << 4));
success &= this->write_byte(MCP9600_REGISTER_CONFIG, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT1_CONFIG, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT2_CONFIG, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT3_CONFIG, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT4_CONFIG, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT1_HYSTERESIS, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT2_HYSTERESIS, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT3_HYSTERESIS, 0x00);
success &= this->write_byte(MCP9600_REGISTER_ALERT4_HYSTERESIS, 0x00);
success &= this->write_byte_16(MCP9600_REGISTER_ALERT1_LIMIT, 0x0000);
success &= this->write_byte_16(MCP9600_REGISTER_ALERT2_LIMIT, 0x0000);
success &= this->write_byte_16(MCP9600_REGISTER_ALERT3_LIMIT, 0x0000);
success &= this->write_byte_16(MCP9600_REGISTER_ALERT4_LIMIT, 0x0000);
if (!success) {
this->error_code_ = FAILED_TO_UPDATE_CONFIGURATION;
+4 -4
View File
@@ -50,10 +50,10 @@ bool Nextion::check_connect_() {
return true;
#ifdef USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
ESP_LOGW(TAG, "Connected (no handshake)"); // Log the connection status without handshake
this->is_connected_ = true; // Set the connection status to true
return true; // Return true indicating the connection is set
#else // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
ESP_LOGW(TAG, "Connected (no handshake)"); // Log the connection status without handshake
this->connection_state_.is_connected_ = true; // Set the connection status to true
return true; // Return true indicating the connection is set
#else // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
if (this->comok_sent_ == 0) {
this->reset_(false);
+2 -2
View File
@@ -1160,13 +1160,13 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
/** Add a callback to be notified when the nextion changes pages.
*
* @param callback The void(std::string) callback.
* @param callback The void(uint8_t) callback.
*/
template<typename F> void add_new_page_callback(F &&callback) { this->page_callback_.add(std::forward<F>(callback)); }
/** Add a callback to be notified when Nextion has a touch event.
*
* @param callback The void() callback.
* @param callback The void(uint8_t, uint8_t, bool) callback.
*/
template<typename F> void add_touch_event_callback(F &&callback) {
this->touch_callback_.add(std::forward<F>(callback));
+7
View File
@@ -115,6 +115,13 @@
#define SNTP_SERVER_COUNT 3
#define USE_MEDIA_PLAYER
#define USE_MEDIA_SOURCE
#define USE_NEXTION_COMMAND_SPACING
#define USE_NEXTION_CONF_START_UP_PAGE
#define USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
#define USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START
#define USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE
#define USE_NEXTION_MAX_COMMANDS_PER_LOOP
#define USE_NEXTION_MAX_QUEUE_SIZE
#define USE_NEXTION_TFT_UPLOAD
#define USE_NUMBER
#define USE_OUTPUT
+1 -1
View File
@@ -2,7 +2,7 @@ dependencies:
bblanchon/arduinojson:
version: "7.4.2"
esphome/esp-audio-libs:
version: 2.0.3
version: 2.0.4
esphome/micro-opus:
version: 0.3.6
espressif/esp-dsp:
@@ -0,0 +1,79 @@
#include <benchmark/benchmark.h>
#include "esphome/components/sensor/sensor.h"
namespace esphome::benchmarks {
// Inner iteration count to amortize CodSpeed instrumentation overhead.
// Without this, the ~60ns per-iteration valgrind start/stop cost dominates
// sub-microsecond benchmarks.
static constexpr int kInnerIterations = 2000;
// Test subclass to access protected configure_entity_() for benchmark setup.
class TestSensor : public sensor::Sensor {
public:
void configure(const char *name) { this->configure_entity_(name, 0x12345678, 0); }
};
// --- Sensor::publish_state() with no callbacks registered ---
// Measures baseline publish overhead: state assignment, logging,
// internal_send_state_to_frontend, ControllerRegistry notification.
static void SensorPublish_NoCallbacks(benchmark::State &state) {
TestSensor sensor;
sensor.configure("test_sensor");
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(static_cast<float>(i));
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(SensorPublish_NoCallbacks);
// --- Sensor::publish_state() with one state callback ---
// Measures callback dispatch overhead through LazyCallbackManager.
static void SensorPublish_WithCallback(benchmark::State &state) {
TestSensor sensor;
sensor.configure("test_sensor");
float callback_value = 0.0f;
sensor.add_on_state_callback([&callback_value](float value) { callback_value = value; });
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(static_cast<float>(i));
}
benchmark::DoNotOptimize(callback_value);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(SensorPublish_WithCallback);
// --- Sensor::publish_state() with the same value every time ---
// Steady-state pattern: sensor reports an unchanged reading.
// Sensor doesn't dedup today, so this exercises the same code path
// as changing values, but tracks the common real-world pattern
// separately for regression detection.
static void SensorPublish_SameValue(benchmark::State &state) {
TestSensor sensor;
sensor.configure("test_sensor");
// Warm up so has_state is already set
sensor.publish_state(23.5f);
for (auto _ : state) {
for (int i = 0; i < kInnerIterations; i++) {
sensor.publish_state(23.5f);
}
benchmark::DoNotOptimize(sensor.state);
}
state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(SensorPublish_SameValue);
} // namespace esphome::benchmarks
+11 -20
View File
@@ -43,9 +43,6 @@ lvgl:
start_value: 0
end_value: 180
bg_color: light_blue
disp_bg_color: color_id
disp_bg_image: cat_image
disp_bg_opa: cover
bottom_layer:
widgets:
- obj:
@@ -58,7 +55,6 @@ lvgl:
gradients:
- id: color_bar
direction: hor
# dither: err_diff
stops:
- color: 0xFF0000
position: 0
@@ -143,12 +139,11 @@ lvgl:
body:
text: This is a sample messagebox
bg_color: 0x808080
button_style:
bg_color: 0xff00
border_width: 4
buttons:
- id: msgbox_button
text: Button
bg_color: 0x00ff00
border_width: 4
- id: msgbox_apply
text: "Close"
on_click:
@@ -160,8 +155,8 @@ lvgl:
bg_opa: !lambda return 0.5;
- lvgl.image.update:
id: lv_image
zoom: !lambda return 512;
angle: !lambda return 100;
scale: !lambda return 512;
rotation: !lambda return 100;
pivot_x: !lambda return 20;
pivot_y: !lambda return 20;
offset_x: !lambda return 20;
@@ -287,8 +282,8 @@ lvgl:
then:
- lvgl.animimg.stop: anim_img
- lvgl.update:
disp_bg_color: 0xffff00
disp_bg_image: none
bottom_layer:
bg_color: 0xffff00
- lvgl.widget.show: message_box
- label:
text: "Hello shiny day"
@@ -361,8 +356,6 @@ lvgl:
pad_right: 10px
pad_top: 10px
shadow_color: light_blue
shadow_ofs_x: 5
shadow_ofs_y: 5
shadow_opa: cover
shadow_spread: 5
shadow_width: 10
@@ -373,12 +366,10 @@ lvgl:
text_letter_space: 4
text_line_space: 4
text_opa: cover
transform_angle: 180
transform_rotation: 90
transform_height: 100
transform_pivot_x: 50%
transform_pivot_y: 50%
transform_zoom: 0.5
transform_scale: 2.0
transform_scale_x: 1.5
transform_scale_y: 0.8
@@ -470,11 +461,11 @@ lvgl:
id: button_button
width: 20%
height: 10%
transform_angle: !lambda return(180*100);
transform_rotation: !lambda return(180*100);
arc_width: !lambda return 4;
border_width: !lambda return 6;
shadow_ofs_x: !lambda return 6;
shadow_ofs_y: !lambda return 6;
shadow_offset_x: !lambda return 6;
shadow_offset_y: !lambda return 6;
shadow_spread: !lambda return 6;
shadow_width: !lambda return 6;
pressed:
@@ -646,8 +637,8 @@ lvgl:
border_opa: 80%
shadow_color: black
shadow_width: 10
shadow_ofs_x: 5
shadow_ofs_y: 5
shadow_offset_x: 5
shadow_offset_y: 5
shadow_spread: 4
shadow_opa: cover
outline_color: red
+30 -17
View File
@@ -273,26 +273,39 @@ text_sensor:
display:
- platform: nextion
id: main_lcd
auto_wake_on_touch: true
brightness: 80%
command_spacing: 5ms
dump_device_info: true
exit_reparse_on_start: true
lambda: |-
ESP_LOGD("display","Display is being tested!");
max_commands_per_loop: 20
max_queue_age: 5000ms # Remove queue items after 5s
max_queue_size: 50
update_interval: 5s
on_sleep:
then:
lambda: 'ESP_LOGD("display","Display went to sleep");'
on_wake:
then:
lambda: 'ESP_LOGD("display","Display woke up");'
on_setup:
then:
lambda: 'ESP_LOGD("display","Display setup completed");'
on_page:
then:
lambda: 'ESP_LOGD("display","Display shows new page %u", x);'
on_buffer_overflow:
then:
logger.log: "Nextion reported a buffer overflow!"
command_spacing: 5ms
dump_device_info: true
max_queue_age: 5000ms # Remove queue items after 5s
on_page:
then:
lambda: 'ESP_LOGD("display","Display shows new page %u", x);'
on_setup:
then:
lambda: 'ESP_LOGD("display","Display setup completed");'
on_sleep:
then:
lambda: 'ESP_LOGD("display","Display went to sleep");'
on_touch:
then:
lambda: |-
ESP_LOGD("display",
"Display was touched at page %u, component %u, touch event: %s",
page_id, component_id, touch_event ? "press" : "release");
on_wake:
then:
lambda: 'ESP_LOGD("display","Display woke up");'
update_interval: 5s
start_up_page: 1
startup_override_ms: 10000ms # Wait 10s for display ready
touch_sleep_timeout: 3
wake_up_page: 2