mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
[lvgl] Migrate to 9.6.0 (#20257)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ from esphome.components.esp32 import (
|
||||
add_idf_component,
|
||||
add_idf_sdkconfig_option,
|
||||
get_esp32_variant,
|
||||
idf_version,
|
||||
)
|
||||
from esphome.components.image import (
|
||||
CONF_OPAQUE,
|
||||
@@ -158,7 +159,7 @@ def as_macro(macro, value):
|
||||
return f"#define {macro} {value}"
|
||||
|
||||
|
||||
LVGL_VERSION = "9.5.0"
|
||||
LVGL_VERSION = "9.6.0"
|
||||
LV_CONF_FILENAME = "lv_conf.h"
|
||||
LV_CONF_H_FORMAT = """\
|
||||
#pragma once
|
||||
@@ -324,10 +325,16 @@ async def to_code(configs):
|
||||
# Skip compiling lvgl examples
|
||||
add_idf_sdkconfig_option("CONFIG_LV_BUILD_EXAMPLES", False)
|
||||
add_idf_sdkconfig_option("CONFIG_LV_BUILD_DEMOS", False)
|
||||
# Match LV_USE_STDLIB_MALLOC in lv_conf.h. This also drops the builtin malloc
|
||||
# options, which LVGL 9.5 builds leave set to deprecated values.
|
||||
add_idf_sdkconfig_option("CONFIG_LV_USE_CUSTOM_MALLOC", True)
|
||||
if get_esp32_variant() == VARIANT_ESP32P4:
|
||||
add_idf_sdkconfig_option("CONFIG_LV_DRAW_BUF_ALIGN", 64)
|
||||
# disable use of PPA for fills until upstream bugs fixed
|
||||
df.add_define("LV_USE_PPA", "0")
|
||||
# The LVGL PPA driver uses cache functions added in ESP-IDF 6.0
|
||||
if idf_version() >= cv.Version(6, 0, 0):
|
||||
df.add_define("LV_USE_PPA", "1")
|
||||
else:
|
||||
df.add_define("LV_USE_PPA", "0")
|
||||
df.add_define("LV_DRAW_BUF_ALIGN", "64")
|
||||
else:
|
||||
df.add_define("LV_DRAW_BUF_ALIGN", "32")
|
||||
@@ -337,6 +344,8 @@ async def to_code(configs):
|
||||
cg.add_library("lvgl/lvgl", LVGL_VERSION)
|
||||
df.add_define("LV_DRAW_BUF_STRIDE_ALIGN", "1")
|
||||
df.add_define("LV_USE_DRAW_SW", "1")
|
||||
# LVGL 9.6 rejects 0 here unless tiled rendering is disabled
|
||||
df.add_define("LV_DRAW_SW_DRAW_UNIT_CNT", "1")
|
||||
df.add_define("LV_USE_STDLIB_SPRINTF", "LV_STDLIB_CLIB")
|
||||
df.add_define("LV_USE_STDLIB_STRING", "LV_STDLIB_CLIB")
|
||||
df.add_define("LV_USE_STDLIB_MALLOC", "LV_STDLIB_CUSTOM")
|
||||
@@ -356,15 +365,22 @@ async def to_code(configs):
|
||||
"LVGL_LOG_LEVEL",
|
||||
cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config_0[CONF_LOG_LEVEL]}"),
|
||||
)
|
||||
df.add_define("LV_COLOR_DEPTH", config_0[CONF_COLOR_DEPTH])
|
||||
# Big-endian displays get LVGL to render directly in swapped byte order
|
||||
display_format = (
|
||||
"RGB565_SWAPPED" if config_0[CONF_BYTE_ORDER] == BYTE_ORDER_BIG else "RGB565"
|
||||
)
|
||||
df.add_define("LV_COLOR_FORMAT_DEFAULT", f"LV_COLOR_FORMAT_{display_format}")
|
||||
for font in df.get_lv_fonts_used():
|
||||
df.add_define(f"LV_FONT_{font.upper()}")
|
||||
|
||||
if config_0[CONF_COLOR_DEPTH] == 16:
|
||||
df.add_define("LV_OBJ_STYLE_CACHE", "1")
|
||||
if any(config[df.CONF_CHECK_ARGS] for config in configs):
|
||||
df.add_define("LV_USE_CHECK_ARG", "1")
|
||||
df.add_define(
|
||||
"LV_COLOR_16_SWAP",
|
||||
"1" if config_0[CONF_BYTE_ORDER] == "big_endian" else "0",
|
||||
"LV_CHECK_ARG_LOG_MODE",
|
||||
f"LV_CHECK_ARG_LOG_MODE_{df.LV_CHECK_ARG_LOG_MODES[config_0[CONF_LOG_LEVEL]]}",
|
||||
)
|
||||
else:
|
||||
df.add_define("LV_USE_CHECK_ARG", "0")
|
||||
df.add_define(
|
||||
"LV_COLOR_CHROMA_KEY",
|
||||
await lvalid.lv_color.process(config_0[df.CONF_TRANSPARENCY_KEY]),
|
||||
@@ -504,8 +520,9 @@ async def to_code(configs):
|
||||
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"}
|
||||
# The display buffer format is always needed, RGB565 is used by canvas buffers and
|
||||
# ARGB8888 for layer blending
|
||||
lv_image_formats = {"RGB565", display_format, "ARGB8888"}
|
||||
|
||||
for image_id in get_lv_images_used():
|
||||
await cg.get_variable(image_id)
|
||||
@@ -519,7 +536,7 @@ async def to_code(configs):
|
||||
if image_type == ImageRGB565:
|
||||
lv_image_formats.add("RGB565A8" if transparent else "RGB565")
|
||||
if image_type == ImageRGB:
|
||||
lv_image_formats.add("ARGB8888" if transparent else "RGB8888")
|
||||
lv_image_formats.add("ARGB8888" if transparent else "RGB888")
|
||||
if df.is_defined("LV_GRADIENT_MAX_STOPS"):
|
||||
lv_image_formats.add("RGB888")
|
||||
for fmt in lv_image_formats:
|
||||
@@ -595,6 +612,7 @@ LVGL_TOP_LEVEL_SCHEMA = (
|
||||
cv.Optional(CONF_LOG_LEVEL, default="WARN"): cv.one_of(
|
||||
*df.LV_LOG_LEVELS, upper=True
|
||||
),
|
||||
cv.Optional(df.CONF_CHECK_ARGS, default=False): cv.boolean,
|
||||
cv.Optional(CONF_BYTE_ORDER): cv.one_of(
|
||||
"big_endian", "little_endian", lower=True
|
||||
),
|
||||
|
||||
@@ -8,6 +8,7 @@ import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ACTION,
|
||||
CONF_GROUP,
|
||||
CONF_HIDDEN,
|
||||
CONF_ID,
|
||||
CONF_POSITION,
|
||||
CONF_ROTATION,
|
||||
@@ -354,7 +355,7 @@ async def obj_enable_to_code(config, action_id, template_arg, args):
|
||||
)
|
||||
async def obj_hide_to_code(config, action_id, template_arg, args):
|
||||
async def do_hide(widget: Widget):
|
||||
widget.add_flag("LV_OBJ_FLAG_HIDDEN")
|
||||
widget.set_flag(CONF_HIDDEN, True)
|
||||
|
||||
widgets = [widget.outer or widget for widget in await get_widgets(config)]
|
||||
return await action_to_code(widgets, do_hide, action_id, template_arg, args)
|
||||
@@ -365,7 +366,7 @@ async def obj_hide_to_code(config, action_id, template_arg, args):
|
||||
)
|
||||
async def obj_show_to_code(config, action_id, template_arg, args):
|
||||
async def do_show(widget: Widget):
|
||||
widget.clear_flag("LV_OBJ_FLAG_HIDDEN")
|
||||
widget.set_flag(CONF_HIDDEN, False)
|
||||
if widget.move_to_foreground:
|
||||
lv_obj.move_foreground(widget.obj)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from esphome.components.binary_sensor import (
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_STATE
|
||||
|
||||
from ..defines import CONF_WIDGET, LV_OBJ_FLAG, LvConstant
|
||||
from ..defines import CONF_CLICKABLE, CONF_WIDGET, LvConstant
|
||||
from ..lvcode import EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext, lvgl_static
|
||||
from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t
|
||||
from ..widgets import Widget, get_widgets
|
||||
@@ -41,7 +41,7 @@ async def to_code(config):
|
||||
ctx.add(sensor.publish_initial_state(test_expr))
|
||||
if is_pressed:
|
||||
events = [LV_EVENT.PRESSED, LV_EVENT.RELEASED]
|
||||
widget.add_flag(LV_OBJ_FLAG.CLICKABLE)
|
||||
widget.set_flag(CONF_CLICKABLE, True)
|
||||
else:
|
||||
events = [LV_EVENT.VALUE_CHANGED, UPDATE_EVENT]
|
||||
ctx.add(
|
||||
|
||||
@@ -477,6 +477,16 @@ LV_LOG_LEVELS = {
|
||||
"NONE": "NONE",
|
||||
}
|
||||
|
||||
# Log output for failed LVGL argument checks, chosen from the LVGL log level
|
||||
LV_CHECK_ARG_LOG_MODES = {
|
||||
"VERBOSE": "VERBOSE",
|
||||
"DEBUG": "VERBOSE",
|
||||
"INFO": "MINIMAL",
|
||||
"WARN": "MINIMAL",
|
||||
"ERROR": "NONE",
|
||||
"NONE": "NONE",
|
||||
}
|
||||
|
||||
LV_LONG_MODES = LvConstant(
|
||||
"LV_LABEL_LONG_",
|
||||
"WRAP",
|
||||
@@ -606,13 +616,20 @@ OBJ_FLAGS = (
|
||||
"ignore_layout",
|
||||
"floating",
|
||||
"overflow_visible",
|
||||
"send_draw_task_events",
|
||||
"event_trickle",
|
||||
"state_trickle",
|
||||
"radio_button",
|
||||
"flex_in_new_track",
|
||||
"layout_1",
|
||||
"layout_2",
|
||||
"send_draw_task_events",
|
||||
"widget_1",
|
||||
"widget_2",
|
||||
)
|
||||
LV_OBJ_FLAG = LvConstant("LV_OBJ_FLAG_", *OBJ_FLAGS)
|
||||
# Flags whose lv_obj_set_<flag>() setter has a different name
|
||||
FLAG_SETTER_ALIASES = {"layout_1": "flex_in_new_track"}
|
||||
# Custom flags that have no lv_obj_set_<flag>() setter
|
||||
CUSTOM_FLAGS = ("layout_2", "widget_1", "widget_2")
|
||||
|
||||
ARC_MODES = LvConstant("LV_ARC_MODE_", "NORMAL", "REVERSE", "SYMMETRICAL")
|
||||
BAR_MODES = LvConstant("LV_BAR_MODE_", "NORMAL", "SYMMETRICAL", "RANGE")
|
||||
@@ -703,6 +720,7 @@ CONF_DISP_BG_OPA = "disp_bg_opa"
|
||||
CONF_BODY = "body"
|
||||
CONF_BUTTONS = "buttons"
|
||||
CONF_CHANGE_RATE = "change_rate"
|
||||
CONF_CLICKABLE = "clickable"
|
||||
CONF_CLOSE_BUTTON = "close_button"
|
||||
CONF_COLOR_END = "color_end"
|
||||
CONF_COLOR_START = "color_start"
|
||||
@@ -727,6 +745,7 @@ CONF_FLEX_ALIGN_CROSS = "flex_align_cross"
|
||||
CONF_FLEX_ALIGN_TRACK = "flex_align_track"
|
||||
CONF_FLEX_GROW = "flex_grow"
|
||||
CONF_FREEZE = "freeze"
|
||||
CONF_CHECK_ARGS = "check_args"
|
||||
CONF_DARK_MODE = "dark_mode"
|
||||
CONF_FULL_REFRESH = "full_refresh"
|
||||
CONF_GRADIENTS = "gradients"
|
||||
@@ -795,6 +814,7 @@ CONF_SCALE = "scale"
|
||||
CONF_SCALE_LINES = "scale_lines"
|
||||
CONF_SCROLLBAR_MODE = "scrollbar_mode"
|
||||
CONF_SCROLL_DIR = "scroll_dir"
|
||||
CONF_SCROLLABLE = "scrollable"
|
||||
CONF_SCROLL_SNAP_X = "scroll_snap_x"
|
||||
CONF_SCROLL_SNAP_Y = "scroll_snap_y"
|
||||
CONF_SELECTED_INDEX = "selected_index"
|
||||
@@ -891,7 +911,8 @@ def get_part_state_selector(part: str, state: str) -> MockObj:
|
||||
|
||||
# fmt: off
|
||||
LV_COLOR_FORMATS = (
|
||||
"RGB565", "SWAPPED", "RGB565A8", "RGB888", "XRGB8888", "ARGB8888", "PREMULTIPLIED", "L8", "AL88", "A8", "I1",
|
||||
"RGB565", "RGB565_SWAPPED", "RGB565A8", "RGB888", "XRGB8888", "ARGB8888", "ARGB8888_PREMULTIPLIED", "L8", "AL88",
|
||||
"A8", "I1",
|
||||
)
|
||||
|
||||
LV_DEFINES = (
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core/lv_global.h"
|
||||
#include "core/lv_obj_class_private.h"
|
||||
#include "core/lv_obj_private.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
@@ -225,6 +226,36 @@ void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_ev
|
||||
add_event_cb(obj, callback, event3, user_data);
|
||||
}
|
||||
|
||||
void LvglComponent::lv_obj_set_custom_flag(lv_obj_t *obj, lv_obj_flag_t flag, bool value) {
|
||||
// LVGL 9.6 keeps these flags in spec_attr->user_flags, in the same bits as its deprecated lv_obj_add_flag()
|
||||
uint8_t mask;
|
||||
switch (flag) {
|
||||
case LV_OBJ_FLAG_LAYOUT_2:
|
||||
mask = 1 << 1;
|
||||
break;
|
||||
case LV_OBJ_FLAG_WIDGET_1:
|
||||
mask = 1 << 2;
|
||||
break;
|
||||
case LV_OBJ_FLAG_WIDGET_2:
|
||||
mask = 1 << 3;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (value) {
|
||||
lv_obj_spec_attr_t *spec_attr = lv_obj_allocate_spec_attr(obj);
|
||||
if (spec_attr == nullptr)
|
||||
return;
|
||||
spec_attr->user_flags |= mask;
|
||||
} else if (obj->spec_attr != nullptr) {
|
||||
obj->spec_attr->user_flags &= ~mask;
|
||||
}
|
||||
if (flag == LV_OBJ_FLAG_LAYOUT_2) {
|
||||
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
|
||||
lv_obj_mark_layout_as_dirty(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void LvglComponent::add_page(LvPageType *page) {
|
||||
this->pages_.push_back(page);
|
||||
page->set_parent(this);
|
||||
@@ -822,7 +853,7 @@ void LvglComponent::setup() {
|
||||
}
|
||||
this->draw_buf_ = static_cast<uint8_t *>(buffer);
|
||||
this->set_resolution_();
|
||||
lv_display_set_color_format(this->disp_, LV_COLOR_FORMAT_RGB565);
|
||||
lv_display_set_color_format(this->disp_, LV_COLOR_FORMAT_DEFAULT);
|
||||
lv_display_set_flush_cb(this->disp_, static_flush_cb);
|
||||
lv_display_set_user_data(this->disp_, this);
|
||||
lv_display_add_event_cb(this->disp_, rounder_cb, LV_EVENT_INVALIDATE_AREA, this);
|
||||
@@ -994,7 +1025,7 @@ lv_point_t LvglComponent::get_touch_relative_to_obj(lv_obj_t *obj) {
|
||||
|
||||
static void lv_container_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj) {
|
||||
LV_TRACE_OBJ_CREATE("begin");
|
||||
lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_set_clickable(obj, false);
|
||||
LV_UNUSED(class_p);
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,9 @@ class LvglComponent final : public PollingComponent {
|
||||
static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
|
||||
lv_event_code_t event3, void *user_data = nullptr);
|
||||
|
||||
// Set or clear LV_OBJ_FLAG_LAYOUT_2, LV_OBJ_FLAG_WIDGET_1 or LV_OBJ_FLAG_WIDGET_2, which have no setter in LVGL 9.6
|
||||
static void lv_obj_set_custom_flag(lv_obj_t *obj, lv_obj_flag_t flag, bool value);
|
||||
|
||||
// change the state of a widget and fire an event if changed (only needed for CHECKED)
|
||||
|
||||
static void lv_obj_set_state_value(lv_obj_t *obj, lv_state_t state, bool value) {
|
||||
|
||||
@@ -391,7 +391,6 @@ SET_STATE_SCHEMA = cv.Schema(
|
||||
)
|
||||
# Setting object flags
|
||||
FLAG_SCHEMA = cv.Schema({cv.Optional(flag): lvalid.lv_bool for flag in df.OBJ_FLAGS})
|
||||
FLAG_LIST = cv.ensure_list(df.LV_OBJ_FLAG.one_of)
|
||||
|
||||
VALUE_TRIGGER_SCHEMA = {
|
||||
cv.Optional(CONF_TRIGGER, default=CONF_ON_VALUE): cv.one_of(
|
||||
|
||||
@@ -17,6 +17,8 @@ from .defines import (
|
||||
CONF_ALIGN,
|
||||
CONF_ALIGN_TO,
|
||||
CONF_ALIGN_TO_LAMBDA_ID,
|
||||
CONF_CLICKABLE,
|
||||
CONF_SCROLLABLE,
|
||||
DIRECTIONS,
|
||||
LV_DISPLAY_EVENT_MAP,
|
||||
LV_DISPLAY_EVENT_TRIGGERS,
|
||||
@@ -72,7 +74,7 @@ async def generate_triggers():
|
||||
event: conf for event, conf in config.items() if event in all_triggers
|
||||
}.items():
|
||||
conf = conf[0]
|
||||
w.add_flag("LV_OBJ_FLAG_CLICKABLE")
|
||||
w.set_flag(CONF_CLICKABLE, True)
|
||||
await add_trigger(conf, w, event)
|
||||
|
||||
for event, conf in {
|
||||
@@ -82,7 +84,7 @@ async def generate_triggers():
|
||||
dir = event[9:].upper()
|
||||
dir = {"UP": "TOP", "DOWN": "BOTTOM"}.get(dir, dir)
|
||||
dir = DIRECTIONS.mapper(dir)
|
||||
w.clear_flag("LV_OBJ_FLAG_SCROLLABLE")
|
||||
w.set_flag(CONF_SCROLLABLE, False)
|
||||
selected = literal(
|
||||
f"lv_indev_get_gesture_dir(lv_indev_active()) == {dir}"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
@@ -37,6 +37,8 @@ from ..defines import (
|
||||
CONF_SCALE,
|
||||
CONF_STYLES,
|
||||
CONF_WIDGETS,
|
||||
CUSTOM_FLAGS,
|
||||
FLAG_SETTER_ALIASES,
|
||||
LOGGER,
|
||||
OBJ_FLAGS,
|
||||
PARTS,
|
||||
@@ -47,13 +49,11 @@ from ..defines import (
|
||||
get_part_state_selector,
|
||||
get_styles_used,
|
||||
get_widget_map,
|
||||
join_enums,
|
||||
literal,
|
||||
next_debug_outline_color,
|
||||
)
|
||||
from ..lv_validation import lv_int
|
||||
from ..lvcode import (
|
||||
LvConditional,
|
||||
add_line_marks,
|
||||
lv,
|
||||
lv_add,
|
||||
@@ -295,7 +295,9 @@ class Widget:
|
||||
This class has a lot of methods. Adding any more runs foul of lint checks ("too many public methods").
|
||||
"""
|
||||
|
||||
def __init__(self, var, wtype: WidgetType, config: dict = None):
|
||||
def __init__(
|
||||
self, var: MockObj | ID, wtype: WidgetType, config: dict | None = None
|
||||
) -> None:
|
||||
self.var = var
|
||||
self.type = wtype
|
||||
self.config = config
|
||||
@@ -314,39 +316,50 @@ class Widget:
|
||||
self.parent = None
|
||||
|
||||
@staticmethod
|
||||
def create(name, var, wtype: WidgetType, config: dict = None):
|
||||
def create(
|
||||
name: ID | str, var: MockObj, wtype: WidgetType, config: dict | None = None
|
||||
) -> "Widget":
|
||||
w = Widget(var, wtype, config)
|
||||
get_widget_map()[name] = w
|
||||
return w
|
||||
|
||||
def set_state(self, state: MockObj, value: bool | Expression):
|
||||
def set_state(self, state: MockObj, value: bool | Expression) -> None:
|
||||
lv_add(lvgl_static.lv_obj_set_state_value(self.obj, state, value))
|
||||
|
||||
def has_state(self, state: MockObj):
|
||||
def has_state(self, state: MockObj) -> MockObj:
|
||||
return lv_expr.obj_has_state(self.obj, state)
|
||||
|
||||
def is_pressed(self):
|
||||
def is_pressed(self) -> MockObj:
|
||||
return self.has_state(LV_STATE.PRESSED)
|
||||
|
||||
def is_checked(self):
|
||||
def is_checked(self) -> MockObj:
|
||||
return self.has_state(LV_STATE.CHECKED)
|
||||
|
||||
def add_flag(self, flag):
|
||||
if "|" in flag:
|
||||
flag = f"(lv_obj_flag_t)({flag})"
|
||||
return lv_obj.add_flag(self.obj, literal(flag))
|
||||
def set_flag(self, flag: str, value: bool | Expression) -> None:
|
||||
"""
|
||||
Set or clear an object flag, given as the lower case name without the LV_OBJ_FLAG_ prefix
|
||||
"""
|
||||
flag = FLAG_SETTER_ALIASES.get(flag, flag)
|
||||
if flag in CUSTOM_FLAGS:
|
||||
lv_add(
|
||||
lvgl_static.lv_obj_set_custom_flag(
|
||||
self.obj, literal(f"LV_OBJ_FLAG_{flag.upper()}"), value
|
||||
)
|
||||
)
|
||||
else:
|
||||
lv_obj.call(f"set_{flag}", self.obj, value)
|
||||
|
||||
def clear_flag(self, flag):
|
||||
if "|" in flag:
|
||||
flag = f"(lv_obj_flag_t)({flag})"
|
||||
return lv_obj.remove_flag(self.obj, literal(flag))
|
||||
|
||||
def add_style(self, style_id, state=LV_STATE.DEFAULT):
|
||||
def add_style(self, style_id: ID, state: MockObj | str = LV_STATE.DEFAULT) -> None:
|
||||
lv_obj.add_style(self.obj, MockObj(style_id), literal(state))
|
||||
|
||||
async def set_property(
|
||||
self, prop, value, animated: bool = None, lv_name=None, processor=None
|
||||
):
|
||||
self,
|
||||
prop: str,
|
||||
value: Any,
|
||||
animated: bool | None = None,
|
||||
lv_name: str | None = None,
|
||||
processor: LValidator | Callable[[Any], Awaitable[Any]] | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Set a property of the widget.
|
||||
:param prop: The property name
|
||||
@@ -387,11 +400,13 @@ class Widget:
|
||||
literal("LV_ANIM_ON" if animated else "LV_ANIM_OFF"),
|
||||
)
|
||||
|
||||
def get_property(self, prop, ltype=None):
|
||||
def get_property(self, prop: str, ltype: str | None = None) -> cg.RawExpression:
|
||||
ltype = ltype or self.__type_base()
|
||||
return cg.RawExpression(f"lv_{ltype}_get_{prop}({self.obj})")
|
||||
|
||||
def set_style(self, prop: str, value, state=LV_STATE.DEFAULT):
|
||||
def set_style(
|
||||
self, prop: str, value: Any, state: MockObj | str = LV_STATE.DEFAULT
|
||||
) -> None:
|
||||
if value is None:
|
||||
return
|
||||
get_styles_used().add(prop)
|
||||
@@ -399,22 +414,22 @@ class Widget:
|
||||
value = literal(value)
|
||||
lv.call(f"obj_set_style_{prop}", self.obj, value, state)
|
||||
|
||||
def __type_base(self):
|
||||
def __type_base(self) -> str:
|
||||
wtype = self.type.w_type
|
||||
base = str(wtype)
|
||||
if base.startswith("Lv"):
|
||||
return f"{wtype}".removeprefix("Lv").removesuffix("Type").lower()
|
||||
return f"{wtype}".removeprefix("lv_").removesuffix("_t")
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return f"({self.var}, {self.type})"
|
||||
|
||||
def get_args(self):
|
||||
def get_args(self) -> list[tuple[MockObj, str]]:
|
||||
if isinstance(self.type.w_type, LvType):
|
||||
return self.type.w_type.args.copy()
|
||||
return [(lv_obj_t_ptr, "obj")]
|
||||
|
||||
def get_value(self):
|
||||
def get_value(self) -> Expression:
|
||||
if isinstance(self.type.w_type, LvType):
|
||||
result = self.type.w_type.value(self)
|
||||
if isinstance(result, list):
|
||||
@@ -422,7 +437,7 @@ class Widget:
|
||||
return result
|
||||
return self.obj
|
||||
|
||||
def get_values(self):
|
||||
def get_values(self) -> list[Expression]:
|
||||
if isinstance(self.type.w_type, LvType):
|
||||
result = self.type.w_type.value(self)
|
||||
if isinstance(result, list):
|
||||
@@ -430,13 +445,13 @@ class Widget:
|
||||
return [result]
|
||||
return [self.obj]
|
||||
|
||||
def get_number_value(self):
|
||||
def get_number_value(self) -> Expression:
|
||||
value = self.type.mock_obj.get_value(self.obj)
|
||||
if self.scale == 1.0:
|
||||
return value
|
||||
return value / float(self.scale)
|
||||
|
||||
def is_selected(self):
|
||||
def is_selected(self) -> MockObj | None:
|
||||
"""
|
||||
Overridable property to determine if the widget is selected. Will be None except
|
||||
for matrix buttons
|
||||
@@ -444,7 +459,7 @@ class Widget:
|
||||
"""
|
||||
return
|
||||
|
||||
def get_scale(self):
|
||||
def get_scale(self) -> float:
|
||||
return self.type.get_scale(self.config)
|
||||
|
||||
|
||||
@@ -630,29 +645,14 @@ async def set_obj_properties(w: Widget, config):
|
||||
group = await cg.get_variable(group)
|
||||
lv.group_add_obj(group, w.obj)
|
||||
props = parts[CONF_MAIN][CONF_DEFAULT]
|
||||
lambs = {}
|
||||
flag_set = set()
|
||||
flag_clr = set()
|
||||
for prop, value in {k: v for k, v in props.items() if k in OBJ_FLAGS}.items():
|
||||
for flag, value in props.items():
|
||||
if flag not in OBJ_FLAGS:
|
||||
continue
|
||||
if isinstance(value, cv.Lambda):
|
||||
lambs[prop] = value
|
||||
elif value:
|
||||
flag_set.add(prop)
|
||||
else:
|
||||
flag_clr.add(prop)
|
||||
if flag_set:
|
||||
adds = join_enums(flag_set, "LV_OBJ_FLAG_")
|
||||
w.add_flag(adds)
|
||||
if flag_clr:
|
||||
clrs = join_enums(flag_clr, "LV_OBJ_FLAG_")
|
||||
w.clear_flag(clrs)
|
||||
for key, value in lambs.items():
|
||||
lamb = await cg.process_lambda(value, [], capture="=", return_type=cg.bool_)
|
||||
flag = f"LV_OBJ_FLAG_{key.upper()}"
|
||||
with LvConditional(call_lambda(lamb)) as cond:
|
||||
w.add_flag(flag)
|
||||
cond.else_()
|
||||
w.clear_flag(flag)
|
||||
value = call_lambda(
|
||||
await cg.process_lambda(value, [], capture="=", return_type=cg.bool_)
|
||||
)
|
||||
w.set_flag(flag, value)
|
||||
|
||||
for key, value in config.get(CONF_STATE, {}).items():
|
||||
if isinstance(value, cv.Lambda):
|
||||
|
||||
@@ -13,12 +13,12 @@ from ..defines import (
|
||||
ARC_MODES,
|
||||
CONF_ADJUSTABLE,
|
||||
CONF_CHANGE_RATE,
|
||||
CONF_CLICKABLE,
|
||||
CONF_END_ANGLE,
|
||||
CONF_INDICATOR,
|
||||
CONF_KNOB,
|
||||
CONF_MAIN,
|
||||
CONF_START_ANGLE,
|
||||
LV_OBJ_FLAG,
|
||||
LV_PART,
|
||||
)
|
||||
from ..lv_validation import (
|
||||
@@ -85,7 +85,7 @@ class ArcType(NumberType):
|
||||
if CONF_ADJUSTABLE in config:
|
||||
if not config[CONF_ADJUSTABLE]:
|
||||
lv_obj.remove_style(w.obj, nullptr, LV_PART.KNOB)
|
||||
w.clear_flag(LV_OBJ_FLAG.CLICKABLE)
|
||||
w.set_flag(CONF_CLICKABLE, False)
|
||||
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)
|
||||
|
||||
@@ -99,7 +99,7 @@ class CanvasType(WidgetType):
|
||||
if config[CONF_TRANSPARENT]:
|
||||
color_format = "LV_COLOR_FORMAT_ARGB8888"
|
||||
else:
|
||||
color_format = "LV_COLOR_FORMAT_NATIVE"
|
||||
color_format = "LV_COLOR_FORMAT_RGB565"
|
||||
|
||||
# LVGL 9.4: LV_CANVAS_BUF_SIZE(width, height, bits_per_pixel, stride)
|
||||
# stride is 0 for default (width * bytes_per_pixel)
|
||||
|
||||
@@ -20,6 +20,7 @@ from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from ..automation import action_to_code
|
||||
from ..defines import (
|
||||
CONF_ALIGN_TO,
|
||||
CONF_CLICKABLE,
|
||||
CONF_MAIN,
|
||||
CONF_PAD_ROW,
|
||||
CONF_SCROLLBAR,
|
||||
@@ -441,7 +442,7 @@ async def _wire_dynamic_triggers(w: Widget, config: dict) -> None:
|
||||
for event, conf in {
|
||||
event: conf for event, conf in config.items() if event in LV_EVENT_TRIGGERS
|
||||
}.items():
|
||||
w.add_flag("LV_OBJ_FLAG_CLICKABLE")
|
||||
w.set_flag(CONF_CLICKABLE, True)
|
||||
await add_trigger(
|
||||
conf[0], event_target, event, attach_obj=w.obj, user_data=user_data
|
||||
)
|
||||
|
||||
@@ -38,7 +38,6 @@ from ..defines import (
|
||||
CONF_SRC,
|
||||
CONF_START_VALUE,
|
||||
CONF_TICKS,
|
||||
LV_OBJ_FLAG,
|
||||
LV_PART,
|
||||
LV_SCALE_MODE,
|
||||
add_lv_use,
|
||||
@@ -457,7 +456,7 @@ class MeterType(WidgetType):
|
||||
LV_EVENT.DRAW_TASK_ADDED,
|
||||
nullptr,
|
||||
)
|
||||
lv.obj_add_flag(scale_var, LV_OBJ_FLAG.SEND_DRAW_TASK_EVENTS)
|
||||
lv_obj.set_send_draw_task_events(scale_var, True)
|
||||
|
||||
if t == CONF_LINE:
|
||||
# Needle represented by a line
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from esphome import codegen as cg, config_validation as cv
|
||||
from esphome.const import CONF_BUTTON, CONF_ID, CONF_TEXT
|
||||
from esphome.const import CONF_BUTTON, CONF_HIDDEN, CONF_ID, CONF_TEXT
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_types import nullptr
|
||||
@@ -14,7 +14,6 @@ from ..defines import (
|
||||
CONF_MSGBOXES,
|
||||
CONF_SRC,
|
||||
CONF_TITLE,
|
||||
LV_OBJ_FLAG,
|
||||
TYPE_FLEX,
|
||||
add_lv_use,
|
||||
add_warning,
|
||||
@@ -141,7 +140,7 @@ async def msgbox_to_code(top_layer, conf):
|
||||
percent100 = await pixels_or_percent.process(1.0)
|
||||
lv_obj.set_size(outer, percent100, percent100)
|
||||
outer_widget.add_style(await OUTER_STYLE.get_var())
|
||||
outer_widget.add_flag(LV_OBJ_FLAG.HIDDEN)
|
||||
outer_widget.set_flag(CONF_HIDDEN, True)
|
||||
lv.msgbox_add_title(msgbox, title)
|
||||
lv.msgbox_add_text(msgbox, text)
|
||||
lv_obj.set_style_align(msgbox, literal("LV_ALIGN_CENTER"), 0)
|
||||
@@ -153,7 +152,7 @@ async def msgbox_to_code(top_layer, conf):
|
||||
await widget_to_code(button, header_button_spec, msgbox)
|
||||
|
||||
async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action:
|
||||
outer_widget.add_flag(LV_OBJ_FLAG.HIDDEN)
|
||||
outer_widget.set_flag(CONF_HIDDEN, True)
|
||||
if close_button:
|
||||
with LocalVariable(
|
||||
"close_btn_", lv_obj_t, lv_expr.msgbox_add_close_button(msgbox)
|
||||
|
||||
@@ -107,7 +107,7 @@ dependencies:
|
||||
rules:
|
||||
- if: "$ESPHOME_ARDUINO_COMPONENT == 0"
|
||||
lvgl/lvgl:
|
||||
version: 9.5.0
|
||||
version: 9.6.0
|
||||
fastled/FastLED:
|
||||
git: https://github.com/FastLED/FastLED.git
|
||||
version: d44c800a9e876a8394caefc2ce4915dd96dac77b
|
||||
|
||||
+2
-2
@@ -108,7 +108,7 @@ build_unflags =
|
||||
lib_deps =
|
||||
esphome/dlms_parser@2.2.0 ; dlms_meter
|
||||
bblanchon/ArduinoJson@7.4.3 ; json
|
||||
lvgl/lvgl@9.5.0 ; lvgl
|
||||
lvgl/lvgl@9.6.0 ; lvgl
|
||||
|
||||
; This are common settings for the ESP8266 using Arduino.
|
||||
[common:esp8266-arduino]
|
||||
@@ -646,7 +646,7 @@ extends = common
|
||||
platform = platformio/native
|
||||
lib_deps =
|
||||
esphome/noise-c@0.1.31 ; used by noise (api, ota)
|
||||
lvgl/lvgl@9.5.0 ; lvgl
|
||||
lvgl/lvgl@9.6.0 ; lvgl
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_HOST
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
esphome:
|
||||
name: test-lv-conf-check-second
|
||||
|
||||
esp32:
|
||||
board: lolin_c3_mini
|
||||
|
||||
spi:
|
||||
mosi_pin:
|
||||
number: GPIO2
|
||||
ignore_strapping_warning: true
|
||||
clk_pin: GPIO1
|
||||
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
id: display_a
|
||||
data_rate: 20MHz
|
||||
model: st7735
|
||||
cs_pin:
|
||||
number: GPIO8
|
||||
ignore_strapping_warning: true
|
||||
dc_pin:
|
||||
number: GPIO3
|
||||
- platform: mipi_spi
|
||||
id: display_b
|
||||
data_rate: 20MHz
|
||||
model: st7735
|
||||
cs_pin: GPIO4
|
||||
dc_pin: GPIO5
|
||||
|
||||
lvgl:
|
||||
- id: lvgl_a
|
||||
displays: display_a
|
||||
widgets:
|
||||
- label:
|
||||
text: Hello
|
||||
- id: lvgl_b
|
||||
displays: display_b
|
||||
check_args: true
|
||||
widgets:
|
||||
- label:
|
||||
text: Hello
|
||||
@@ -0,0 +1,27 @@
|
||||
esphome:
|
||||
name: test-lv-conf-check-warn
|
||||
|
||||
esp32:
|
||||
board: lolin_c3_mini
|
||||
|
||||
spi:
|
||||
mosi_pin:
|
||||
number: GPIO2
|
||||
ignore_strapping_warning: true
|
||||
clk_pin: GPIO1
|
||||
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
data_rate: 20MHz
|
||||
model: st7735
|
||||
cs_pin:
|
||||
number: GPIO8
|
||||
ignore_strapping_warning: true
|
||||
dc_pin:
|
||||
number: GPIO3
|
||||
|
||||
lvgl:
|
||||
check_args: true
|
||||
widgets:
|
||||
- label:
|
||||
text: Hello
|
||||
@@ -0,0 +1,26 @@
|
||||
esphome:
|
||||
name: test-lv-conf-defaults
|
||||
|
||||
esp32:
|
||||
board: lolin_c3_mini
|
||||
|
||||
spi:
|
||||
mosi_pin:
|
||||
number: GPIO2
|
||||
ignore_strapping_warning: true
|
||||
clk_pin: GPIO1
|
||||
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
data_rate: 20MHz
|
||||
model: st7735
|
||||
cs_pin:
|
||||
number: GPIO8
|
||||
ignore_strapping_warning: true
|
||||
dc_pin:
|
||||
number: GPIO3
|
||||
|
||||
lvgl:
|
||||
widgets:
|
||||
- label:
|
||||
text: Hello
|
||||
@@ -0,0 +1,29 @@
|
||||
esphome:
|
||||
name: test-lv-conf-options
|
||||
|
||||
esp32:
|
||||
board: lolin_c3_mini
|
||||
|
||||
spi:
|
||||
mosi_pin:
|
||||
number: GPIO2
|
||||
ignore_strapping_warning: true
|
||||
clk_pin: GPIO1
|
||||
|
||||
display:
|
||||
- platform: mipi_spi
|
||||
data_rate: 20MHz
|
||||
model: st7735
|
||||
byte_order: little_endian
|
||||
cs_pin:
|
||||
number: GPIO8
|
||||
ignore_strapping_warning: true
|
||||
dc_pin:
|
||||
number: GPIO3
|
||||
|
||||
lvgl:
|
||||
check_args: true
|
||||
log_level: DEBUG
|
||||
widgets:
|
||||
- label:
|
||||
text: Hello
|
||||
@@ -287,10 +287,7 @@ def test_add_text_with_index_moves_before_firing_on_add(main_cpp: str) -> None:
|
||||
|
||||
def test_add_button_with_checkable_flag(main_cpp: str) -> None:
|
||||
assert "lv_obj_t *dyn_button_VAR_ = lv_button_create(test_list);" in main_cpp
|
||||
assert (
|
||||
"lv_obj_add_flag(dyn_button_VAR_, (lv_obj_flag_t)(LV_OBJ_FLAG_CHECKABLE));"
|
||||
in main_cpp
|
||||
)
|
||||
assert "lv_obj_set_checkable(dyn_button_VAR_, true);" in main_cpp
|
||||
assert (
|
||||
'lv_label_set_text(lv_obj_get_child(dyn_button_VAR_, 0), "Entry");' in main_cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Tests for the LVGL configuration defines written to lv_conf.h."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.lvgl import defines as df
|
||||
from esphome.components.lvgl.schemas import WIDGET_TYPES
|
||||
|
||||
# LVGL 9.6 stops the build with #error when an enabled option "selects" another
|
||||
# option that is disabled. ESPHome sets every unused LV_USE_* to 0, so each widget
|
||||
# must list these in get_uses().
|
||||
LVGL_SELECTS = (
|
||||
("SLIDER", "BAR"),
|
||||
("BARCODE", "CANVAS"),
|
||||
("QRCODE", "CANVAS"),
|
||||
("CANVAS", "IMAGE"),
|
||||
("DROPDOWN", "LABEL"),
|
||||
("MSGBOX", "LABEL"),
|
||||
("ROLLER", "LABEL"),
|
||||
("TEXTAREA", "LABEL"),
|
||||
)
|
||||
|
||||
|
||||
def test_default_lv_conf_defines(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path("lv_conf_defaults.yaml"))
|
||||
defines = df.get_defines()
|
||||
assert defines["LV_COLOR_FORMAT_DEFAULT"] == "LV_COLOR_FORMAT_RGB565_SWAPPED"
|
||||
assert defines["LV_DRAW_SW_SUPPORT_RGB565_SWAPPED"] == "1"
|
||||
assert defines["LV_OBJ_STYLE_CACHE"] == "1"
|
||||
assert defines["LV_USE_CHECK_ARG"] == "0"
|
||||
assert "LV_CHECK_ARG_LOG_MODE" not in defines
|
||||
# Both are deprecated in LVGL 9.6 and produce build warnings
|
||||
assert "LV_COLOR_DEPTH" not in defines
|
||||
assert "LV_COLOR_16_SWAP" not in defines
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("yaml_file", "color_format", "log_mode"),
|
||||
[
|
||||
("lv_conf_options.yaml", "RGB565", "VERBOSE"),
|
||||
("lv_conf_check_warn.yaml", "RGB565_SWAPPED", "MINIMAL"),
|
||||
# Only the second LVGL instance sets check_args
|
||||
("lv_conf_check_second.yaml", "RGB565_SWAPPED", "MINIMAL"),
|
||||
],
|
||||
)
|
||||
def test_lv_conf_options(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
yaml_file: str,
|
||||
color_format: str,
|
||||
log_mode: str,
|
||||
) -> None:
|
||||
generate_main(component_config_path(yaml_file))
|
||||
defines = df.get_defines()
|
||||
assert defines["LV_COLOR_FORMAT_DEFAULT"] == f"LV_COLOR_FORMAT_{color_format}"
|
||||
assert defines["LV_USE_CHECK_ARG"] == "1"
|
||||
assert defines["LV_CHECK_ARG_LOG_MODE"] == f"LV_CHECK_ARG_LOG_MODE_{log_mode}"
|
||||
|
||||
|
||||
def test_check_arg_log_modes_cover_all_log_levels() -> None:
|
||||
assert df.LV_CHECK_ARG_LOG_MODES.keys() == df.LV_LOG_LEVELS.keys()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("widget_name", sorted(WIDGET_TYPES))
|
||||
def test_widget_uses_satisfy_lvgl_selects(widget_name: str) -> None:
|
||||
widget_type = WIDGET_TYPES[widget_name]
|
||||
df.add_lv_use(widget_type.name, *widget_type.get_uses())
|
||||
enabled = {use.upper() for use in df.get_lv_uses()}
|
||||
for option, required in LVGL_SELECTS:
|
||||
if option in enabled:
|
||||
assert required in enabled, (
|
||||
f"'{widget_name}' enables LV_USE_{option}, which needs LV_USE_{required}"
|
||||
)
|
||||
@@ -10,11 +10,15 @@ from esphome.config_validation import Invalid
|
||||
|
||||
|
||||
def _config(
|
||||
displays: list[str], theme: dict | None = None, debug_outline: bool = False
|
||||
displays: list[str],
|
||||
theme: dict | None = None,
|
||||
debug_outline: bool = False,
|
||||
check_args: bool = False,
|
||||
) -> dict:
|
||||
config = {
|
||||
df.CONF_DISPLAYS: displays,
|
||||
"log_level": "WARN",
|
||||
df.CONF_CHECK_ARGS: check_args,
|
||||
"color_depth": 16,
|
||||
"byte_order": "big_endian",
|
||||
df.CONF_TRANSPARENCY_KEY: 0x000400,
|
||||
@@ -73,3 +77,8 @@ class TestDebugOutlineOnMultipleInstances:
|
||||
_config(["disp_b"], debug_outline=True),
|
||||
]
|
||||
multi_conf_validate(configs)
|
||||
|
||||
|
||||
def test_check_args_may_differ_between_instances() -> None:
|
||||
configs = [_config(["disp_a"], check_args=True), _config(["disp_b"])]
|
||||
multi_conf_validate(configs)
|
||||
|
||||
@@ -53,6 +53,7 @@ lvgl:
|
||||
id: lvgl_id
|
||||
rotation: 90
|
||||
log_level: debug
|
||||
check_args: true
|
||||
resume_on_input: true
|
||||
paused: true
|
||||
update_when_display_idle: true
|
||||
@@ -976,6 +977,16 @@ lvgl:
|
||||
- spinner:
|
||||
align: right_mid
|
||||
send_draw_task_events: true
|
||||
event_trickle: true
|
||||
state_trickle: true
|
||||
radio_button: true
|
||||
flex_in_new_track: !lambda return true;
|
||||
- spinner:
|
||||
align: top_mid
|
||||
layout_1: true
|
||||
layout_2: true
|
||||
widget_1: !lambda return false;
|
||||
widget_2: false
|
||||
- image:
|
||||
id: lv_image
|
||||
src: cat_image
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# LVGL only uses the PPA to draw on the ESP32-P4 with ESP-IDF 6.0 or later
|
||||
esp32:
|
||||
framework:
|
||||
version: 6.0.1
|
||||
|
||||
display:
|
||||
- platform: mipi_dsi
|
||||
model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-3.4C
|
||||
|
||||
lvgl:
|
||||
widgets:
|
||||
- obj:
|
||||
bg_color: 0x2040C0
|
||||
widgets:
|
||||
- label:
|
||||
text: PPA
|
||||
|
||||
psram:
|
||||
|
||||
esp_ldo:
|
||||
- channel: 3
|
||||
voltage: 2.5V
|
||||
@@ -3,6 +3,7 @@ display:
|
||||
model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-3.4C
|
||||
lvgl:
|
||||
byte_order: little_endian
|
||||
check_args: true
|
||||
rotation: 90
|
||||
|
||||
psram:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
display:
|
||||
- platform: sdl
|
||||
- platform: snapshot
|
||||
id: sdl0
|
||||
auto_clear_enabled: false
|
||||
dimensions:
|
||||
@@ -11,11 +11,6 @@ display:
|
||||
width: 480
|
||||
height: 480
|
||||
|
||||
touchscreen:
|
||||
- platform: sdl
|
||||
display: sdl0
|
||||
sdl_id: sdl0
|
||||
|
||||
lvgl:
|
||||
- id: lvgl_0
|
||||
default_font: space16
|
||||
|
||||
@@ -30,7 +30,7 @@ EXPECTED_SHA256 = "a995b002dd1d183c47514da15ab9a60a3e7d788c2e24386a02fddd4865509
|
||||
# Bundled LVGL version (esphome/components/lvgl/__init__.py, LVGL_VERSION) the hash above was
|
||||
# generated against. A version bump can shift anti-aliasing enough to change the hash even though
|
||||
# nothing is actually wrong -- if this test fails, check that first before regenerating the hash.
|
||||
EXPECTED_LVGL_VERSION = "9.5.0"
|
||||
EXPECTED_LVGL_VERSION = "9.6.0"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
Reference in New Issue
Block a user