from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_ROW from ..automation import action_to_code from ..defines import ( CONF_ANIMATED, CONF_COLUMN, CONF_DIR, CONF_MAIN, CONF_SCROLLBAR, CONF_TILE_ID, CONF_TILES, TILE_DIRECTIONS, literal, ) from ..lv_validation import animated, lv_int, pixels_or_percent from ..lvcode import lv, lv_assign, lv_expr, lv_obj, lv_Pvariable from ..schemas import container_schema from ..types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties from .obj import obj_spec CONF_TILEVIEW = "tileview" lv_tile_t = LvType("lv_tileview_tile_t") lv_tileview_t = LvType( "lv_tileview_t", largs=[(lv_obj_t_ptr, "tile")], lvalue=lambda w: w.get_property("tile_active"), has_on_value=True, ) tile_spec = WidgetType("lv_tileview_tile_t", lv_tile_t, (CONF_MAIN,), {}) TILEVIEW_SCHEMA = cv.Schema( { cv.Required(CONF_TILES): cv.ensure_list( container_schema( obj_spec, { cv.Required(CONF_ROW): cv.positive_int, cv.Required(CONF_COLUMN): cv.positive_int, cv.GenerateID(): cv.declare_id(lv_tile_t), cv.Optional(CONF_DIR, default="ALL"): TILE_DIRECTIONS.several_of, }, ) ), } ) class TileviewType(WidgetType): def __init__(self): super().__init__( CONF_TILEVIEW, lv_tileview_t, (CONF_MAIN, CONF_SCROLLBAR), schema=TILEVIEW_SCHEMA, modify_schema={}, ) async def to_code(self, w: Widget, config: dict): tiles = config[CONF_TILES] for tile_conf in tiles: w_id = tile_conf[CONF_ID] tile_obj = lv_Pvariable(lv_obj_t, w_id) tile = Widget.create(w_id, tile_obj, tile_spec, tile_conf) dirs = await TILE_DIRECTIONS.process(tile_conf[CONF_DIR]) row_pos = tile_conf[CONF_ROW] col_pos = tile_conf[CONF_COLUMN] lv_assign( tile_obj, lv_expr.tileview_add_tile(w.obj, col_pos, row_pos, dirs), ) # Bugfix for LVGL 8.x lv_obj.set_pos( tile_obj, await pixels_or_percent.process(float(col_pos)), await pixels_or_percent.process(float(row_pos)), ) await set_obj_properties(tile, tile_conf) await add_widgets(tile, tile_conf) if tiles: # Set the first tile as active lv.tileview_set_tile_by_index( w.obj, tiles[0][CONF_COLUMN], tiles[0][CONF_ROW], literal("LV_ANIM_OFF") ) tileview_spec = TileviewType() def tile_select_validate(config): row = CONF_ROW in config column = CONF_COLUMN in config tile = CONF_TILE_ID in config if tile and (row or column) or not tile and not (row and column): raise cv.Invalid("Specify either a tile id, or both a row and a column") return config @automation.register_action( "lvgl.tileview.select", ObjUpdateAction, cv.Schema( { cv.Required(CONF_ID): cv.use_id(lv_tileview_t), cv.Optional(CONF_ANIMATED, default=False): animated, cv.Optional(CONF_ROW): lv_int, cv.Optional(CONF_COLUMN): lv_int, cv.Optional(CONF_TILE_ID): cv.use_id(lv_tile_t), }, ).add_extra(tile_select_validate), synchronous=True, ) async def tileview_select(config, action_id, template_arg, args): widgets = await get_widgets(config) async def do_select(w: Widget): if tile := config.get(CONF_TILE_ID): tile = await cg.get_variable(tile) lv.tileview_set_tile(w.obj, tile, literal(config[CONF_ANIMATED])) else: row = await lv_int.process(config[CONF_ROW]) column = await lv_int.process(config[CONF_COLUMN]) lv.tileview_set_tile_by_index( widgets[0].obj, column, row, literal(config[CONF_ANIMATED]) ) lv_obj.send_event(w.obj, LV_EVENT.VALUE_CHANGED, cg.nullptr) return await action_to_code(widgets, do_select, action_id, template_arg, args)