mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 00:18:11 +01:00
[lvgl] Revise code generation to allow early widget creation (#7611)
Some checks failed
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 IDF (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP8266 (push) Blocked by required conditions
CI / list-components (push) Blocked by required conditions
CI / Run script/ci-custom (push) Blocked by required conditions
CI / Create common environment (push) Waiting to run
CI / Check black (push) Blocked by required conditions
CI / Check flake8 (push) Blocked by required conditions
CI / Check pylint (push) Blocked by required conditions
CI / Check pyupgrade (push) Blocked by required conditions
CI / Run pytest (push) Blocked by required conditions
CI / Check clang-format (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Blocked by required conditions
CI / Component test (push) Blocked by required conditions
CI / Split components for testing into 20 groups maximum (push) Blocked by required conditions
CI / Test split components (push) Blocked by required conditions
CI / CI Status (push) Blocked by required conditions
YAML lint / yamllint (push) Waiting to run
CI for docker images / Build docker containers (push) Has been cancelled
Some checks failed
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 IDF (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP8266 (push) Blocked by required conditions
CI / list-components (push) Blocked by required conditions
CI / Run script/ci-custom (push) Blocked by required conditions
CI / Create common environment (push) Waiting to run
CI / Check black (push) Blocked by required conditions
CI / Check flake8 (push) Blocked by required conditions
CI / Check pylint (push) Blocked by required conditions
CI / Check pyupgrade (push) Blocked by required conditions
CI / Run pytest (push) Blocked by required conditions
CI / Check clang-format (push) Blocked by required conditions
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Blocked by required conditions
CI / Component test (push) Blocked by required conditions
CI / Split components for testing into 20 groups maximum (push) Blocked by required conditions
CI / Test split components (push) Blocked by required conditions
CI / CI Status (push) Blocked by required conditions
YAML lint / yamllint (push) Waiting to run
CI for docker images / Build docker containers (push) Has been cancelled
This commit is contained in:
parent
f490585f66
commit
8bbe4efded
10 changed files with 155 additions and 132 deletions
|
@ -22,7 +22,7 @@ from esphome.helpers import write_file_if_changed
|
||||||
|
|
||||||
from . import defines as df, helpers, lv_validation as lvalid
|
from . import defines as df, helpers, lv_validation as lvalid
|
||||||
from .automation import disp_update, focused_widgets, update_to_code
|
from .automation import disp_update, focused_widgets, update_to_code
|
||||||
from .defines import CONF_WIDGETS, add_define
|
from .defines import add_define
|
||||||
from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
|
from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
|
||||||
from .gradient import GRADIENT_SCHEMA, gradients_to_code
|
from .gradient import GRADIENT_SCHEMA, gradients_to_code
|
||||||
from .hello_world import get_hello_world
|
from .hello_world import get_hello_world
|
||||||
|
@ -54,7 +54,7 @@ from .types import (
|
||||||
lv_style_t,
|
lv_style_t,
|
||||||
lvgl_ns,
|
lvgl_ns,
|
||||||
)
|
)
|
||||||
from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties, styles_used
|
from .widgets import Widget, add_widgets, get_scr_act, set_obj_properties, styles_used
|
||||||
from .widgets.animimg import animimg_spec
|
from .widgets.animimg import animimg_spec
|
||||||
from .widgets.arc import arc_spec
|
from .widgets.arc import arc_spec
|
||||||
from .widgets.button import button_spec
|
from .widgets.button import button_spec
|
||||||
|
@ -186,7 +186,7 @@ def final_validation(config):
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_library("lvgl/lvgl", "8.4.0")
|
cg.add_library("lvgl/lvgl", "8.4.0")
|
||||||
CORE.add_define("USE_LVGL")
|
cg.add_define("USE_LVGL")
|
||||||
# suppress default enabling of extra widgets
|
# suppress default enabling of extra widgets
|
||||||
add_define("_LV_KCONFIG_PRESENT")
|
add_define("_LV_KCONFIG_PRESENT")
|
||||||
# Always enable - lots of things use it.
|
# Always enable - lots of things use it.
|
||||||
|
@ -200,7 +200,13 @@ async def to_code(config):
|
||||||
add_define("LV_MEM_CUSTOM_REALLOC", "lv_custom_mem_realloc")
|
add_define("LV_MEM_CUSTOM_REALLOC", "lv_custom_mem_realloc")
|
||||||
add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"')
|
add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"')
|
||||||
|
|
||||||
add_define("LV_LOG_LEVEL", f"LV_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}")
|
add_define(
|
||||||
|
"LV_LOG_LEVEL", f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config[df.CONF_LOG_LEVEL]]}"
|
||||||
|
)
|
||||||
|
cg.add_define(
|
||||||
|
"LVGL_LOG_LEVEL",
|
||||||
|
cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}"),
|
||||||
|
)
|
||||||
add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH])
|
add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH])
|
||||||
for font in helpers.lv_fonts_used:
|
for font in helpers.lv_fonts_used:
|
||||||
add_define(f"LV_FONT_{font.upper()}")
|
add_define(f"LV_FONT_{font.upper()}")
|
||||||
|
@ -214,15 +220,9 @@ async def to_code(config):
|
||||||
"LV_COLOR_CHROMA_KEY",
|
"LV_COLOR_CHROMA_KEY",
|
||||||
await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]),
|
await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]),
|
||||||
)
|
)
|
||||||
CORE.add_build_flag("-Isrc")
|
cg.add_build_flag("-Isrc")
|
||||||
|
|
||||||
cg.add_global(lvgl_ns.using)
|
cg.add_global(lvgl_ns.using)
|
||||||
lv_component = cg.new_Pvariable(config[CONF_ID])
|
|
||||||
await cg.register_component(lv_component, config)
|
|
||||||
Widget.create(config[CONF_ID], lv_component, obj_spec, config)
|
|
||||||
for display in config[df.CONF_DISPLAYS]:
|
|
||||||
cg.add(lv_component.add_display(await cg.get_variable(display)))
|
|
||||||
|
|
||||||
frac = config[CONF_BUFFER_SIZE]
|
frac = config[CONF_BUFFER_SIZE]
|
||||||
if frac >= 0.75:
|
if frac >= 0.75:
|
||||||
frac = 1
|
frac = 1
|
||||||
|
@ -232,10 +232,17 @@ async def to_code(config):
|
||||||
frac = 4
|
frac = 4
|
||||||
else:
|
else:
|
||||||
frac = 8
|
frac = 8
|
||||||
cg.add(lv_component.set_buffer_frac(int(frac)))
|
displays = [await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]]
|
||||||
cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH]))
|
lv_component = cg.new_Pvariable(
|
||||||
cg.add(lv_component.set_draw_rounding(config[df.CONF_DRAW_ROUNDING]))
|
config[CONF_ID],
|
||||||
cg.add(lv_component.set_resume_on_input(config[df.CONF_RESUME_ON_INPUT]))
|
displays,
|
||||||
|
frac,
|
||||||
|
config[df.CONF_FULL_REFRESH],
|
||||||
|
config[df.CONF_DRAW_ROUNDING],
|
||||||
|
config[df.CONF_RESUME_ON_INPUT],
|
||||||
|
)
|
||||||
|
await cg.register_component(lv_component, config)
|
||||||
|
Widget.create(config[CONF_ID], lv_component, obj_spec, config)
|
||||||
|
|
||||||
for font in helpers.esphome_fonts_used:
|
for font in helpers.esphome_fonts_used:
|
||||||
await cg.get_variable(font)
|
await cg.get_variable(font)
|
||||||
|
@ -257,6 +264,7 @@ async def to_code(config):
|
||||||
else:
|
else:
|
||||||
add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font))
|
add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font))
|
||||||
|
|
||||||
|
lv_scr_act = get_scr_act(lv_component)
|
||||||
async with LvContext(lv_component):
|
async with LvContext(lv_component):
|
||||||
await touchscreens_to_code(lv_component, config)
|
await touchscreens_to_code(lv_component, config)
|
||||||
await encoders_to_code(lv_component, config)
|
await encoders_to_code(lv_component, config)
|
||||||
|
@ -266,11 +274,9 @@ async def to_code(config):
|
||||||
await set_obj_properties(lv_scr_act, config)
|
await set_obj_properties(lv_scr_act, config)
|
||||||
await add_widgets(lv_scr_act, config)
|
await add_widgets(lv_scr_act, config)
|
||||||
await add_pages(lv_component, config)
|
await add_pages(lv_component, config)
|
||||||
await add_top_layer(config)
|
await add_top_layer(lv_component, config)
|
||||||
await msgboxes_to_code(config)
|
await msgboxes_to_code(lv_component, config)
|
||||||
await disp_update(f"{lv_component}->get_disp()", config)
|
await disp_update(lv_component.get_disp(), config)
|
||||||
# At this point only the setup code should be generated
|
|
||||||
assert LvContext.added_lambda_count == 1
|
|
||||||
# Set this directly since we are limited in how many methods can be added to the Widget class.
|
# Set this directly since we are limited in how many methods can be added to the Widget class.
|
||||||
Widget.widgets_completed = True
|
Widget.widgets_completed = True
|
||||||
async with LvContext(lv_component):
|
async with LvContext(lv_component):
|
||||||
|
@ -291,15 +297,15 @@ async def to_code(config):
|
||||||
await build_automation(resume_trigger, [], conf)
|
await build_automation(resume_trigger, [], conf)
|
||||||
|
|
||||||
for comp in helpers.lvgl_components_required:
|
for comp in helpers.lvgl_components_required:
|
||||||
CORE.add_define(f"USE_LVGL_{comp.upper()}")
|
cg.add_define(f"USE_LVGL_{comp.upper()}")
|
||||||
if "transform_angle" in styles_used:
|
if "transform_angle" in styles_used:
|
||||||
add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
||||||
for use in helpers.lv_uses:
|
for use in helpers.lv_uses:
|
||||||
add_define(f"LV_USE_{use.upper()}")
|
add_define(f"LV_USE_{use.upper()}")
|
||||||
lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
|
lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
|
||||||
write_file_if_changed(lv_conf_h_file, generate_lv_conf_h())
|
write_file_if_changed(lv_conf_h_file, generate_lv_conf_h())
|
||||||
CORE.add_build_flag("-DLV_CONF_H=1")
|
cg.add_build_flag("-DLV_CONF_H=1")
|
||||||
CORE.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"')
|
cg.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"')
|
||||||
|
|
||||||
|
|
||||||
def display_schema(config):
|
def display_schema(config):
|
||||||
|
@ -308,9 +314,9 @@ def display_schema(config):
|
||||||
|
|
||||||
|
|
||||||
def add_hello_world(config):
|
def add_hello_world(config):
|
||||||
if CONF_WIDGETS not in config and CONF_PAGES not in config:
|
if df.CONF_WIDGETS not in config and CONF_PAGES not in config:
|
||||||
LOGGER.info("No pages or widgets configured, creating default hello_world page")
|
LOGGER.info("No pages or widgets configured, creating default hello_world page")
|
||||||
config[CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
|
config[df.CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@ -329,7 +335,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int,
|
cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int,
|
||||||
cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
|
cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
|
||||||
cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
|
cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
|
||||||
*df.LOG_LEVELS, upper=True
|
*df.LV_LOG_LEVELS, upper=True
|
||||||
),
|
),
|
||||||
cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of(
|
cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of(
|
||||||
"big_endian", "little_endian"
|
"big_endian", "little_endian"
|
||||||
|
|
|
@ -5,7 +5,7 @@ from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
|
from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
|
||||||
from esphome.cpp_generator import RawExpression, get_variable
|
from esphome.cpp_generator import get_variable
|
||||||
from esphome.cpp_types import nullptr
|
from esphome.cpp_types import nullptr
|
||||||
|
|
||||||
from .defines import (
|
from .defines import (
|
||||||
|
@ -23,7 +23,6 @@ from .lvcode import (
|
||||||
UPDATE_EVENT,
|
UPDATE_EVENT,
|
||||||
LambdaContext,
|
LambdaContext,
|
||||||
LocalVariable,
|
LocalVariable,
|
||||||
LvConditional,
|
|
||||||
LvglComponent,
|
LvglComponent,
|
||||||
ReturnStatement,
|
ReturnStatement,
|
||||||
add_line_marks,
|
add_line_marks,
|
||||||
|
@ -47,8 +46,8 @@ from .types import (
|
||||||
)
|
)
|
||||||
from .widgets import (
|
from .widgets import (
|
||||||
Widget,
|
Widget,
|
||||||
|
get_scr_act,
|
||||||
get_widgets,
|
get_widgets,
|
||||||
lv_scr_act,
|
|
||||||
set_obj_properties,
|
set_obj_properties,
|
||||||
wait_for_widgets,
|
wait_for_widgets,
|
||||||
)
|
)
|
||||||
|
@ -66,8 +65,6 @@ async def action_to_code(
|
||||||
):
|
):
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
async with LambdaContext(parameters=args, where=action_id) as context:
|
async with LambdaContext(parameters=args, where=action_id) as context:
|
||||||
with LvConditional(lv_expr.is_pre_initialise()):
|
|
||||||
context.add(RawExpression("return"))
|
|
||||||
for widget in widgets:
|
for widget in widgets:
|
||||||
await action(widget)
|
await action(widget)
|
||||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||||
|
@ -126,7 +123,7 @@ async def lvgl_is_idle(config, condition_id, template_arg, args):
|
||||||
async def disp_update(disp, config: dict):
|
async def disp_update(disp, config: dict):
|
||||||
if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config:
|
if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config:
|
||||||
return
|
return
|
||||||
with LocalVariable("lv_disp_tmp", lv_disp_t, literal(disp)) as disp_temp:
|
with LocalVariable("lv_disp_tmp", lv_disp_t, disp) as disp_temp:
|
||||||
if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None:
|
if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None:
|
||||||
lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color))
|
lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color))
|
||||||
if bg_image := config.get(CONF_DISP_BG_IMAGE):
|
if bg_image := config.get(CONF_DISP_BG_IMAGE):
|
||||||
|
@ -136,15 +133,24 @@ async def disp_update(disp, config: dict):
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"lvgl.widget.redraw",
|
"lvgl.widget.redraw",
|
||||||
ObjUpdateAction,
|
ObjUpdateAction,
|
||||||
cv.Schema(
|
cv.Any(
|
||||||
{
|
cv.maybe_simple_value(
|
||||||
cv.Optional(CONF_ID): cv.use_id(lv_obj_t),
|
{
|
||||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
cv.Required(CONF_ID): cv.use_id(lv_obj_t),
|
||||||
}
|
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||||
|
},
|
||||||
|
key=CONF_ID,
|
||||||
|
),
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||||
|
}
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def obj_invalidate_to_code(config, action_id, template_arg, args):
|
async def obj_invalidate_to_code(config, action_id, template_arg, args):
|
||||||
widgets = await get_widgets(config) or [lv_scr_act]
|
lv_comp = await cg.get_variable(config[CONF_LVGL_ID])
|
||||||
|
widgets = await get_widgets(config) or [get_scr_act(lv_comp)]
|
||||||
|
|
||||||
async def do_invalidate(widget: Widget):
|
async def do_invalidate(widget: Widget):
|
||||||
lv_obj.invalidate(widget.obj)
|
lv_obj.invalidate(widget.obj)
|
||||||
|
@ -164,7 +170,7 @@ async def obj_invalidate_to_code(config, action_id, template_arg, args):
|
||||||
async def lvgl_update_to_code(config, action_id, template_arg, args):
|
async def lvgl_update_to_code(config, action_id, template_arg, args):
|
||||||
widgets = await get_widgets(config)
|
widgets = await get_widgets(config)
|
||||||
w = widgets[0]
|
w = widgets[0]
|
||||||
disp = f"{w.obj}->get_disp()"
|
disp = literal(f"{w.obj}->get_disp()")
|
||||||
async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context:
|
async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context:
|
||||||
await disp_update(disp, config)
|
await disp_update(disp, config)
|
||||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||||
|
|
|
@ -189,14 +189,14 @@ LV_ANIM = LvConstant(
|
||||||
LV_GRAD_DIR = LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER")
|
LV_GRAD_DIR = LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER")
|
||||||
LV_DITHER = LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF")
|
LV_DITHER = LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF")
|
||||||
|
|
||||||
LOG_LEVELS = (
|
LV_LOG_LEVELS = {
|
||||||
"TRACE",
|
"VERBOSE": "TRACE",
|
||||||
"INFO",
|
"DEBUG": "TRACE",
|
||||||
"WARN",
|
"INFO": "INFO",
|
||||||
"ERROR",
|
"WARN": "WARN",
|
||||||
"USER",
|
"ERROR": "ERROR",
|
||||||
"NONE",
|
"NONE": "NONE",
|
||||||
)
|
}
|
||||||
|
|
||||||
LV_LONG_MODES = LvConstant(
|
LV_LONG_MODES = LvConstant(
|
||||||
"LV_LABEL_LONG_",
|
"LV_LABEL_LONG_",
|
||||||
|
|
|
@ -183,17 +183,11 @@ class LvContext(LambdaContext):
|
||||||
super().__init__(parameters=self.args)
|
super().__init__(parameters=self.args)
|
||||||
self.lv_component = lv_component
|
self.lv_component = lv_component
|
||||||
|
|
||||||
async def add_init_lambda(self):
|
|
||||||
if self.code_list:
|
|
||||||
cg.add(self.lv_component.add_init_lambda(await self.get_lambda()))
|
|
||||||
LvContext.added_lambda_count += 1
|
|
||||||
|
|
||||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
await super().__aexit__(exc_type, exc_val, exc_tb)
|
await super().__aexit__(exc_type, exc_val, exc_tb)
|
||||||
await self.add_init_lambda()
|
|
||||||
|
|
||||||
def add(self, expression: Union[Expression, Statement]):
|
def add(self, expression: Union[Expression, Statement]):
|
||||||
self.code_list.append(self.indented_statement(expression))
|
cg.add(expression)
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
|
|
|
@ -11,12 +11,6 @@ namespace esphome {
|
||||||
namespace lvgl {
|
namespace lvgl {
|
||||||
static const char *const TAG = "lvgl";
|
static const char *const TAG = "lvgl";
|
||||||
|
|
||||||
#if LV_USE_LOG
|
|
||||||
static void log_cb(const char *buf) {
|
|
||||||
esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
|
|
||||||
}
|
|
||||||
#endif // LV_USE_LOG
|
|
||||||
|
|
||||||
static const char *const EVENT_NAMES[] = {
|
static const char *const EVENT_NAMES[] = {
|
||||||
"NONE",
|
"NONE",
|
||||||
"PRESSED",
|
"PRESSED",
|
||||||
|
@ -383,26 +377,48 @@ void LvglComponent::write_random_() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LvglComponent::setup() {
|
/**
|
||||||
ESP_LOGCONFIG(TAG, "LVGL Setup starts");
|
* @class LvglComponent
|
||||||
#if LV_USE_LOG
|
* @brief Component for rendering LVGL.
|
||||||
lv_log_register_print_cb(log_cb);
|
*
|
||||||
#endif
|
* This component renders LVGL widgets on a display. Some initialisation must be done in the constructor
|
||||||
|
* since LVGL needs to be initialised before any widgets can be created.
|
||||||
|
*
|
||||||
|
* @param displays a list of displays to render onto. All displays must have the same
|
||||||
|
* resolution.
|
||||||
|
* @param buffer_frac the fraction of the display resolution to use for the LVGL
|
||||||
|
* draw buffer. A higher value will make animations smoother but
|
||||||
|
* also increase memory usage.
|
||||||
|
* @param full_refresh if true, the display will be fully refreshed on every frame.
|
||||||
|
* If false, only changed areas will be updated.
|
||||||
|
* @param draw_rounding the rounding to use when drawing. A value of 1 will draw
|
||||||
|
* without any rounding, a value of 2 will round to the nearest
|
||||||
|
* multiple of 2, and so on.
|
||||||
|
* @param resume_on_input if true, this component will resume rendering when the user
|
||||||
|
* presses a key or clicks on the screen.
|
||||||
|
*/
|
||||||
|
LvglComponent::LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh,
|
||||||
|
int draw_rounding, bool resume_on_input)
|
||||||
|
: draw_rounding(draw_rounding),
|
||||||
|
displays_(std::move(displays)),
|
||||||
|
buffer_frac_(buffer_frac),
|
||||||
|
full_refresh_(full_refresh),
|
||||||
|
resume_on_input_(resume_on_input) {
|
||||||
lv_init();
|
lv_init();
|
||||||
lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
|
lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
|
||||||
lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
|
lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
|
||||||
auto *display = this->displays_[0];
|
auto *display = this->displays_[0];
|
||||||
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
|
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
|
||||||
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
|
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
|
||||||
auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
|
this->rotation = display->get_rotation();
|
||||||
if (buf == nullptr) {
|
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
|
||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
|
||||||
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
if (this->rotate_buf_ == nullptr)
|
||||||
#endif
|
return;
|
||||||
this->mark_failed();
|
|
||||||
this->status_set_error("Memory allocation failure");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
|
||||||
|
if (buf == nullptr)
|
||||||
|
return;
|
||||||
lv_disp_draw_buf_init(&this->draw_buf_, buf, nullptr, buffer_pixels);
|
lv_disp_draw_buf_init(&this->draw_buf_, buf, nullptr, buffer_pixels);
|
||||||
lv_disp_drv_init(&this->disp_drv_);
|
lv_disp_drv_init(&this->disp_drv_);
|
||||||
this->disp_drv_.draw_buf = &this->draw_buf_;
|
this->disp_drv_.draw_buf = &this->draw_buf_;
|
||||||
|
@ -410,18 +426,7 @@ void LvglComponent::setup() {
|
||||||
this->disp_drv_.full_refresh = this->full_refresh_;
|
this->disp_drv_.full_refresh = this->full_refresh_;
|
||||||
this->disp_drv_.flush_cb = static_flush_cb;
|
this->disp_drv_.flush_cb = static_flush_cb;
|
||||||
this->disp_drv_.rounder_cb = rounder_cb;
|
this->disp_drv_.rounder_cb = rounder_cb;
|
||||||
this->rotation = display->get_rotation();
|
// reset the display rotation since we will handle all rotations
|
||||||
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
|
|
||||||
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
|
|
||||||
if (this->rotate_buf_ == nullptr) {
|
|
||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
|
||||||
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
|
||||||
#endif
|
|
||||||
this->mark_failed();
|
|
||||||
this->status_set_error("Memory allocation failure");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
|
display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
|
||||||
switch (this->rotation) {
|
switch (this->rotation) {
|
||||||
default:
|
default:
|
||||||
|
@ -435,12 +440,30 @@ void LvglComponent::setup() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
|
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
|
||||||
for (const auto &v : this->init_lambdas_)
|
}
|
||||||
v(this);
|
|
||||||
|
void LvglComponent::setup() {
|
||||||
|
if (this->draw_buf_.buf1 == nullptr) {
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error("Memory allocation failure");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGCONFIG(TAG, "LVGL Setup starts");
|
||||||
|
#if LV_USE_LOG
|
||||||
|
lv_log_register_print_cb([](const char *buf) {
|
||||||
|
auto next = strchr(buf, ')');
|
||||||
|
if (next != nullptr)
|
||||||
|
buf = next + 1;
|
||||||
|
while (isspace(*buf))
|
||||||
|
buf++;
|
||||||
|
esp_log_printf_(LVGL_LOG_LEVEL, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
|
||||||
|
});
|
||||||
|
#endif
|
||||||
this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0);
|
this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0);
|
||||||
lv_disp_trig_activity(this->disp_);
|
lv_disp_trig_activity(this->disp_);
|
||||||
ESP_LOGCONFIG(TAG, "LVGL Setup complete");
|
ESP_LOGCONFIG(TAG, "LVGL Setup complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
void LvglComponent::update() {
|
void LvglComponent::update() {
|
||||||
// update indicators
|
// update indicators
|
||||||
if (this->paused_) {
|
if (this->paused_) {
|
||||||
|
@ -455,13 +478,6 @@ void LvglComponent::loop() {
|
||||||
}
|
}
|
||||||
lv_timer_handler_run_in_period(5);
|
lv_timer_handler_run_in_period(5);
|
||||||
}
|
}
|
||||||
bool lv_is_pre_initialise() {
|
|
||||||
if (!lv_is_initialized()) {
|
|
||||||
ESP_LOGE(TAG, "LVGL call before component is initialised");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_LVGL_ANIMIMG
|
#ifdef USE_LVGL_ANIMIMG
|
||||||
void lv_animimg_stop(lv_obj_t *obj) {
|
void lv_animimg_stop(lv_obj_t *obj) {
|
||||||
|
|
|
@ -39,7 +39,6 @@ namespace lvgl {
|
||||||
extern lv_event_code_t lv_api_event; // NOLINT
|
extern lv_event_code_t lv_api_event; // NOLINT
|
||||||
extern lv_event_code_t lv_update_event; // NOLINT
|
extern lv_event_code_t lv_update_event; // NOLINT
|
||||||
extern std::string lv_event_code_name_for(uint8_t event_code);
|
extern std::string lv_event_code_name_for(uint8_t event_code);
|
||||||
extern bool lv_is_pre_initialise();
|
|
||||||
#if LV_COLOR_DEPTH == 16
|
#if LV_COLOR_DEPTH == 16
|
||||||
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565;
|
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565;
|
||||||
#elif LV_COLOR_DEPTH == 32
|
#elif LV_COLOR_DEPTH == 32
|
||||||
|
@ -108,6 +107,8 @@ class LvglComponent : public PollingComponent {
|
||||||
constexpr static const char *const TAG = "lvgl";
|
constexpr static const char *const TAG = "lvgl";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
|
||||||
|
bool resume_on_input);
|
||||||
static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||||
|
|
||||||
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
||||||
|
@ -118,13 +119,10 @@ class LvglComponent : public PollingComponent {
|
||||||
this->idle_callbacks_.add(std::move(callback));
|
this->idle_callbacks_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
|
void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
|
||||||
void add_display(display::Display *display) { this->displays_.push_back(display); }
|
|
||||||
void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; }
|
|
||||||
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
||||||
void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
|
|
||||||
lv_disp_t *get_disp() { return this->disp_; }
|
lv_disp_t *get_disp() { return this->disp_; }
|
||||||
|
lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); }
|
||||||
// Pause or resume the display.
|
// Pause or resume the display.
|
||||||
// @param paused If true, pause the display. If false, resume the display.
|
// @param paused If true, pause the display. If false, resume the display.
|
||||||
// @param show_snow If true, show the snow effect when paused.
|
// @param show_snow If true, show the snow effect when paused.
|
||||||
|
@ -155,17 +153,19 @@ class LvglComponent : public PollingComponent {
|
||||||
}
|
}
|
||||||
// rounding factor to align bounds of update area when drawing
|
// rounding factor to align bounds of update area when drawing
|
||||||
size_t draw_rounding{2};
|
size_t draw_rounding{2};
|
||||||
void set_draw_rounding(size_t rounding) { this->draw_rounding = rounding; }
|
|
||||||
void set_resume_on_input(bool resume_on_input) { this->resume_on_input_ = resume_on_input; }
|
|
||||||
|
|
||||||
// if set to true, the bounds of the update area will always start at 0,0
|
|
||||||
display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES};
|
display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void write_random_();
|
void write_random_();
|
||||||
void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
|
void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
|
||||||
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||||
|
|
||||||
std::vector<display::Display *> displays_{};
|
std::vector<display::Display *> displays_{};
|
||||||
|
size_t buffer_frac_{1};
|
||||||
|
bool full_refresh_{};
|
||||||
|
bool resume_on_input_{};
|
||||||
|
|
||||||
lv_disp_draw_buf_t draw_buf_{};
|
lv_disp_draw_buf_t draw_buf_{};
|
||||||
lv_disp_drv_t disp_drv_{};
|
lv_disp_drv_t disp_drv_{};
|
||||||
lv_disp_t *disp_{};
|
lv_disp_t *disp_{};
|
||||||
|
@ -174,14 +174,10 @@ class LvglComponent : public PollingComponent {
|
||||||
size_t current_page_{0};
|
size_t current_page_{0};
|
||||||
bool show_snow_{};
|
bool show_snow_{};
|
||||||
bool page_wrap_{true};
|
bool page_wrap_{true};
|
||||||
bool resume_on_input_{};
|
|
||||||
std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
|
std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
|
||||||
|
|
||||||
std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
|
|
||||||
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
||||||
CallbackManager<void(bool)> pause_callbacks_{};
|
CallbackManager<void(bool)> pause_callbacks_{};
|
||||||
size_t buffer_frac_{1};
|
|
||||||
bool full_refresh_{};
|
|
||||||
lv_color_t *rotate_buf_{};
|
lv_color_t *rotate_buf_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,6 @@ from .types import lv_lambda_t, lv_obj_t, lv_obj_t_ptr
|
||||||
from .widgets import Widget, add_widgets, set_obj_properties, theme_widget_map
|
from .widgets import Widget, add_widgets, set_obj_properties, theme_widget_map
|
||||||
from .widgets.obj import obj_spec
|
from .widgets.obj import obj_spec
|
||||||
|
|
||||||
TOP_LAYER = literal("lv_disp_get_layer_top(lv_component->get_disp())")
|
|
||||||
|
|
||||||
|
|
||||||
async def styles_to_code(config):
|
async def styles_to_code(config):
|
||||||
"""Convert styles to C__ code."""
|
"""Convert styles to C__ code."""
|
||||||
|
@ -51,9 +49,10 @@ async def theme_to_code(config):
|
||||||
lv_assign(apply, await context.get_lambda())
|
lv_assign(apply, await context.get_lambda())
|
||||||
|
|
||||||
|
|
||||||
async def add_top_layer(config):
|
async def add_top_layer(lv_component, config):
|
||||||
|
top_layer = lv.disp_get_layer_top(lv_component.get_disp())
|
||||||
if top_conf := config.get(CONF_TOP_LAYER):
|
if top_conf := config.get(CONF_TOP_LAYER):
|
||||||
with LocalVariable("top_layer", lv_obj_t, TOP_LAYER) as top_layer_obj:
|
with LocalVariable("top_layer", lv_obj_t, top_layer) as top_layer_obj:
|
||||||
top_w = Widget(top_layer_obj, obj_spec, top_conf)
|
top_w = Widget(top_layer_obj, obj_spec, top_conf)
|
||||||
await set_obj_properties(top_w, top_conf)
|
await set_obj_properties(top_w, top_conf)
|
||||||
await add_widgets(top_w, top_conf)
|
await add_widgets(top_w, top_conf)
|
||||||
|
|
|
@ -55,18 +55,6 @@ theme_widget_map = {}
|
||||||
styles_used = set()
|
styles_used = set()
|
||||||
|
|
||||||
|
|
||||||
class LvScrActType(WidgetType):
|
|
||||||
"""
|
|
||||||
A "widget" representing the active screen.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__("lv_scr_act()", lv_obj_t, ())
|
|
||||||
|
|
||||||
async def to_code(self, w, config: dict):
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
class Widget:
|
class Widget:
|
||||||
"""
|
"""
|
||||||
Represents a Widget.
|
Represents a Widget.
|
||||||
|
@ -221,6 +209,25 @@ class Widget:
|
||||||
widget_map: dict[Any, Widget] = {}
|
widget_map: dict[Any, Widget] = {}
|
||||||
|
|
||||||
|
|
||||||
|
class LvScrActType(WidgetType):
|
||||||
|
"""
|
||||||
|
A "widget" representing the active screen.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("lv_scr_act()", lv_obj_t, ())
|
||||||
|
|
||||||
|
async def to_code(self, w, config: dict):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
lv_scr_act_spec = LvScrActType()
|
||||||
|
|
||||||
|
|
||||||
|
def get_scr_act(lv_comp: MockObj) -> Widget:
|
||||||
|
return Widget.create(None, lv_comp.get_scr_act(), lv_scr_act_spec, {})
|
||||||
|
|
||||||
|
|
||||||
def get_widget_generator(wid):
|
def get_widget_generator(wid):
|
||||||
"""
|
"""
|
||||||
Used to wait for a widget during code generation.
|
Used to wait for a widget during code generation.
|
||||||
|
@ -451,7 +458,3 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent):
|
||||||
await set_obj_properties(w, w_cnfig)
|
await set_obj_properties(w, w_cnfig)
|
||||||
await add_widgets(w, w_cnfig)
|
await add_widgets(w, w_cnfig)
|
||||||
await spec.to_code(w, w_cnfig)
|
await spec.to_code(w, w_cnfig)
|
||||||
|
|
||||||
|
|
||||||
lv_scr_act_spec = LvScrActType()
|
|
||||||
lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {})
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ from ..lvcode import (
|
||||||
EVENT_ARG,
|
EVENT_ARG,
|
||||||
LambdaContext,
|
LambdaContext,
|
||||||
LocalVariable,
|
LocalVariable,
|
||||||
|
lv,
|
||||||
lv_add,
|
lv_add,
|
||||||
lv_assign,
|
lv_assign,
|
||||||
lv_expr,
|
lv_expr,
|
||||||
|
@ -27,7 +28,6 @@ from ..lvcode import (
|
||||||
lv_Pvariable,
|
lv_Pvariable,
|
||||||
)
|
)
|
||||||
from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema
|
from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema
|
||||||
from ..styles import TOP_LAYER
|
|
||||||
from ..types import LV_EVENT, char_ptr, lv_obj_t
|
from ..types import LV_EVENT, char_ptr, lv_obj_t
|
||||||
from . import Widget, set_obj_properties
|
from . import Widget, set_obj_properties
|
||||||
from .button import button_spec
|
from .button import button_spec
|
||||||
|
@ -59,7 +59,7 @@ MSGBOX_SCHEMA = container_schema(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def msgbox_to_code(conf):
|
async def msgbox_to_code(top_layer, conf):
|
||||||
"""
|
"""
|
||||||
Construct a message box. This consists of a full-screen translucent background enclosing a centered container
|
Construct a message box. This consists of a full-screen translucent background enclosing a centered container
|
||||||
with an optional title, body, close button and a button matrix. And any other widgets the user cares to add
|
with an optional title, body, close button and a button matrix. And any other widgets the user cares to add
|
||||||
|
@ -101,7 +101,7 @@ async def msgbox_to_code(conf):
|
||||||
text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, ""))
|
text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, ""))
|
||||||
title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, ""))
|
title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, ""))
|
||||||
close_button = conf[CONF_CLOSE_BUTTON]
|
close_button = conf[CONF_CLOSE_BUTTON]
|
||||||
lv_assign(outer, lv_expr.obj_create(TOP_LAYER))
|
lv_assign(outer, lv_expr.obj_create(top_layer))
|
||||||
lv_obj.set_width(outer, lv_pct(100))
|
lv_obj.set_width(outer, lv_pct(100))
|
||||||
lv_obj.set_height(outer, lv_pct(100))
|
lv_obj.set_height(outer, lv_pct(100))
|
||||||
lv_obj.set_style_bg_opa(outer, 128, 0)
|
lv_obj.set_style_bg_opa(outer, 128, 0)
|
||||||
|
@ -141,6 +141,7 @@ async def msgbox_to_code(conf):
|
||||||
set_btn_data(buttonmatrix.obj, ctrl_list, width_list)
|
set_btn_data(buttonmatrix.obj, ctrl_list, width_list)
|
||||||
|
|
||||||
|
|
||||||
async def msgboxes_to_code(config):
|
async def msgboxes_to_code(lv_component, config):
|
||||||
|
top_layer = lv.disp_get_layer_top(lv_component.get_disp())
|
||||||
for conf in config.get(CONF_MSGBOXES, ()):
|
for conf in config.get(CONF_MSGBOXES, ()):
|
||||||
await msgbox_to_code(conf)
|
await msgbox_to_code(top_layer, conf)
|
||||||
|
|
|
@ -12,12 +12,12 @@ substitutions:
|
||||||
arrow_down: "\U000F004B"
|
arrow_down: "\U000F004B"
|
||||||
|
|
||||||
lvgl:
|
lvgl:
|
||||||
|
log_level: debug
|
||||||
resume_on_input: true
|
resume_on_input: true
|
||||||
on_pause:
|
on_pause:
|
||||||
logger.log: LVGL is Paused
|
logger.log: LVGL is Paused
|
||||||
on_resume:
|
on_resume:
|
||||||
logger.log: LVGL has resumed
|
logger.log: LVGL has resumed
|
||||||
log_level: TRACE
|
|
||||||
bg_color: light_blue
|
bg_color: light_blue
|
||||||
disp_bg_color: color_id
|
disp_bg_color: color_id
|
||||||
disp_bg_image: cat_image
|
disp_bg_image: cat_image
|
||||||
|
@ -125,6 +125,8 @@ lvgl:
|
||||||
on_unload:
|
on_unload:
|
||||||
- logger.log: page unloaded
|
- logger.log: page unloaded
|
||||||
- lvgl.widget.focus: mark
|
- lvgl.widget.focus: mark
|
||||||
|
- lvgl.widget.redraw: hello_label
|
||||||
|
- lvgl.widget.redraw:
|
||||||
on_all_events:
|
on_all_events:
|
||||||
logger.log:
|
logger.log:
|
||||||
format: "Event %s"
|
format: "Event %s"
|
||||||
|
|
Loading…
Reference in a new issue