mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
[lvgl] Add lvgl.widget.focus action and related triggers. (#7315)
Some checks are pending
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 script/ci-custom (push) Blocked by required conditions
CI / Run pytest (macOS-latest, 3.11) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.10) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.9) (push) Blocked by required conditions
CI / Run pytest (windows-latest, 3.11) (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 / 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 / Component test ${{ matrix.file }} (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
Some checks are pending
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 script/ci-custom (push) Blocked by required conditions
CI / Run pytest (macOS-latest, 3.11) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.10) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Run pytest (ubuntu-latest, 3.9) (push) Blocked by required conditions
CI / Run pytest (windows-latest, 3.11) (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 / 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 / Component test ${{ matrix.file }} (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
This commit is contained in:
parent
458a8970b6
commit
d6df466237
13 changed files with 253 additions and 27 deletions
|
@ -21,8 +21,8 @@ from esphome.final_validate import full_config
|
|||
from esphome.helpers import write_file_if_changed
|
||||
|
||||
from . import defines as df, helpers, lv_validation as lvalid
|
||||
from .automation import disp_update, update_to_code
|
||||
from .defines import CONF_SKIP
|
||||
from .automation import disp_update, focused_widgets, update_to_code
|
||||
from .defines import CONF_ADJUSTABLE, CONF_SKIP
|
||||
from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
|
||||
from .lv_validation import lv_bool, lv_images_used
|
||||
from .lvcode import LvContext, LvglComponent
|
||||
|
@ -67,7 +67,7 @@ from .widgets.lv_bar import bar_spec
|
|||
from .widgets.meter import meter_spec
|
||||
from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code
|
||||
from .widgets.obj import obj_spec
|
||||
from .widgets.page import add_pages, page_spec
|
||||
from .widgets.page import add_pages, generate_page_triggers, page_spec
|
||||
from .widgets.roller import roller_spec
|
||||
from .widgets.slider import slider_spec
|
||||
from .widgets.spinbox import spinbox_spec
|
||||
|
@ -182,6 +182,14 @@ def final_validation(config):
|
|||
raise cv.Invalid(
|
||||
"Using RGBA or RGB24 in image config not compatible with LVGL", 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 CONF_ADJUSTABLE in widget_conf and not widget_conf[CONF_ADJUSTABLE]:
|
||||
raise cv.Invalid(
|
||||
"A non adjustable arc may not be focused",
|
||||
path,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
@ -271,6 +279,7 @@ async def to_code(config):
|
|||
Widget.set_completed()
|
||||
async with LvContext(lv_component):
|
||||
await generate_triggers(lv_component)
|
||||
await generate_page_triggers(lv_component, config)
|
||||
for conf in config.get(CONF_ON_IDLE, ()):
|
||||
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)
|
||||
idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ)
|
||||
|
|
|
@ -4,13 +4,15 @@ from typing import Callable
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_TIMEOUT
|
||||
from esphome.cpp_generator import RawExpression
|
||||
from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
|
||||
from esphome.cpp_generator import RawExpression, get_variable
|
||||
from esphome.cpp_types import nullptr
|
||||
|
||||
from .defines import (
|
||||
CONF_DISP_BG_COLOR,
|
||||
CONF_DISP_BG_IMAGE,
|
||||
CONF_EDITING,
|
||||
CONF_FREEZE,
|
||||
CONF_LVGL_ID,
|
||||
CONF_SHOW_SNOW,
|
||||
literal,
|
||||
|
@ -30,6 +32,7 @@ from .lvcode import (
|
|||
lv_expr,
|
||||
lv_obj,
|
||||
lvgl_comp,
|
||||
static_cast,
|
||||
)
|
||||
from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA
|
||||
from .types import (
|
||||
|
@ -38,7 +41,9 @@ from .types import (
|
|||
LvglCondition,
|
||||
ObjUpdateAction,
|
||||
lv_disp_t,
|
||||
lv_group_t,
|
||||
lv_obj_t,
|
||||
lv_pseudo_button_t,
|
||||
)
|
||||
from .widgets import (
|
||||
Widget,
|
||||
|
@ -48,6 +53,9 @@ from .widgets import (
|
|||
wait_for_widgets,
|
||||
)
|
||||
|
||||
# Record widgets that are used in a focused action here
|
||||
focused_widgets = set()
|
||||
|
||||
|
||||
async def action_to_code(
|
||||
widgets: list[Widget],
|
||||
|
@ -234,3 +242,72 @@ async def obj_show_to_code(config, action_id, template_arg, args):
|
|||
return await action_to_code(
|
||||
await get_widgets(config), do_show, action_id, template_arg, args
|
||||
)
|
||||
|
||||
|
||||
def focused_id(value):
|
||||
value = cv.use_id(lv_pseudo_button_t)(value)
|
||||
focused_widgets.add(value)
|
||||
return value
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"lvgl.widget.focus",
|
||||
ObjUpdateAction,
|
||||
cv.Any(
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Optional(CONF_GROUP): cv.use_id(lv_group_t),
|
||||
cv.Required(CONF_ACTION): cv.one_of(
|
||||
"MARK", "RESTORE", "NEXT", "PREVIOUS", upper=True
|
||||
),
|
||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||
cv.Optional(CONF_FREEZE, default=False): cv.boolean,
|
||||
},
|
||||
key=CONF_ACTION,
|
||||
),
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_ID): focused_id,
|
||||
cv.Optional(CONF_FREEZE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_EDITING, default=False): cv.boolean,
|
||||
},
|
||||
key=CONF_ID,
|
||||
),
|
||||
),
|
||||
)
|
||||
async def widget_focus(config, action_id, template_arg, args):
|
||||
widget = await get_widgets(config)
|
||||
if widget:
|
||||
widget = widget[0]
|
||||
group = static_cast(
|
||||
lv_group_t.operator("ptr"), lv_expr.obj_get_group(widget.obj)
|
||||
)
|
||||
elif group := config.get(CONF_GROUP):
|
||||
group = await get_variable(group)
|
||||
else:
|
||||
group = lv_expr.group_get_default()
|
||||
|
||||
async with LambdaContext(parameters=args, where=action_id) as context:
|
||||
if widget:
|
||||
lv.group_focus_freeze(group, False)
|
||||
lv.group_focus_obj(widget.obj)
|
||||
if config[CONF_EDITING]:
|
||||
lv.group_set_editing(group, True)
|
||||
else:
|
||||
action = config[CONF_ACTION]
|
||||
lv_comp = await get_variable(config[CONF_LVGL_ID])
|
||||
if action == "MARK":
|
||||
context.add(lv_comp.set_focus_mark(group))
|
||||
else:
|
||||
lv.group_focus_freeze(group, False)
|
||||
if action == "RESTORE":
|
||||
context.add(lv_comp.restore_focus_mark(group))
|
||||
elif action == "NEXT":
|
||||
lv.group_focus_next(group)
|
||||
else:
|
||||
lv.group_focus_prev(group)
|
||||
|
||||
if config[CONF_FREEZE]:
|
||||
lv.group_focus_freeze(group, True)
|
||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||
return var
|
||||
|
|
|
@ -148,6 +148,7 @@ LV_EVENT_MAP = {
|
|||
"DEFOCUS": "DEFOCUSED",
|
||||
"READY": "READY",
|
||||
"CANCEL": "CANCEL",
|
||||
"ALL_EVENTS": "ALL",
|
||||
}
|
||||
|
||||
LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP)
|
||||
|
@ -390,6 +391,7 @@ CONF_DEFAULT_FONT = "default_font"
|
|||
CONF_DEFAULT_GROUP = "default_group"
|
||||
CONF_DIR = "dir"
|
||||
CONF_DISPLAYS = "displays"
|
||||
CONF_EDITING = "editing"
|
||||
CONF_ENCODERS = "encoders"
|
||||
CONF_END_ANGLE = "end_angle"
|
||||
CONF_END_VALUE = "end_value"
|
||||
|
@ -401,6 +403,7 @@ CONF_FLEX_ALIGN_MAIN = "flex_align_main"
|
|||
CONF_FLEX_ALIGN_CROSS = "flex_align_cross"
|
||||
CONF_FLEX_ALIGN_TRACK = "flex_align_track"
|
||||
CONF_FLEX_GROW = "flex_grow"
|
||||
CONF_FREEZE = "freeze"
|
||||
CONF_FULL_REFRESH = "full_refresh"
|
||||
CONF_GRID_CELL_ROW_POS = "grid_cell_row_pos"
|
||||
CONF_GRID_CELL_COLUMN_POS = "grid_cell_column_pos"
|
||||
|
@ -428,9 +431,9 @@ CONF_MSGBOXES = "msgboxes"
|
|||
CONF_OBJ = "obj"
|
||||
CONF_OFFSET_X = "offset_x"
|
||||
CONF_OFFSET_Y = "offset_y"
|
||||
CONF_ONE_CHECKED = "one_checked"
|
||||
CONF_ONE_LINE = "one_line"
|
||||
CONF_ON_SELECT = "on_select"
|
||||
CONF_ONE_CHECKED = "one_checked"
|
||||
CONF_NEXT = "next"
|
||||
CONF_PAD_ROW = "pad_row"
|
||||
CONF_PAD_COLUMN = "pad_column"
|
||||
|
|
|
@ -28,7 +28,7 @@ LVGL_COMP = "lv_component" # used as a lambda argument in lvgl_comp()
|
|||
LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent)
|
||||
LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)]
|
||||
lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr")
|
||||
EVENT_ARG = [(lv_event_t_ptr, "ev")]
|
||||
EVENT_ARG = [(lv_event_t_ptr, "event")]
|
||||
# Two custom events; API_EVENT is fired when an entity is updated remotely by an API interaction;
|
||||
# UPDATE_EVENT is fired when an entity is programmatically updated locally.
|
||||
# VALUE_CHANGED is the event generated by LVGL when an entity's value changes through user interaction.
|
||||
|
@ -291,6 +291,10 @@ class LvExpr(MockLv):
|
|||
pass
|
||||
|
||||
|
||||
def static_cast(type, value):
|
||||
return literal(f"static_cast<{type}>({value})")
|
||||
|
||||
|
||||
# Top level mock for generic lv_ calls to be recorded
|
||||
lv = MockLv("lv_")
|
||||
# Just generate an expression
|
||||
|
|
|
@ -15,6 +15,60 @@ static void log_cb(const char *buf) {
|
|||
}
|
||||
#endif // LV_USE_LOG
|
||||
|
||||
static const char *const EVENT_NAMES[] = {
|
||||
"NONE",
|
||||
"PRESSED",
|
||||
"PRESSING",
|
||||
"PRESS_LOST",
|
||||
"SHORT_CLICKED",
|
||||
"LONG_PRESSED",
|
||||
"LONG_PRESSED_REPEAT",
|
||||
"CLICKED",
|
||||
"RELEASED",
|
||||
"SCROLL_BEGIN",
|
||||
"SCROLL_END",
|
||||
"SCROLL",
|
||||
"GESTURE",
|
||||
"KEY",
|
||||
"FOCUSED",
|
||||
"DEFOCUSED",
|
||||
"LEAVE",
|
||||
"HIT_TEST",
|
||||
"COVER_CHECK",
|
||||
"REFR_EXT_DRAW_SIZE",
|
||||
"DRAW_MAIN_BEGIN",
|
||||
"DRAW_MAIN",
|
||||
"DRAW_MAIN_END",
|
||||
"DRAW_POST_BEGIN",
|
||||
"DRAW_POST",
|
||||
"DRAW_POST_END",
|
||||
"DRAW_PART_BEGIN",
|
||||
"DRAW_PART_END",
|
||||
"VALUE_CHANGED",
|
||||
"INSERT",
|
||||
"REFRESH",
|
||||
"READY",
|
||||
"CANCEL",
|
||||
"DELETE",
|
||||
"CHILD_CHANGED",
|
||||
"CHILD_CREATED",
|
||||
"CHILD_DELETED",
|
||||
"SCREEN_UNLOAD_START",
|
||||
"SCREEN_LOAD_START",
|
||||
"SCREEN_LOADED",
|
||||
"SCREEN_UNLOADED",
|
||||
"SIZE_CHANGED",
|
||||
"STYLE_CHANGED",
|
||||
"LAYOUT_CHANGED",
|
||||
"GET_SELF_SIZE",
|
||||
};
|
||||
|
||||
std::string lv_event_code_name_for(uint8_t event_code) {
|
||||
if (event_code < sizeof(EVENT_NAMES) / sizeof(EVENT_NAMES[0])) {
|
||||
return EVENT_NAMES[event_code];
|
||||
}
|
||||
return str_sprintf("%2d", event_code);
|
||||
}
|
||||
static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
|
||||
// make sure all coordinates are even
|
||||
if (area->x1 & 1)
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace lvgl {
|
|||
|
||||
extern lv_event_code_t lv_api_event; // NOLINT
|
||||
extern lv_event_code_t lv_update_event; // NOLINT
|
||||
extern std::string lv_event_code_name_for(uint8_t event_code);
|
||||
extern bool lv_is_pre_initialise();
|
||||
#ifdef USE_LVGL_COLOR
|
||||
inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); }
|
||||
|
@ -143,6 +144,13 @@ class LvglComponent : public PollingComponent {
|
|||
void show_next_page(lv_scr_load_anim_t anim, uint32_t time);
|
||||
void show_prev_page(lv_scr_load_anim_t anim, uint32_t time);
|
||||
void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; }
|
||||
void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); }
|
||||
void restore_focus_mark(lv_group_t *group) {
|
||||
auto *mark = this->focus_marks_[group];
|
||||
if (mark != nullptr) {
|
||||
lv_group_focus_obj(mark);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_random_();
|
||||
|
@ -158,6 +166,7 @@ class LvglComponent : public PollingComponent {
|
|||
bool show_snow_{};
|
||||
lv_coord_t snow_line_{};
|
||||
bool page_wrap_{true};
|
||||
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_{};
|
||||
|
|
|
@ -20,7 +20,7 @@ from . import defines as df, lv_validation as lvalid
|
|||
from .defines import CONF_TIME_FORMAT
|
||||
from .helpers import add_lv_use, requires_component, validate_printf
|
||||
from .lv_validation import lv_color, lv_font, lv_image
|
||||
from .lvcode import LvglComponent
|
||||
from .lvcode import LvglComponent, lv_event_t_ptr
|
||||
from .types import (
|
||||
LVEncoderListener,
|
||||
LvType,
|
||||
|
@ -215,14 +215,12 @@ def automation_schema(typ: LvType):
|
|||
events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,)
|
||||
else:
|
||||
events = df.LV_EVENT_TRIGGERS
|
||||
if isinstance(typ, LvType):
|
||||
template = Trigger.template(typ.get_arg_type())
|
||||
else:
|
||||
template = Trigger.template()
|
||||
args = [typ.get_arg_type()] if isinstance(typ, LvType) else []
|
||||
args.append(lv_event_t_ptr)
|
||||
return {
|
||||
cv.Optional(event): validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(template),
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template(*args)),
|
||||
}
|
||||
)
|
||||
for event in events
|
||||
|
|
|
@ -19,6 +19,7 @@ from .lvcode import (
|
|||
LvConditional,
|
||||
lv,
|
||||
lv_add,
|
||||
lv_event_t_ptr,
|
||||
)
|
||||
from .types import LV_EVENT
|
||||
from .widgets import widget_map
|
||||
|
@ -65,10 +66,10 @@ async def generate_triggers(lv_component):
|
|||
async def add_trigger(conf, lv_component, w, *events):
|
||||
tid = conf[CONF_TRIGGER_ID]
|
||||
trigger = cg.new_Pvariable(tid)
|
||||
args = w.get_args()
|
||||
args = w.get_args() + [(lv_event_t_ptr, "event")]
|
||||
value = w.get_value()
|
||||
await automation.build_automation(trigger, args, conf)
|
||||
async with LambdaContext(EVENT_ARG, where=tid) as context:
|
||||
with LvConditional(w.is_selected()):
|
||||
lv_add(trigger.trigger(value))
|
||||
lv_add(trigger.trigger(value, literal("event")))
|
||||
lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events))
|
||||
|
|
|
@ -57,7 +57,7 @@ lv_group_t = cg.global_ns.struct("lv_group_t")
|
|||
LVTouchListener = lvgl_ns.class_("LVTouchListener")
|
||||
LVEncoderListener = lvgl_ns.class_("LVEncoderListener")
|
||||
lv_obj_t = LvType("lv_obj_t")
|
||||
lv_page_t = cg.global_ns.class_("LvPageType", LvCompound)
|
||||
lv_page_t = LvType("LvPageType", parents=(LvCompound,))
|
||||
lv_img_t = LvType("lv_img_t")
|
||||
|
||||
LV_EVENT = MockObj(base="LV_EVENT_", op="")
|
||||
|
|
|
@ -225,7 +225,7 @@ def get_widget_generator(wid):
|
|||
yield
|
||||
|
||||
|
||||
async def get_widget_(wid: Widget):
|
||||
async def get_widget_(wid):
|
||||
if obj := widget_map.get(wid):
|
||||
return obj
|
||||
return await FakeAwaitable(get_widget_generator(wid))
|
||||
|
@ -348,8 +348,6 @@ async def set_obj_properties(w: Widget, config):
|
|||
if group := config.get(CONF_GROUP):
|
||||
group = await cg.get_variable(group)
|
||||
lv.group_add_obj(group, w.obj)
|
||||
flag_clr = set()
|
||||
flag_set = set()
|
||||
props = parts[CONF_MAIN][CONF_DEFAULT]
|
||||
lambs = {}
|
||||
flag_set = set()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_GROUP,
|
||||
CONF_MAX_VALUE,
|
||||
CONF_MIN_VALUE,
|
||||
CONF_MODE,
|
||||
|
@ -20,7 +21,7 @@ from ..defines import (
|
|||
literal,
|
||||
)
|
||||
from ..lv_validation import angle, get_start_value, lv_float
|
||||
from ..lvcode import lv, lv_obj
|
||||
from ..lvcode import lv, lv_expr, lv_obj
|
||||
from ..types import LvNumber, NumberType
|
||||
from . import Widget
|
||||
|
||||
|
@ -69,6 +70,9 @@ class ArcType(NumberType):
|
|||
if config.get(CONF_ADJUSTABLE) is False:
|
||||
lv_obj.remove_style(w.obj, nullptr, literal("LV_PART_KNOB"))
|
||||
w.clear_flag("LV_OBJ_FLAG_CLICKABLE")
|
||||
elif CONF_GROUP not in config:
|
||||
# For some reason arc does not get automatically added to the default group
|
||||
lv.group_add_obj(lv_expr.group_get_default(), w.obj)
|
||||
|
||||
value = await get_start_value(config)
|
||||
if value is not None:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from esphome import automation, codegen as cg
|
||||
from esphome.automation import Trigger
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME
|
||||
from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME, CONF_TRIGGER_ID
|
||||
|
||||
from ..defines import (
|
||||
CONF_ANIMATION,
|
||||
|
@ -9,12 +10,39 @@ from ..defines import (
|
|||
CONF_PAGE_WRAP,
|
||||
CONF_SKIP,
|
||||
LV_ANIM,
|
||||
literal,
|
||||
)
|
||||
from ..lv_validation import lv_bool, lv_milliseconds
|
||||
from ..lvcode import LVGL_COMP_ARG, LambdaContext, add_line_marks, lv_add, lvgl_comp
|
||||
from ..lvcode import (
|
||||
EVENT_ARG,
|
||||
LVGL_COMP_ARG,
|
||||
LambdaContext,
|
||||
add_line_marks,
|
||||
lv_add,
|
||||
lvgl_comp,
|
||||
)
|
||||
from ..schemas import LVGL_SCHEMA
|
||||
from ..types import LvglAction, lv_page_t
|
||||
from . import Widget, WidgetType, add_widgets, set_obj_properties
|
||||
from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties
|
||||
|
||||
CONF_ON_LOAD = "on_load"
|
||||
CONF_ON_UNLOAD = "on_unload"
|
||||
|
||||
PAGE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_SKIP, default=False): lv_bool,
|
||||
cv.Optional(CONF_ON_LOAD): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template()),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_UNLOAD): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template()),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class PageType(WidgetType):
|
||||
|
@ -23,9 +51,8 @@ class PageType(WidgetType):
|
|||
CONF_PAGE,
|
||||
lv_page_t,
|
||||
(),
|
||||
{
|
||||
cv.Optional(CONF_SKIP, default=False): lv_bool,
|
||||
},
|
||||
PAGE_SCHEMA,
|
||||
modify_schema={},
|
||||
)
|
||||
|
||||
async def to_code(self, w: Widget, config: dict):
|
||||
|
@ -39,7 +66,6 @@ SHOW_SCHEMA = LVGL_SCHEMA.extend(
|
|||
}
|
||||
)
|
||||
|
||||
|
||||
page_spec = PageType()
|
||||
|
||||
|
||||
|
@ -111,3 +137,21 @@ async def add_pages(lv_component, config):
|
|||
await set_obj_properties(page, config)
|
||||
await set_obj_properties(page, pconf)
|
||||
await add_widgets(page, pconf)
|
||||
|
||||
|
||||
async def generate_page_triggers(lv_component, config):
|
||||
for pconf in config.get(CONF_PAGES, ()):
|
||||
page = (await get_widgets(pconf))[0]
|
||||
for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD):
|
||||
for loaded in pconf.get(ev, ()):
|
||||
trigger = cg.new_Pvariable(loaded[CONF_TRIGGER_ID])
|
||||
await automation.build_automation(trigger, [], loaded)
|
||||
async with LambdaContext(EVENT_ARG, where=id) as context:
|
||||
lv_add(trigger.trigger())
|
||||
lv_add(
|
||||
lv_component.add_event_cb(
|
||||
page.obj,
|
||||
await context.get_lambda(),
|
||||
literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"),
|
||||
)
|
||||
)
|
||||
|
|
|
@ -54,6 +54,17 @@ lvgl:
|
|||
long_press_time: 500ms
|
||||
pages:
|
||||
- id: page1
|
||||
on_load:
|
||||
- logger.log: page loaded
|
||||
- lvgl.widget.focus:
|
||||
action: restore
|
||||
on_unload:
|
||||
- logger.log: page unloaded
|
||||
- lvgl.widget.focus: mark
|
||||
on_all_events:
|
||||
logger.log:
|
||||
format: "Event %s"
|
||||
args: ['lv_event_code_name_for(event->code).c_str()']
|
||||
skip: true
|
||||
layout:
|
||||
type: flex
|
||||
|
@ -70,6 +81,10 @@ lvgl:
|
|||
repeat_count: 10
|
||||
duration: 1s
|
||||
auto_start: true
|
||||
on_all_events:
|
||||
logger.log:
|
||||
format: "Event %s"
|
||||
args: ['lv_event_code_name_for(event->code).c_str()']
|
||||
- label:
|
||||
id: hello_label
|
||||
text: Hello world
|
||||
|
@ -229,6 +244,16 @@ lvgl:
|
|||
- label:
|
||||
text: Button
|
||||
on_click:
|
||||
- lvgl.widget.focus: spin_up
|
||||
- lvgl.widget.focus: next
|
||||
- lvgl.widget.focus: previous
|
||||
- lvgl.widget.focus:
|
||||
action: previous
|
||||
freeze: true
|
||||
- lvgl.widget.focus:
|
||||
id: spin_up
|
||||
freeze: true
|
||||
editing: true
|
||||
- lvgl.label.update:
|
||||
id: hello_label
|
||||
bg_color: 0x123456
|
||||
|
|
Loading…
Reference in a new issue