mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
[lvgl] Allow multiple LVGL instances (#7712)
Co-authored-by: clydeps <U5yx99dok9>
This commit is contained in:
parent
80b4c26481
commit
248b0bc378
17 changed files with 287 additions and 226 deletions
|
@ -27,7 +27,7 @@ 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
|
||||||
from .lv_validation import lv_bool, lv_images_used
|
from .lv_validation import lv_bool, lv_images_used
|
||||||
from .lvcode import LvContext, LvglComponent
|
from .lvcode import LvContext, LvglComponent, lvgl_static
|
||||||
from .schemas import (
|
from .schemas import (
|
||||||
DISP_BG_SCHEMA,
|
DISP_BG_SCHEMA,
|
||||||
FLEX_OBJ_SCHEMA,
|
FLEX_OBJ_SCHEMA,
|
||||||
|
@ -152,41 +152,70 @@ def generate_lv_conf_h():
|
||||||
return LV_CONF_H_FORMAT.format("\n".join(definitions))
|
return LV_CONF_H_FORMAT.format("\n".join(definitions))
|
||||||
|
|
||||||
|
|
||||||
def final_validation(config):
|
def multi_conf_validate(configs: list[dict]):
|
||||||
if pages := config.get(CONF_PAGES):
|
displays = [config[df.CONF_DISPLAYS] for config in configs]
|
||||||
if all(p[df.CONF_SKIP] for p in pages):
|
# flatten the display list
|
||||||
raise cv.Invalid("At least one page must not be skipped")
|
display_list = [disp for disps in displays for disp in disps]
|
||||||
|
if len(display_list) != len(set(display_list)):
|
||||||
|
raise cv.Invalid("A display ID may be used in only one LVGL instance")
|
||||||
|
base_config = configs[0]
|
||||||
|
for config in configs[1:]:
|
||||||
|
for item in (
|
||||||
|
df.CONF_LOG_LEVEL,
|
||||||
|
df.CONF_COLOR_DEPTH,
|
||||||
|
df.CONF_BYTE_ORDER,
|
||||||
|
df.CONF_TRANSPARENCY_KEY,
|
||||||
|
):
|
||||||
|
if base_config[item] != config[item]:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Config item '{item}' must be the same for all LVGL instances"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def final_validation(configs):
|
||||||
|
multi_conf_validate(configs)
|
||||||
global_config = full_config.get()
|
global_config = full_config.get()
|
||||||
for display_id in config[df.CONF_DISPLAYS]:
|
for config in configs:
|
||||||
path = global_config.get_path_for_id(display_id)[:-1]
|
if pages := config.get(CONF_PAGES):
|
||||||
display = global_config.get_config_for_path(path)
|
if all(p[df.CONF_SKIP] for p in pages):
|
||||||
if CONF_LAMBDA in display:
|
raise cv.Invalid("At least one page must not be skipped")
|
||||||
raise cv.Invalid("Using lambda: in display config not compatible with LVGL")
|
for display_id in config[df.CONF_DISPLAYS]:
|
||||||
if display[CONF_AUTO_CLEAR_ENABLED]:
|
path = global_config.get_path_for_id(display_id)[:-1]
|
||||||
raise cv.Invalid(
|
display = global_config.get_config_for_path(path)
|
||||||
"Using auto_clear_enabled: true in display config not compatible with LVGL"
|
if CONF_LAMBDA in display:
|
||||||
)
|
raise cv.Invalid(
|
||||||
buffer_frac = config[CONF_BUFFER_SIZE]
|
"Using lambda: in display config not compatible with LVGL"
|
||||||
if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config:
|
)
|
||||||
LOGGER.warning("buffer_size: may need to be reduced without PSRAM")
|
if display[CONF_AUTO_CLEAR_ENABLED]:
|
||||||
for image_id in lv_images_used:
|
raise cv.Invalid(
|
||||||
path = global_config.get_path_for_id(image_id)[:-1]
|
"Using auto_clear_enabled: true in display config not compatible with LVGL"
|
||||||
image_conf = global_config.get_config_for_path(path)
|
)
|
||||||
if image_conf[CONF_TYPE] in ("RGBA", "RGB24"):
|
buffer_frac = config[CONF_BUFFER_SIZE]
|
||||||
raise cv.Invalid(
|
if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config:
|
||||||
"Using RGBA or RGB24 in image config not compatible with LVGL", path
|
LOGGER.warning("buffer_size: may need to be reduced without PSRAM")
|
||||||
)
|
for image_id in lv_images_used:
|
||||||
for w in focused_widgets:
|
path = global_config.get_path_for_id(image_id)[:-1]
|
||||||
path = global_config.get_path_for_id(w)
|
image_conf = global_config.get_config_for_path(path)
|
||||||
widget_conf = global_config.get_config_for_path(path[:-1])
|
if image_conf[CONF_TYPE] in ("RGBA", "RGB24"):
|
||||||
if df.CONF_ADJUSTABLE in widget_conf and not widget_conf[df.CONF_ADJUSTABLE]:
|
raise cv.Invalid(
|
||||||
raise cv.Invalid(
|
"Using RGBA or RGB24 in image config not compatible with LVGL", path
|
||||||
"A non adjustable arc may not be focused",
|
)
|
||||||
path,
|
for w in focused_widgets:
|
||||||
)
|
path = global_config.get_path_for_id(w)
|
||||||
|
widget_conf = global_config.get_config_for_path(path[:-1])
|
||||||
|
if (
|
||||||
|
df.CONF_ADJUSTABLE in widget_conf
|
||||||
|
and not widget_conf[df.CONF_ADJUSTABLE]
|
||||||
|
):
|
||||||
|
raise cv.Invalid(
|
||||||
|
"A non adjustable arc may not be focused",
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(configs):
|
||||||
|
config_0 = configs[0]
|
||||||
|
# Global configuration
|
||||||
cg.add_library("lvgl/lvgl", "8.4.0")
|
cg.add_library("lvgl/lvgl", "8.4.0")
|
||||||
cg.add_define("USE_LVGL")
|
cg.add_define("USE_LVGL")
|
||||||
# suppress default enabling of extra widgets
|
# suppress default enabling of extra widgets
|
||||||
|
@ -203,53 +232,33 @@ async def to_code(config):
|
||||||
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(
|
add_define(
|
||||||
"LV_LOG_LEVEL", f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config[df.CONF_LOG_LEVEL]]}"
|
"LV_LOG_LEVEL",
|
||||||
|
f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config_0[df.CONF_LOG_LEVEL]]}",
|
||||||
)
|
)
|
||||||
cg.add_define(
|
cg.add_define(
|
||||||
"LVGL_LOG_LEVEL",
|
"LVGL_LOG_LEVEL",
|
||||||
cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}"),
|
cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config_0[df.CONF_LOG_LEVEL]}"),
|
||||||
)
|
)
|
||||||
add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH])
|
add_define("LV_COLOR_DEPTH", config_0[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()}")
|
||||||
|
|
||||||
if config[df.CONF_COLOR_DEPTH] == 16:
|
if config_0[df.CONF_COLOR_DEPTH] == 16:
|
||||||
add_define(
|
add_define(
|
||||||
"LV_COLOR_16_SWAP",
|
"LV_COLOR_16_SWAP",
|
||||||
"1" if config[df.CONF_BYTE_ORDER] == "big_endian" else "0",
|
"1" if config_0[df.CONF_BYTE_ORDER] == "big_endian" else "0",
|
||||||
)
|
)
|
||||||
add_define(
|
add_define(
|
||||||
"LV_COLOR_CHROMA_KEY",
|
"LV_COLOR_CHROMA_KEY",
|
||||||
await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]),
|
await lvalid.lv_color.process(config_0[df.CONF_TRANSPARENCY_KEY]),
|
||||||
)
|
)
|
||||||
cg.add_build_flag("-Isrc")
|
cg.add_build_flag("-Isrc")
|
||||||
|
|
||||||
cg.add_global(lvgl_ns.using)
|
cg.add_global(lvgl_ns.using)
|
||||||
frac = config[CONF_BUFFER_SIZE]
|
|
||||||
if frac >= 0.75:
|
|
||||||
frac = 1
|
|
||||||
elif frac >= 0.375:
|
|
||||||
frac = 2
|
|
||||||
elif frac > 0.19:
|
|
||||||
frac = 4
|
|
||||||
else:
|
|
||||||
frac = 8
|
|
||||||
displays = [await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]]
|
|
||||||
lv_component = cg.new_Pvariable(
|
|
||||||
config[CONF_ID],
|
|
||||||
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)
|
||||||
cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font))
|
cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font))
|
||||||
default_font = config[df.CONF_DEFAULT_FONT]
|
default_font = config_0[df.CONF_DEFAULT_FONT]
|
||||||
if not lvalid.is_lv_font(default_font):
|
if not lvalid.is_lv_font(default_font):
|
||||||
add_define(
|
add_define(
|
||||||
"LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})"
|
"LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})"
|
||||||
|
@ -265,39 +274,71 @@ async def to_code(config):
|
||||||
add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT)
|
add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT)
|
||||||
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))
|
||||||
|
cg.add(lvgl_static.esphome_lvgl_init())
|
||||||
|
|
||||||
lv_scr_act = get_scr_act(lv_component)
|
for config in configs:
|
||||||
async with LvContext(lv_component):
|
frac = config[CONF_BUFFER_SIZE]
|
||||||
await touchscreens_to_code(lv_component, config)
|
if frac >= 0.75:
|
||||||
await encoders_to_code(lv_component, config)
|
frac = 1
|
||||||
await theme_to_code(config)
|
elif frac >= 0.375:
|
||||||
await styles_to_code(config)
|
frac = 2
|
||||||
await gradients_to_code(config)
|
elif frac > 0.19:
|
||||||
await set_obj_properties(lv_scr_act, config)
|
frac = 4
|
||||||
await add_widgets(lv_scr_act, config)
|
else:
|
||||||
await add_pages(lv_component, config)
|
frac = 8
|
||||||
await add_top_layer(lv_component, config)
|
displays = [
|
||||||
await msgboxes_to_code(lv_component, config)
|
await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]
|
||||||
await disp_update(lv_component.get_disp(), config)
|
]
|
||||||
|
lv_component = cg.new_Pvariable(
|
||||||
|
config[CONF_ID],
|
||||||
|
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)
|
||||||
|
|
||||||
|
lv_scr_act = get_scr_act(lv_component)
|
||||||
|
async with LvContext():
|
||||||
|
await touchscreens_to_code(lv_component, config)
|
||||||
|
await encoders_to_code(lv_component, config)
|
||||||
|
await theme_to_code(config)
|
||||||
|
await styles_to_code(config)
|
||||||
|
await gradients_to_code(config)
|
||||||
|
await set_obj_properties(lv_scr_act, config)
|
||||||
|
await add_widgets(lv_scr_act, config)
|
||||||
|
await add_pages(lv_component, config)
|
||||||
|
await add_top_layer(lv_component, config)
|
||||||
|
await msgboxes_to_code(lv_component, config)
|
||||||
|
await disp_update(lv_component.get_disp(), config)
|
||||||
# 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():
|
||||||
await generate_triggers(lv_component)
|
await generate_triggers()
|
||||||
await generate_page_triggers(lv_component, config)
|
for config in configs:
|
||||||
await initial_focus_to_code(config)
|
lv_component = await cg.get_variable(config[CONF_ID])
|
||||||
for conf in config.get(CONF_ON_IDLE, ()):
|
await generate_page_triggers(config)
|
||||||
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
|
await initial_focus_to_code(config)
|
||||||
idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
|
for conf in config.get(CONF_ON_IDLE, ()):
|
||||||
await build_automation(idle_trigger, [], conf)
|
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
|
||||||
for conf in config.get(df.CONF_ON_PAUSE, ()):
|
idle_trigger = cg.new_Pvariable(
|
||||||
pause_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, True)
|
conf[CONF_TRIGGER_ID], lv_component, templ
|
||||||
await build_automation(pause_trigger, [], conf)
|
)
|
||||||
for conf in config.get(df.CONF_ON_RESUME, ()):
|
await build_automation(idle_trigger, [], conf)
|
||||||
resume_trigger = cg.new_Pvariable(
|
for conf in config.get(df.CONF_ON_PAUSE, ()):
|
||||||
conf[CONF_TRIGGER_ID], lv_component, False
|
pause_trigger = cg.new_Pvariable(
|
||||||
)
|
conf[CONF_TRIGGER_ID], lv_component, True
|
||||||
await build_automation(resume_trigger, [], conf)
|
)
|
||||||
|
await build_automation(pause_trigger, [], conf)
|
||||||
|
for conf in config.get(df.CONF_ON_RESUME, ()):
|
||||||
|
resume_trigger = cg.new_Pvariable(
|
||||||
|
conf[CONF_TRIGGER_ID], lv_component, False
|
||||||
|
)
|
||||||
|
await build_automation(resume_trigger, [], conf)
|
||||||
|
|
||||||
|
# This must be done after all widgets are created
|
||||||
for comp in helpers.lvgl_components_required:
|
for comp in helpers.lvgl_components_required:
|
||||||
cg.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:
|
||||||
|
@ -312,7 +353,10 @@ async def to_code(config):
|
||||||
|
|
||||||
def display_schema(config):
|
def display_schema(config):
|
||||||
value = cv.ensure_list(cv.use_id(Display))(config)
|
value = cv.ensure_list(cv.use_id(Display))(config)
|
||||||
return value or [cv.use_id(Display)(config)]
|
value = value or [cv.use_id(Display)(config)]
|
||||||
|
if len(set(value)) != len(value):
|
||||||
|
raise cv.Invalid("Display IDs must be unique")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def add_hello_world(config):
|
def add_hello_world(config):
|
||||||
|
@ -324,7 +368,7 @@ def add_hello_world(config):
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = final_validation
|
FINAL_VALIDATE_SCHEMA = final_validation
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
LVGL_SCHEMA = (
|
||||||
cv.polling_component_schema("1s")
|
cv.polling_component_schema("1s")
|
||||||
.extend(obj_schema(obj_spec))
|
.extend(obj_schema(obj_spec))
|
||||||
.extend(
|
.extend(
|
||||||
|
@ -393,3 +437,16 @@ CONFIG_SCHEMA = (
|
||||||
.extend(DISP_BG_SCHEMA)
|
.extend(DISP_BG_SCHEMA)
|
||||||
.add_extra(add_hello_world)
|
.add_extra(add_hello_world)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def lvgl_config_schema(config):
|
||||||
|
"""
|
||||||
|
Can't use cv.ensure_list here because it converts an empty config to an empty list,
|
||||||
|
rather than a default config.
|
||||||
|
"""
|
||||||
|
if not config or isinstance(config, dict):
|
||||||
|
return [LVGL_SCHEMA(config)]
|
||||||
|
return cv.Schema([LVGL_SCHEMA])(config)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = lvgl_config_schema
|
||||||
|
|
|
@ -137,20 +137,18 @@ async def disp_update(disp, config: dict):
|
||||||
cv.maybe_simple_value(
|
cv.maybe_simple_value(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.use_id(lv_obj_t),
|
cv.Required(CONF_ID): cv.use_id(lv_obj_t),
|
||||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
|
||||||
},
|
},
|
||||||
key=CONF_ID,
|
key=CONF_ID,
|
||||||
),
|
),
|
||||||
cv.Schema(
|
LVGL_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):
|
||||||
lv_comp = await cg.get_variable(config[CONF_LVGL_ID])
|
if CONF_LVGL_ID in config:
|
||||||
widgets = await get_widgets(config) or [get_scr_act(lv_comp)]
|
lv_comp = await cg.get_variable(config[CONF_LVGL_ID])
|
||||||
|
widgets = [get_scr_act(lv_comp)]
|
||||||
|
else:
|
||||||
|
widgets = await get_widgets(config)
|
||||||
|
|
||||||
async def do_invalidate(widget: Widget):
|
async def do_invalidate(widget: Widget):
|
||||||
lv_obj.invalidate(widget.obj)
|
lv_obj.invalidate(widget.obj)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components.binary_sensor import (
|
from esphome.components.binary_sensor import (
|
||||||
BinarySensor,
|
BinarySensor,
|
||||||
binary_sensor_schema,
|
binary_sensor_schema,
|
||||||
|
@ -6,36 +5,30 @@ from esphome.components.binary_sensor import (
|
||||||
)
|
)
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET
|
from ..defines import CONF_WIDGET
|
||||||
from ..lvcode import EVENT_ARG, LambdaContext, LvContext
|
from ..lvcode import EVENT_ARG, LambdaContext, LvContext, lvgl_static
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LV_EVENT, lv_pseudo_button_t
|
from ..types import LV_EVENT, lv_pseudo_button_t
|
||||||
from ..widgets import Widget, get_widgets, wait_for_widgets
|
from ..widgets import Widget, get_widgets, wait_for_widgets
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = binary_sensor_schema(BinarySensor).extend(
|
||||||
binary_sensor_schema(BinarySensor)
|
{
|
||||||
.extend(LVGL_SCHEMA)
|
cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t),
|
||||||
.extend(
|
}
|
||||||
{
|
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
sensor = await new_binary_sensor(config)
|
sensor = await new_binary_sensor(config)
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
assert isinstance(widget, Widget)
|
assert isinstance(widget, Widget)
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
async with LambdaContext(EVENT_ARG) as pressed_ctx:
|
async with LambdaContext(EVENT_ARG) as pressed_ctx:
|
||||||
pressed_ctx.add(sensor.publish_state(widget.is_pressed()))
|
pressed_ctx.add(sensor.publish_state(widget.is_pressed()))
|
||||||
async with LvContext(paren) as ctx:
|
async with LvContext() as ctx:
|
||||||
ctx.add(sensor.publish_initial_state(widget.is_pressed()))
|
ctx.add(sensor.publish_initial_state(widget.is_pressed()))
|
||||||
ctx.add(
|
ctx.add(
|
||||||
paren.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
widget.obj,
|
widget.obj,
|
||||||
await pressed_ctx.get_lambda(),
|
await pressed_ctx.get_lambda(),
|
||||||
LV_EVENT.PRESSING,
|
LV_EVENT.PRESSING,
|
||||||
|
|
|
@ -4,9 +4,8 @@ from esphome.components.light import LightOutput
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_GAMMA_CORRECT, CONF_OUTPUT_ID
|
from esphome.const import CONF_GAMMA_CORRECT, CONF_OUTPUT_ID
|
||||||
|
|
||||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET
|
from ..defines import CONF_WIDGET
|
||||||
from ..lvcode import LvContext
|
from ..lvcode import LvContext
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LvType, lvgl_ns
|
from ..types import LvType, lvgl_ns
|
||||||
from ..widgets import get_widgets, wait_for_widgets
|
from ..widgets import get_widgets, wait_for_widgets
|
||||||
|
|
||||||
|
@ -18,16 +17,15 @@ CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend(
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(lv_led_t),
|
cv.Required(CONF_WIDGET): cv.use_id(lv_led_t),
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(LVLight),
|
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(LVLight),
|
||||||
}
|
}
|
||||||
).extend(LVGL_SCHEMA)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||||
await light.register_light(var, config)
|
await light.register_light(var, config)
|
||||||
|
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
async with LvContext(paren) as ctx:
|
async with LvContext() as ctx:
|
||||||
ctx.add(var.set_obj(widget.obj))
|
ctx.add(var.set_obj(widget.obj))
|
||||||
|
|
|
@ -178,10 +178,9 @@ class LvContext(LambdaContext):
|
||||||
|
|
||||||
added_lambda_count = 0
|
added_lambda_count = 0
|
||||||
|
|
||||||
def __init__(self, lv_component, args=None):
|
def __init__(self, args=None):
|
||||||
self.args = args or LVGL_COMP_ARG
|
self.args = args or LVGL_COMP_ARG
|
||||||
super().__init__(parameters=self.args)
|
super().__init__(parameters=self.args)
|
||||||
self.lv_component = lv_component
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -298,6 +297,7 @@ lv_expr = LvExpr("lv_")
|
||||||
lv_obj = MockLv("lv_obj_")
|
lv_obj = MockLv("lv_obj_")
|
||||||
# Operations on the LVGL component
|
# Operations on the LVGL component
|
||||||
lvgl_comp = MockObj(LVGL_COMP, "->")
|
lvgl_comp = MockObj(LVGL_COMP, "->")
|
||||||
|
lvgl_static = MockObj("LvglComponent", "::")
|
||||||
|
|
||||||
|
|
||||||
# equivalent to cg.add() for the current code context
|
# equivalent to cg.add() for the current code context
|
||||||
|
|
|
@ -98,19 +98,24 @@ void LvglComponent::set_paused(bool paused, bool show_snow) {
|
||||||
this->pause_callbacks_.call(paused);
|
this->pause_callbacks_.call(paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LvglComponent::esphome_lvgl_init() {
|
||||||
|
lv_init();
|
||||||
|
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());
|
||||||
|
}
|
||||||
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
|
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
|
||||||
lv_obj_add_event_cb(obj, callback, event, this);
|
lv_obj_add_event_cb(obj, callback, event, nullptr);
|
||||||
}
|
}
|
||||||
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
|
void LvglComponent::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 event2) {
|
||||||
this->add_event_cb(obj, callback, event1);
|
add_event_cb(obj, callback, event1);
|
||||||
this->add_event_cb(obj, callback, event2);
|
add_event_cb(obj, callback, event2);
|
||||||
}
|
}
|
||||||
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
|
void LvglComponent::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) {
|
lv_event_code_t event2, lv_event_code_t event3) {
|
||||||
this->add_event_cb(obj, callback, event1);
|
add_event_cb(obj, callback, event1);
|
||||||
this->add_event_cb(obj, callback, event2);
|
add_event_cb(obj, callback, event2);
|
||||||
this->add_event_cb(obj, callback, event3);
|
add_event_cb(obj, callback, event3);
|
||||||
}
|
}
|
||||||
void LvglComponent::add_page(LvPageType *page) {
|
void LvglComponent::add_page(LvPageType *page) {
|
||||||
this->pages_.push_back(page);
|
this->pages_.push_back(page);
|
||||||
|
@ -218,8 +223,10 @@ PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_LVGL_TOUCHSCREEN
|
#ifdef USE_LVGL_TOUCHSCREEN
|
||||||
LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) {
|
LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent) {
|
||||||
|
this->set_parent(parent);
|
||||||
lv_indev_drv_init(&this->drv_);
|
lv_indev_drv_init(&this->drv_);
|
||||||
|
this->drv_.disp = parent->get_disp();
|
||||||
this->drv_.long_press_repeat_time = long_press_repeat_time;
|
this->drv_.long_press_repeat_time = long_press_repeat_time;
|
||||||
this->drv_.long_press_time = long_press_time;
|
this->drv_.long_press_time = long_press_time;
|
||||||
this->drv_.type = LV_INDEV_TYPE_POINTER;
|
this->drv_.type = LV_INDEV_TYPE_POINTER;
|
||||||
|
@ -235,6 +242,7 @@ LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_r
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) {
|
void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) {
|
||||||
this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty();
|
this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty();
|
||||||
if (this->touch_pressed_)
|
if (this->touch_pressed_)
|
||||||
|
@ -405,9 +413,6 @@ LvglComponent::LvglComponent(std::vector<display::Display *> displays, float buf
|
||||||
buffer_frac_(buffer_frac),
|
buffer_frac_(buffer_frac),
|
||||||
full_refresh_(full_refresh),
|
full_refresh_(full_refresh),
|
||||||
resume_on_input_(resume_on_input) {
|
resume_on_input_(resume_on_input) {
|
||||||
lv_init();
|
|
||||||
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());
|
|
||||||
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;
|
||||||
|
|
|
@ -146,10 +146,14 @@ class LvglComponent : public PollingComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
|
/**
|
||||||
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
|
* Initialize the LVGL library and register custom events.
|
||||||
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);
|
static void esphome_lvgl_init();
|
||||||
|
static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
|
||||||
|
static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
|
||||||
|
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 add_page(LvPageType *page);
|
void add_page(LvPageType *page);
|
||||||
void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);
|
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_next_page(lv_scr_load_anim_t anim, uint32_t time);
|
||||||
|
@ -231,7 +235,7 @@ template<typename... Ts> class LvglCondition : public Condition<Ts...>, public P
|
||||||
#ifdef USE_LVGL_TOUCHSCREEN
|
#ifdef USE_LVGL_TOUCHSCREEN
|
||||||
class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
|
class LVTouchListener : public touchscreen::TouchListener, public Parented<LvglComponent> {
|
||||||
public:
|
public:
|
||||||
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time);
|
LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent);
|
||||||
void update(const touchscreen::TouchPoints_t &tpoints) override;
|
void update(const touchscreen::TouchPoints_t &tpoints) override;
|
||||||
void release() override {
|
void release() override {
|
||||||
touch_pressed_ = false;
|
touch_pressed_ = false;
|
||||||
|
|
|
@ -3,7 +3,7 @@ from esphome.components import number
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.cpp_generator import MockObj
|
from esphome.cpp_generator import MockObj
|
||||||
|
|
||||||
from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_UPDATE_ON_RELEASE, CONF_WIDGET
|
from ..defines import CONF_ANIMATED, CONF_UPDATE_ON_RELEASE, CONF_WIDGET
|
||||||
from ..lv_validation import animated
|
from ..lv_validation import animated
|
||||||
from ..lvcode import (
|
from ..lvcode import (
|
||||||
API_EVENT,
|
API_EVENT,
|
||||||
|
@ -13,28 +13,23 @@ from ..lvcode import (
|
||||||
LvContext,
|
LvContext,
|
||||||
lv,
|
lv,
|
||||||
lv_add,
|
lv_add,
|
||||||
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LV_EVENT, LvNumber, lvgl_ns
|
from ..types import LV_EVENT, LvNumber, lvgl_ns
|
||||||
from ..widgets import get_widgets, wait_for_widgets
|
from ..widgets import get_widgets, wait_for_widgets
|
||||||
|
|
||||||
LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number)
|
LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend(
|
||||||
number.number_schema(LVGLNumber)
|
{
|
||||||
.extend(LVGL_SCHEMA)
|
cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
|
||||||
.extend(
|
cv.Optional(CONF_ANIMATED, default=True): animated,
|
||||||
{
|
cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean,
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
|
}
|
||||||
cv.Optional(CONF_ANIMATED, default=True): animated,
|
|
||||||
cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
var = await number.new_number(
|
var = await number.new_number(
|
||||||
|
@ -58,10 +53,10 @@ async def to_code(config):
|
||||||
if not config[CONF_UPDATE_ON_RELEASE]
|
if not config[CONF_UPDATE_ON_RELEASE]
|
||||||
else LV_EVENT.RELEASED
|
else LV_EVENT.RELEASED
|
||||||
)
|
)
|
||||||
async with LvContext(paren):
|
async with LvContext():
|
||||||
lv_add(var.set_control_lambda(await control.get_lambda()))
|
lv_add(var.set_control_lambda(await control.get_lambda()))
|
||||||
lv_add(
|
lv_add(
|
||||||
paren.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code
|
widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,25 +1,19 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import select
|
from esphome.components import select
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_OPTIONS
|
from esphome.const import CONF_OPTIONS
|
||||||
|
|
||||||
from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET, literal
|
from ..defines import CONF_ANIMATED, CONF_WIDGET, literal
|
||||||
from ..lvcode import LvContext
|
from ..lvcode import LvContext
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LvSelect, lvgl_ns
|
from ..types import LvSelect, lvgl_ns
|
||||||
from ..widgets import get_widgets, wait_for_widgets
|
from ..widgets import get_widgets, wait_for_widgets
|
||||||
|
|
||||||
LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select)
|
LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = select.select_schema(LVGLSelect).extend(
|
||||||
select.select_schema(LVGLSelect)
|
{
|
||||||
.extend(LVGL_SCHEMA)
|
cv.Required(CONF_WIDGET): cv.use_id(LvSelect),
|
||||||
.extend(
|
cv.Optional(CONF_ANIMATED, default=False): cv.boolean,
|
||||||
{
|
}
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(LvSelect),
|
|
||||||
cv.Optional(CONF_ANIMATED, default=False): cv.boolean,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,9 +22,8 @@ async def to_code(config):
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
options = widget.config.get(CONF_OPTIONS, [])
|
options = widget.config.get(CONF_OPTIONS, [])
|
||||||
selector = await select.new_select(config, options=options)
|
selector = await select.new_select(config, options=options)
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
async with LvContext(paren) as ctx:
|
async with LvContext() as ctx:
|
||||||
ctx.add(
|
ctx.add(
|
||||||
selector.set_widget(
|
selector.set_widget(
|
||||||
widget.var,
|
widget.var,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components.sensor import Sensor, new_sensor, sensor_schema
|
from esphome.components.sensor import Sensor, new_sensor, sensor_schema
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET
|
from ..defines import CONF_WIDGET
|
||||||
from ..lvcode import (
|
from ..lvcode import (
|
||||||
API_EVENT,
|
API_EVENT,
|
||||||
EVENT_ARG,
|
EVENT_ARG,
|
||||||
|
@ -11,34 +10,29 @@ from ..lvcode import (
|
||||||
LambdaContext,
|
LambdaContext,
|
||||||
LvContext,
|
LvContext,
|
||||||
lv_add,
|
lv_add,
|
||||||
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LV_EVENT, LvNumber
|
from ..types import LV_EVENT, LvNumber
|
||||||
from ..widgets import Widget, get_widgets, wait_for_widgets
|
from ..widgets import Widget, get_widgets, wait_for_widgets
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = sensor_schema(Sensor).extend(
|
||||||
sensor_schema(Sensor)
|
{
|
||||||
.extend(LVGL_SCHEMA)
|
cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
|
||||||
.extend(
|
}
|
||||||
{
|
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
sensor = await new_sensor(config)
|
sensor = await new_sensor(config)
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
assert isinstance(widget, Widget)
|
assert isinstance(widget, Widget)
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
async with LambdaContext(EVENT_ARG) as lamb:
|
async with LambdaContext(EVENT_ARG) as lamb:
|
||||||
lv_add(sensor.publish_state(widget.get_value()))
|
lv_add(sensor.publish_state(widget.get_value()))
|
||||||
async with LvContext(paren, LVGL_COMP_ARG):
|
async with LvContext(LVGL_COMP_ARG):
|
||||||
lv_add(
|
lv_add(
|
||||||
paren.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
widget.obj,
|
widget.obj,
|
||||||
await lamb.get_lambda(),
|
await lamb.get_lambda(),
|
||||||
LV_EVENT.VALUE_CHANGED,
|
LV_EVENT.VALUE_CHANGED,
|
||||||
|
|
|
@ -3,7 +3,7 @@ from esphome.components.switch import Switch, new_switch, switch_schema
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.cpp_generator import MockObj
|
from esphome.cpp_generator import MockObj
|
||||||
|
|
||||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET, literal
|
from ..defines import CONF_WIDGET, literal
|
||||||
from ..lvcode import (
|
from ..lvcode import (
|
||||||
API_EVENT,
|
API_EVENT,
|
||||||
EVENT_ARG,
|
EVENT_ARG,
|
||||||
|
@ -13,26 +13,21 @@ from ..lvcode import (
|
||||||
LvContext,
|
LvContext,
|
||||||
lv,
|
lv,
|
||||||
lv_add,
|
lv_add,
|
||||||
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns
|
from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns
|
||||||
from ..widgets import get_widgets, wait_for_widgets
|
from ..widgets import get_widgets, wait_for_widgets
|
||||||
|
|
||||||
LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch)
|
LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch)
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = switch_schema(LVGLSwitch).extend(
|
||||||
switch_schema(LVGLSwitch)
|
{
|
||||||
.extend(LVGL_SCHEMA)
|
cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t),
|
||||||
.extend(
|
}
|
||||||
{
|
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
switch = await new_switch(config)
|
switch = await new_switch(config)
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
|
@ -45,10 +40,10 @@ async def to_code(config):
|
||||||
widget.clear_state(LV_STATE.CHECKED)
|
widget.clear_state(LV_STATE.CHECKED)
|
||||||
lv.event_send(widget.obj, API_EVENT, cg.nullptr)
|
lv.event_send(widget.obj, API_EVENT, cg.nullptr)
|
||||||
control.add(switch.publish_state(literal("v")))
|
control.add(switch.publish_state(literal("v")))
|
||||||
async with LvContext(paren) as ctx:
|
async with LvContext() as ctx:
|
||||||
lv_add(switch.set_control_lambda(await control.get_lambda()))
|
lv_add(switch.set_control_lambda(await control.get_lambda()))
|
||||||
ctx.add(
|
ctx.add(
|
||||||
paren.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
widget.obj,
|
widget.obj,
|
||||||
await checked_ctx.get_lambda(),
|
await checked_ctx.get_lambda(),
|
||||||
LV_EVENT.VALUE_CHANGED,
|
LV_EVENT.VALUE_CHANGED,
|
||||||
|
|
|
@ -3,7 +3,7 @@ from esphome.components import text
|
||||||
from esphome.components.text import new_text
|
from esphome.components.text import new_text
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET
|
from ..defines import CONF_WIDGET
|
||||||
from ..lvcode import (
|
from ..lvcode import (
|
||||||
API_EVENT,
|
API_EVENT,
|
||||||
EVENT_ARG,
|
EVENT_ARG,
|
||||||
|
@ -12,14 +12,14 @@ from ..lvcode import (
|
||||||
LvContext,
|
LvContext,
|
||||||
lv,
|
lv,
|
||||||
lv_add,
|
lv_add,
|
||||||
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from ..schemas import LVGL_SCHEMA
|
|
||||||
from ..types import LV_EVENT, LvText, lvgl_ns
|
from ..types import LV_EVENT, LvText, lvgl_ns
|
||||||
from ..widgets import get_widgets, wait_for_widgets
|
from ..widgets import get_widgets, wait_for_widgets
|
||||||
|
|
||||||
LVGLText = lvgl_ns.class_("LVGLText", text.Text)
|
LVGLText = lvgl_ns.class_("LVGLText", text.Text)
|
||||||
|
|
||||||
CONFIG_SCHEMA = text.TEXT_SCHEMA.extend(LVGL_SCHEMA).extend(
|
CONFIG_SCHEMA = text.TEXT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(LVGLText),
|
cv.GenerateID(): cv.declare_id(LVGLText),
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(LvText),
|
cv.Required(CONF_WIDGET): cv.use_id(LvText),
|
||||||
|
@ -29,7 +29,6 @@ CONFIG_SCHEMA = text.TEXT_SCHEMA.extend(LVGL_SCHEMA).extend(
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
textvar = await new_text(config)
|
textvar = await new_text(config)
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
|
@ -39,10 +38,10 @@ async def to_code(config):
|
||||||
control.add(textvar.publish_state(widget.get_value()))
|
control.add(textvar.publish_state(widget.get_value()))
|
||||||
async with LambdaContext(EVENT_ARG) as lamb:
|
async with LambdaContext(EVENT_ARG) as lamb:
|
||||||
lv_add(textvar.publish_state(widget.get_value()))
|
lv_add(textvar.publish_state(widget.get_value()))
|
||||||
async with LvContext(paren):
|
async with LvContext():
|
||||||
lv_add(textvar.set_control_lambda(await control.get_lambda()))
|
lv_add(textvar.set_control_lambda(await control.get_lambda()))
|
||||||
lv_add(
|
lv_add(
|
||||||
paren.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
widget.obj,
|
widget.obj,
|
||||||
await lamb.get_lambda(),
|
await lamb.get_lambda(),
|
||||||
LV_EVENT.VALUE_CHANGED,
|
LV_EVENT.VALUE_CHANGED,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components.text_sensor import (
|
from esphome.components.text_sensor import (
|
||||||
TextSensor,
|
TextSensor,
|
||||||
new_text_sensor,
|
new_text_sensor,
|
||||||
|
@ -6,34 +5,35 @@ from esphome.components.text_sensor import (
|
||||||
)
|
)
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
from ..defines import CONF_LVGL_ID, CONF_WIDGET
|
from ..defines import CONF_WIDGET
|
||||||
from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext
|
from ..lvcode import (
|
||||||
from ..schemas import LVGL_SCHEMA
|
API_EVENT,
|
||||||
|
EVENT_ARG,
|
||||||
|
UPDATE_EVENT,
|
||||||
|
LambdaContext,
|
||||||
|
LvContext,
|
||||||
|
lvgl_static,
|
||||||
|
)
|
||||||
from ..types import LV_EVENT, LvText
|
from ..types import LV_EVENT, LvText
|
||||||
from ..widgets import get_widgets, wait_for_widgets
|
from ..widgets import get_widgets, wait_for_widgets
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = text_sensor_schema(TextSensor).extend(
|
||||||
text_sensor_schema(TextSensor)
|
{
|
||||||
.extend(LVGL_SCHEMA)
|
cv.Required(CONF_WIDGET): cv.use_id(LvText),
|
||||||
.extend(
|
}
|
||||||
{
|
|
||||||
cv.Required(CONF_WIDGET): cv.use_id(LvText),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
sensor = await new_text_sensor(config)
|
sensor = await new_text_sensor(config)
|
||||||
paren = await cg.get_variable(config[CONF_LVGL_ID])
|
|
||||||
widget = await get_widgets(config, CONF_WIDGET)
|
widget = await get_widgets(config, CONF_WIDGET)
|
||||||
widget = widget[0]
|
widget = widget[0]
|
||||||
await wait_for_widgets()
|
await wait_for_widgets()
|
||||||
async with LambdaContext(EVENT_ARG) as pressed_ctx:
|
async with LambdaContext(EVENT_ARG) as pressed_ctx:
|
||||||
pressed_ctx.add(sensor.publish_state(widget.get_value()))
|
pressed_ctx.add(sensor.publish_state(widget.get_value()))
|
||||||
async with LvContext(paren) as ctx:
|
async with LvContext() as ctx:
|
||||||
ctx.add(
|
ctx.add(
|
||||||
paren.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
widget.obj,
|
widget.obj,
|
||||||
await pressed_ctx.get_lambda(),
|
await pressed_ctx.get_lambda(),
|
||||||
LV_EVENT.VALUE_CHANGED,
|
LV_EVENT.VALUE_CHANGED,
|
||||||
|
|
|
@ -33,13 +33,12 @@ def touchscreen_schema(config):
|
||||||
return [TOUCHSCREENS_CONFIG(config)]
|
return [TOUCHSCREENS_CONFIG(config)]
|
||||||
|
|
||||||
|
|
||||||
async def touchscreens_to_code(var, config):
|
async def touchscreens_to_code(lv_component, config):
|
||||||
for tconf in config[CONF_TOUCHSCREENS]:
|
for tconf in config[CONF_TOUCHSCREENS]:
|
||||||
lvgl_components_required.add(CONF_TOUCHSCREEN)
|
lvgl_components_required.add(CONF_TOUCHSCREEN)
|
||||||
touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID])
|
touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID])
|
||||||
lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds
|
lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds
|
||||||
lprt = tconf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds
|
lprt = tconf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds
|
||||||
listener = cg.new_Pvariable(tconf[CONF_ID], lpt, lprt)
|
listener = cg.new_Pvariable(tconf[CONF_ID], lpt, lprt, lv_component)
|
||||||
await cg.register_parented(listener, var)
|
|
||||||
lv.indev_drv_register(listener.get_drv())
|
lv.indev_drv_register(listener.get_drv())
|
||||||
cg.add(touchscreen.register_listener(listener))
|
cg.add(touchscreen.register_listener(listener))
|
||||||
|
|
|
@ -20,17 +20,16 @@ from .lvcode import (
|
||||||
lv,
|
lv,
|
||||||
lv_add,
|
lv_add,
|
||||||
lv_event_t_ptr,
|
lv_event_t_ptr,
|
||||||
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from .types import LV_EVENT
|
from .types import LV_EVENT
|
||||||
from .widgets import widget_map
|
from .widgets import widget_map
|
||||||
|
|
||||||
|
|
||||||
async def generate_triggers(lv_component):
|
async def generate_triggers():
|
||||||
"""
|
"""
|
||||||
Generate LVGL triggers for all defined widgets
|
Generate LVGL triggers for all defined widgets
|
||||||
Must be done after all widgets completed
|
Must be done after all widgets completed
|
||||||
:param lv_component: The parent component
|
|
||||||
:return:
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for w in widget_map.values():
|
for w in widget_map.values():
|
||||||
|
@ -43,11 +42,10 @@ async def generate_triggers(lv_component):
|
||||||
conf = conf[0]
|
conf = conf[0]
|
||||||
w.add_flag("LV_OBJ_FLAG_CLICKABLE")
|
w.add_flag("LV_OBJ_FLAG_CLICKABLE")
|
||||||
event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()])
|
event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()])
|
||||||
await add_trigger(conf, lv_component, w, event)
|
await add_trigger(conf, w, event)
|
||||||
for conf in w.config.get(CONF_ON_VALUE, ()):
|
for conf in w.config.get(CONF_ON_VALUE, ()):
|
||||||
await add_trigger(
|
await add_trigger(
|
||||||
conf,
|
conf,
|
||||||
lv_component,
|
|
||||||
w,
|
w,
|
||||||
LV_EVENT.VALUE_CHANGED,
|
LV_EVENT.VALUE_CHANGED,
|
||||||
API_EVENT,
|
API_EVENT,
|
||||||
|
@ -63,7 +61,7 @@ async def generate_triggers(lv_component):
|
||||||
lv.obj_align_to(w.obj, target, align, x, y)
|
lv.obj_align_to(w.obj, target, align, x, y)
|
||||||
|
|
||||||
|
|
||||||
async def add_trigger(conf, lv_component, w, *events):
|
async def add_trigger(conf, w, *events):
|
||||||
tid = conf[CONF_TRIGGER_ID]
|
tid = conf[CONF_TRIGGER_ID]
|
||||||
trigger = cg.new_Pvariable(tid)
|
trigger = cg.new_Pvariable(tid)
|
||||||
args = w.get_args() + [(lv_event_t_ptr, "event")]
|
args = w.get_args() + [(lv_event_t_ptr, "event")]
|
||||||
|
@ -72,4 +70,4 @@ async def add_trigger(conf, lv_component, w, *events):
|
||||||
async with LambdaContext(EVENT_ARG, where=tid) as context:
|
async with LambdaContext(EVENT_ARG, where=tid) as context:
|
||||||
with LvConditional(w.is_selected()):
|
with LvConditional(w.is_selected()):
|
||||||
lv_add(trigger.trigger(*value, literal("event")))
|
lv_add(trigger.trigger(*value, literal("event")))
|
||||||
lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events))
|
lv_add(lvgl_static.add_event_cb(w.obj, await context.get_lambda(), *events))
|
||||||
|
|
|
@ -20,6 +20,7 @@ from ..lvcode import (
|
||||||
add_line_marks,
|
add_line_marks,
|
||||||
lv_add,
|
lv_add,
|
||||||
lvgl_comp,
|
lvgl_comp,
|
||||||
|
lvgl_static,
|
||||||
)
|
)
|
||||||
from ..schemas import LVGL_SCHEMA
|
from ..schemas import LVGL_SCHEMA
|
||||||
from ..types import LvglAction, lv_page_t
|
from ..types import LvglAction, lv_page_t
|
||||||
|
@ -139,7 +140,7 @@ async def add_pages(lv_component, config):
|
||||||
await add_widgets(page, pconf)
|
await add_widgets(page, pconf)
|
||||||
|
|
||||||
|
|
||||||
async def generate_page_triggers(lv_component, config):
|
async def generate_page_triggers(config):
|
||||||
for pconf in config.get(CONF_PAGES, ()):
|
for pconf in config.get(CONF_PAGES, ()):
|
||||||
page = (await get_widgets(pconf))[0]
|
page = (await get_widgets(pconf))[0]
|
||||||
for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD):
|
for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD):
|
||||||
|
@ -149,7 +150,7 @@ async def generate_page_triggers(lv_component, config):
|
||||||
async with LambdaContext(EVENT_ARG, where=id) as context:
|
async with LambdaContext(EVENT_ARG, where=id) as context:
|
||||||
lv_add(trigger.trigger())
|
lv_add(trigger.trigger())
|
||||||
lv_add(
|
lv_add(
|
||||||
lv_component.add_event_cb(
|
lvgl_static.add_event_cb(
|
||||||
page.obj,
|
page.obj,
|
||||||
await context.get_lambda(),
|
await context.get_lambda(),
|
||||||
literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"),
|
literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"),
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
display:
|
display:
|
||||||
- platform: sdl
|
- platform: sdl
|
||||||
|
id: sdl0
|
||||||
|
auto_clear_enabled: false
|
||||||
|
dimensions:
|
||||||
|
width: 480
|
||||||
|
height: 320
|
||||||
|
- platform: sdl
|
||||||
|
id: sdl1
|
||||||
auto_clear_enabled: false
|
auto_clear_enabled: false
|
||||||
dimensions:
|
dimensions:
|
||||||
width: 480
|
width: 480
|
||||||
|
@ -7,5 +14,30 @@ display:
|
||||||
|
|
||||||
touchscreen:
|
touchscreen:
|
||||||
- platform: sdl
|
- platform: sdl
|
||||||
|
display: sdl0
|
||||||
|
sdl_id: sdl0
|
||||||
|
|
||||||
lvgl:
|
lvgl:
|
||||||
|
- id: lvgl_0
|
||||||
|
displays: sdl0
|
||||||
|
- id: lvgl_1
|
||||||
|
displays: sdl1
|
||||||
|
on_idle:
|
||||||
|
timeout: 8s
|
||||||
|
then:
|
||||||
|
if:
|
||||||
|
condition:
|
||||||
|
lvgl.is_idle:
|
||||||
|
lvgl_id: lvgl_1
|
||||||
|
timeout: 5s
|
||||||
|
then:
|
||||||
|
logger.log: Lvgl2 is idle
|
||||||
|
widgets:
|
||||||
|
- button:
|
||||||
|
align: center
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
text: Click ME
|
||||||
|
on_click:
|
||||||
|
logger.log: Clicked
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue