mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[lvgl] Bug fixes (#7300)
This commit is contained in:
		
				
					committed by
					
						 Jesse Hills
						Jesse Hills
					
				
			
			
				
	
			
			
			
						parent
						
							7464b440c0
						
					
				
				
					commit
					5c7d070307
				
			| @@ -6,8 +6,8 @@ Constants already defined in esphome.const are not duplicated here and must be i | |||||||
|  |  | ||||||
| from esphome import codegen as cg, config_validation as cv | from esphome import codegen as cg, config_validation as cv | ||||||
| from esphome.const import CONF_ITEMS | from esphome.const import CONF_ITEMS | ||||||
| from esphome.core import ID, Lambda | from esphome.core import Lambda | ||||||
| from esphome.cpp_generator import MockObj | from esphome.cpp_generator import LambdaExpression, MockObj | ||||||
| from esphome.cpp_types import uint32 | from esphome.cpp_types import uint32 | ||||||
| from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor | from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor | ||||||
|  |  | ||||||
| @@ -22,19 +22,22 @@ def literal(arg): | |||||||
|     return arg |     return arg | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def call_lambda(lamb: LambdaExpression): | ||||||
|  |     expr = lamb.content.strip() | ||||||
|  |     if expr.startswith("return") and expr.endswith(";"): | ||||||
|  |         return expr[7:][:-1] | ||||||
|  |     return f"{lamb}()" | ||||||
|  |  | ||||||
|  |  | ||||||
| class LValidator: | class LValidator: | ||||||
|     """ |     """ | ||||||
|     A validator for a particular type used in LVGL. Usable in configs as a validator, also |     A validator for a particular type used in LVGL. Usable in configs as a validator, also | ||||||
|     has `process()` to convert a value during code generation |     has `process()` to convert a value during code generation | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     def __init__( |     def __init__(self, validator, rtype, retmapper=None, requires=None): | ||||||
|         self, validator, rtype, idtype=None, idexpr=None, retmapper=None, requires=None |  | ||||||
|     ): |  | ||||||
|         self.validator = validator |         self.validator = validator | ||||||
|         self.rtype = rtype |         self.rtype = rtype | ||||||
|         self.idtype = idtype |  | ||||||
|         self.idexpr = idexpr |  | ||||||
|         self.retmapper = retmapper |         self.retmapper = retmapper | ||||||
|         self.requires = requires |         self.requires = requires | ||||||
|  |  | ||||||
| @@ -43,8 +46,6 @@ class LValidator: | |||||||
|             value = requires_component(self.requires)(value) |             value = requires_component(self.requires)(value) | ||||||
|         if isinstance(value, cv.Lambda): |         if isinstance(value, cv.Lambda): | ||||||
|             return cv.returning_lambda(value) |             return cv.returning_lambda(value) | ||||||
|         if self.idtype is not None and isinstance(value, ID): |  | ||||||
|             return cv.use_id(self.idtype)(value) |  | ||||||
|         return self.validator(value) |         return self.validator(value) | ||||||
|  |  | ||||||
|     async def process(self, value, args=()): |     async def process(self, value, args=()): | ||||||
| @@ -52,10 +53,10 @@ class LValidator: | |||||||
|             return None |             return None | ||||||
|         if isinstance(value, Lambda): |         if isinstance(value, Lambda): | ||||||
|             return cg.RawExpression( |             return cg.RawExpression( | ||||||
|                 f"{await cg.process_lambda(value, args, return_type=self.rtype)}()" |                 call_lambda( | ||||||
|  |                     await cg.process_lambda(value, args, return_type=self.rtype) | ||||||
|  |                 ) | ||||||
|             ) |             ) | ||||||
|         if self.idtype is not None and isinstance(value, ID): |  | ||||||
|             return cg.RawExpression(f"{value}->{self.idexpr}") |  | ||||||
|         if self.retmapper is not None: |         if self.retmapper is not None: | ||||||
|             return self.retmapper(value) |             return self.retmapper(value) | ||||||
|         return cg.safe_exp(value) |         return cg.safe_exp(value) | ||||||
| @@ -89,7 +90,7 @@ class LvConstant(LValidator): | |||||||
|             cv.ensure_list(self.one_of), uint32, retmapper=self.mapper |             cv.ensure_list(self.one_of), uint32, retmapper=self.mapper | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def mapper(self, value, args=()): |     def mapper(self, value): | ||||||
|         if not isinstance(value, list): |         if not isinstance(value, list): | ||||||
|             value = [value] |             value = [value] | ||||||
|         return literal( |         return literal( | ||||||
| @@ -103,7 +104,7 @@ class LvConstant(LValidator): | |||||||
|  |  | ||||||
|     def extend(self, *choices): |     def extend(self, *choices): | ||||||
|         """ |         """ | ||||||
|         Extend an LVCconstant with additional choices. |         Extend an LVconstant with additional choices. | ||||||
|         :param choices: The extra choices |         :param choices: The extra choices | ||||||
|         :return: A new LVConstant instance |         :return: A new LVConstant instance | ||||||
|         """ |         """ | ||||||
| @@ -431,6 +432,8 @@ CONF_ONE_LINE = "one_line" | |||||||
| CONF_ON_SELECT = "on_select" | CONF_ON_SELECT = "on_select" | ||||||
| CONF_ONE_CHECKED = "one_checked" | CONF_ONE_CHECKED = "one_checked" | ||||||
| CONF_NEXT = "next" | CONF_NEXT = "next" | ||||||
|  | CONF_PAD_ROW = "pad_row" | ||||||
|  | CONF_PAD_COLUMN = "pad_column" | ||||||
| CONF_PAGE = "page" | CONF_PAGE = "page" | ||||||
| CONF_PAGE_WRAP = "page_wrap" | CONF_PAGE_WRAP = "page_wrap" | ||||||
| CONF_PASSWORD_MODE = "password_mode" | CONF_PASSWORD_MODE = "password_mode" | ||||||
| @@ -462,6 +465,7 @@ CONF_SKIP = "skip" | |||||||
| CONF_SYMBOL = "symbol" | CONF_SYMBOL = "symbol" | ||||||
| CONF_TAB_ID = "tab_id" | CONF_TAB_ID = "tab_id" | ||||||
| CONF_TABS = "tabs" | CONF_TABS = "tabs" | ||||||
|  | CONF_TIME_FORMAT = "time_format" | ||||||
| CONF_TILE = "tile" | CONF_TILE = "tile" | ||||||
| CONF_TILE_ID = "tile_id" | CONF_TILE_ID = "tile_id" | ||||||
| CONF_TILES = "tiles" | CONF_TILES = "tiles" | ||||||
|   | |||||||
| @@ -1,17 +1,14 @@ | |||||||
| from typing import Union | from typing import Union | ||||||
|  |  | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| from esphome.components.binary_sensor import BinarySensor |  | ||||||
| from esphome.components.color import ColorStruct | from esphome.components.color import ColorStruct | ||||||
| from esphome.components.font import Font | from esphome.components.font import Font | ||||||
| from esphome.components.image import Image_ | from esphome.components.image import Image_ | ||||||
| from esphome.components.sensor import Sensor |  | ||||||
| from esphome.components.text_sensor import TextSensor |  | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_VALUE | from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_TIME, CONF_VALUE | ||||||
| from esphome.core import HexInt | from esphome.core import HexInt, Lambda | ||||||
| from esphome.cpp_generator import MockObj | from esphome.cpp_generator import MockObj | ||||||
| from esphome.cpp_types import uint32 | from esphome.cpp_types import ESPTime, uint32 | ||||||
| from esphome.helpers import cpp_string_escape | from esphome.helpers import cpp_string_escape | ||||||
| from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor | from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor | ||||||
|  |  | ||||||
| @@ -19,9 +16,11 @@ from . import types as ty | |||||||
| from .defines import ( | from .defines import ( | ||||||
|     CONF_END_VALUE, |     CONF_END_VALUE, | ||||||
|     CONF_START_VALUE, |     CONF_START_VALUE, | ||||||
|  |     CONF_TIME_FORMAT, | ||||||
|     LV_FONTS, |     LV_FONTS, | ||||||
|     LValidator, |     LValidator, | ||||||
|     LvConstant, |     LvConstant, | ||||||
|  |     call_lambda, | ||||||
|     literal, |     literal, | ||||||
| ) | ) | ||||||
| from .helpers import ( | from .helpers import ( | ||||||
| @@ -110,13 +109,13 @@ def angle(value): | |||||||
| def size_validator(value): | def size_validator(value): | ||||||
|     """A size in one axis - one of "size_content", a number (pixels) or a percentage""" |     """A size in one axis - one of "size_content", a number (pixels) or a percentage""" | ||||||
|     if value == SCHEMA_EXTRACT: |     if value == SCHEMA_EXTRACT: | ||||||
|         return ["size_content", "pixels", "..%"] |         return ["SIZE_CONTENT", "number of pixels", "percentage"] | ||||||
|     if isinstance(value, str) and value.lower().endswith("px"): |     if isinstance(value, str) and value.lower().endswith("px"): | ||||||
|         value = cv.int_(value[:-2]) |         value = cv.int_(value[:-2]) | ||||||
|     if isinstance(value, str) and not value.endswith("%"): |     if isinstance(value, str) and not value.endswith("%"): | ||||||
|         if value.upper() == "SIZE_CONTENT": |         if value.upper() == "SIZE_CONTENT": | ||||||
|             return "LV_SIZE_CONTENT" |             return "LV_SIZE_CONTENT" | ||||||
|         raise cv.Invalid("must be 'size_content', a pixel position or a percentage") |         raise cv.Invalid("must be 'size_content', a percentage or an integer (pixels)") | ||||||
|     if isinstance(value, int): |     if isinstance(value, int): | ||||||
|         return cv.int_(value) |         return cv.int_(value) | ||||||
|     # Will throw an exception if not a percentage. |     # Will throw an exception if not a percentage. | ||||||
| @@ -125,6 +124,15 @@ def size_validator(value): | |||||||
|  |  | ||||||
| size = LValidator(size_validator, uint32, retmapper=literal) | size = LValidator(size_validator, uint32, retmapper=literal) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def pixels_validator(value): | ||||||
|  |     if isinstance(value, str) and value.lower().endswith("px"): | ||||||
|  |         return cv.int_(value[:-2]) | ||||||
|  |     return cv.int_(value) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | pixels = LValidator(pixels_validator, uint32, retmapper=literal) | ||||||
|  |  | ||||||
| radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") | radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -167,9 +175,7 @@ lv_image = LValidator( | |||||||
|     retmapper=lambda x: lv_expr.img_from(MockObj(x)), |     retmapper=lambda x: lv_expr.img_from(MockObj(x)), | ||||||
|     requires="image", |     requires="image", | ||||||
| ) | ) | ||||||
| lv_bool = LValidator( | lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) | ||||||
|     cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def lv_pct(value: Union[int, float]): | def lv_pct(value: Union[int, float]): | ||||||
| @@ -185,42 +191,60 @@ def lvms_validator_(value): | |||||||
|  |  | ||||||
|  |  | ||||||
| lv_milliseconds = LValidator( | lv_milliseconds = LValidator( | ||||||
|     lvms_validator_, |     lvms_validator_, cg.int32, retmapper=lambda x: x.total_milliseconds | ||||||
|     cg.int32, |  | ||||||
|     retmapper=lambda x: x.total_milliseconds, |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TextValidator(LValidator): | class TextValidator(LValidator): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         super().__init__( |         super().__init__(cv.string, cg.std_string, lambda s: cg.safe_exp(f"{s}")) | ||||||
|             cv.string, |  | ||||||
|             cg.const_char_ptr, |  | ||||||
|             TextSensor, |  | ||||||
|             "get_state().c_str()", |  | ||||||
|             lambda s: cg.safe_exp(f"{s}"), |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     def __call__(self, value): |     def __call__(self, value): | ||||||
|         if isinstance(value, dict): |         if isinstance(value, dict) and CONF_FORMAT in value: | ||||||
|             return value |             return value | ||||||
|         return super().__call__(value) |         return super().__call__(value) | ||||||
|  |  | ||||||
|     async def process(self, value, args=()): |     async def process(self, value, args=()): | ||||||
|         if isinstance(value, dict): |         if isinstance(value, dict): | ||||||
|  |             if format_str := value.get(CONF_FORMAT): | ||||||
|                 args = [str(x) for x in value[CONF_ARGS]] |                 args = [str(x) for x in value[CONF_ARGS]] | ||||||
|                 arg_expr = cg.RawExpression(",".join(args)) |                 arg_expr = cg.RawExpression(",".join(args)) | ||||||
|             format_str = cpp_string_escape(value[CONF_FORMAT]) |                 format_str = cpp_string_escape(format_str) | ||||||
|                 return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") |                 return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") | ||||||
|  |             if time_format := value.get(CONF_TIME_FORMAT): | ||||||
|  |                 source = value[CONF_TIME] | ||||||
|  |                 if isinstance(source, Lambda): | ||||||
|  |                     time_format = cpp_string_escape(time_format) | ||||||
|  |                     return cg.RawExpression( | ||||||
|  |                         call_lambda( | ||||||
|  |                             await cg.process_lambda(source, args, return_type=ESPTime) | ||||||
|  |                         ) | ||||||
|  |                         + f".strftime({time_format}).c_str()" | ||||||
|  |                     ) | ||||||
|  |                 # must be an ID | ||||||
|  |                 source = await cg.get_variable(source) | ||||||
|  |                 return source.now().strftime(time_format).c_str() | ||||||
|  |         if isinstance(value, Lambda): | ||||||
|  |             value = call_lambda( | ||||||
|  |                 await cg.process_lambda(value, args, return_type=self.rtype) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             # Was the lambda call reduced to a string? | ||||||
|  |             if value.endswith("c_str()") or ( | ||||||
|  |                 value.endswith('"') and value.startswith('"') | ||||||
|  |             ): | ||||||
|  |                 pass | ||||||
|  |             else: | ||||||
|  |                 # Either a std::string or a lambda call returning that. We need const char* | ||||||
|  |                 value = f"({value}).c_str()" | ||||||
|  |             return cg.RawExpression(value) | ||||||
|         return await super().process(value, args) |         return await super().process(value, args) | ||||||
|  |  | ||||||
|  |  | ||||||
| lv_text = TextValidator() | lv_text = TextValidator() | ||||||
| lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()") | lv_float = LValidator(cv.float_, cg.float_) | ||||||
| lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()") | lv_int = LValidator(cv.int_, cg.int_) | ||||||
| lv_brightness = LValidator( | lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255)) | ||||||
|     cv.percentage, cg.float_, Sensor, "get_state()", retmapper=lambda x: int(x * 255) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def is_lv_font(font): | def is_lv_font(font): | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| from esphome import config_validation as cv | from esphome import config_validation as cv | ||||||
| from esphome.automation import Trigger, validate_automation | from esphome.automation import Trigger, validate_automation | ||||||
|  | from esphome.components.time import RealTimeClock | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     CONF_ARGS, |     CONF_ARGS, | ||||||
|     CONF_FORMAT, |     CONF_FORMAT, | ||||||
| @@ -8,6 +9,7 @@ from esphome.const import ( | |||||||
|     CONF_ON_VALUE, |     CONF_ON_VALUE, | ||||||
|     CONF_STATE, |     CONF_STATE, | ||||||
|     CONF_TEXT, |     CONF_TEXT, | ||||||
|  |     CONF_TIME, | ||||||
|     CONF_TRIGGER_ID, |     CONF_TRIGGER_ID, | ||||||
|     CONF_TYPE, |     CONF_TYPE, | ||||||
| ) | ) | ||||||
| @@ -15,6 +17,7 @@ from esphome.core import TimePeriod | |||||||
| from esphome.schema_extractors import SCHEMA_EXTRACT | from esphome.schema_extractors import SCHEMA_EXTRACT | ||||||
|  |  | ||||||
| from . import defines as df, lv_validation as lvalid | 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 .helpers import add_lv_use, requires_component, validate_printf | ||||||
| from .lv_validation import lv_color, lv_font, lv_image | from .lv_validation import lv_color, lv_font, lv_image | ||||||
| from .lvcode import LvglComponent | from .lvcode import LvglComponent | ||||||
| @@ -46,7 +49,13 @@ TEXT_SCHEMA = cv.Schema( | |||||||
|                 ), |                 ), | ||||||
|                 validate_printf, |                 validate_printf, | ||||||
|             ), |             ), | ||||||
|             lvalid.lv_text, |             cv.Schema( | ||||||
|  |                 { | ||||||
|  |                     cv.Required(CONF_TIME_FORMAT): cv.string, | ||||||
|  |                     cv.GenerateID(CONF_TIME): cv.templatable(cv.use_id(RealTimeClock)), | ||||||
|  |                 } | ||||||
|  |             ), | ||||||
|  |             cv.templatable(cv.string), | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
| @@ -116,15 +125,13 @@ STYLE_PROPS = { | |||||||
|     "opa_layered": lvalid.opacity, |     "opa_layered": lvalid.opacity, | ||||||
|     "outline_color": lvalid.lv_color, |     "outline_color": lvalid.lv_color, | ||||||
|     "outline_opa": lvalid.opacity, |     "outline_opa": lvalid.opacity, | ||||||
|     "outline_pad": lvalid.size, |     "outline_pad": lvalid.pixels, | ||||||
|     "outline_width": lvalid.size, |     "outline_width": lvalid.pixels, | ||||||
|     "pad_all": lvalid.size, |     "pad_all": lvalid.pixels, | ||||||
|     "pad_bottom": lvalid.size, |     "pad_bottom": lvalid.pixels, | ||||||
|     "pad_column": lvalid.size, |     "pad_left": lvalid.pixels, | ||||||
|     "pad_left": lvalid.size, |     "pad_right": lvalid.pixels, | ||||||
|     "pad_right": lvalid.size, |     "pad_top": lvalid.pixels, | ||||||
|     "pad_row": lvalid.size, |  | ||||||
|     "pad_top": lvalid.size, |  | ||||||
|     "shadow_color": lvalid.lv_color, |     "shadow_color": lvalid.lv_color, | ||||||
|     "shadow_ofs_x": cv.int_, |     "shadow_ofs_x": cv.int_, | ||||||
|     "shadow_ofs_y": cv.int_, |     "shadow_ofs_y": cv.int_, | ||||||
| @@ -304,6 +311,8 @@ LAYOUT_SCHEMA = { | |||||||
|                 cv.Required(df.CONF_GRID_COLUMNS): [grid_spec], |                 cv.Required(df.CONF_GRID_COLUMNS): [grid_spec], | ||||||
|                 cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments, |                 cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments, | ||||||
|                 cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments, |                 cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments, | ||||||
|  |                 cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, | ||||||
|  |                 cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, | ||||||
|             }, |             }, | ||||||
|             df.TYPE_FLEX: { |             df.TYPE_FLEX: { | ||||||
|                 cv.Optional( |                 cv.Optional( | ||||||
| @@ -312,6 +321,8 @@ LAYOUT_SCHEMA = { | |||||||
|                 cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, |                 cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, | ||||||
|                 cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments, |                 cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments, | ||||||
|                 cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, |                 cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, | ||||||
|  |                 cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, | ||||||
|  |                 cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|         lower=True, |         lower=True, | ||||||
| @@ -338,7 +349,6 @@ DISP_BG_SCHEMA = cv.Schema( | |||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| # A style schema that can include text | # A style schema that can include text | ||||||
| STYLED_TEXT_SCHEMA = cv.maybe_simple_value( | STYLED_TEXT_SCHEMA = cv.maybe_simple_value( | ||||||
|     STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT |     STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT | ||||||
|   | |||||||
| @@ -20,6 +20,8 @@ from ..defines import ( | |||||||
|     CONF_GRID_ROWS, |     CONF_GRID_ROWS, | ||||||
|     CONF_LAYOUT, |     CONF_LAYOUT, | ||||||
|     CONF_MAIN, |     CONF_MAIN, | ||||||
|  |     CONF_PAD_COLUMN, | ||||||
|  |     CONF_PAD_ROW, | ||||||
|     CONF_SCROLLBAR_MODE, |     CONF_SCROLLBAR_MODE, | ||||||
|     CONF_STYLES, |     CONF_STYLES, | ||||||
|     CONF_WIDGETS, |     CONF_WIDGETS, | ||||||
| @@ -29,6 +31,7 @@ from ..defines import ( | |||||||
|     TYPE_FLEX, |     TYPE_FLEX, | ||||||
|     TYPE_GRID, |     TYPE_GRID, | ||||||
|     LValidator, |     LValidator, | ||||||
|  |     call_lambda, | ||||||
|     join_enums, |     join_enums, | ||||||
|     literal, |     literal, | ||||||
| ) | ) | ||||||
| @@ -273,6 +276,10 @@ async def set_obj_properties(w: Widget, config): | |||||||
|         layout_type: str = layout[CONF_TYPE] |         layout_type: str = layout[CONF_TYPE] | ||||||
|         add_lv_use(layout_type) |         add_lv_use(layout_type) | ||||||
|         lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) |         lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) | ||||||
|  |         if (pad_row := layout.get(CONF_PAD_ROW)) is not None: | ||||||
|  |             w.set_style(CONF_PAD_ROW, pad_row, 0) | ||||||
|  |         if (pad_column := layout.get(CONF_PAD_COLUMN)) is not None: | ||||||
|  |             w.set_style(CONF_PAD_COLUMN, pad_column, 0) | ||||||
|         if layout_type == TYPE_GRID: |         if layout_type == TYPE_GRID: | ||||||
|             wid = config[CONF_ID] |             wid = config[CONF_ID] | ||||||
|             rows = [str(x) for x in layout[CONF_GRID_ROWS]] |             rows = [str(x) for x in layout[CONF_GRID_ROWS]] | ||||||
| @@ -316,8 +323,13 @@ async def set_obj_properties(w: Widget, config): | |||||||
|     flag_clr = set() |     flag_clr = set() | ||||||
|     flag_set = set() |     flag_set = set() | ||||||
|     props = parts[CONF_MAIN][CONF_DEFAULT] |     props = parts[CONF_MAIN][CONF_DEFAULT] | ||||||
|  |     lambs = {} | ||||||
|  |     flag_set = set() | ||||||
|  |     flag_clr = set() | ||||||
|     for prop, value in {k: v for k, v in props.items() if k in OBJ_FLAGS}.items(): |     for prop, value in {k: v for k, v in props.items() if k in OBJ_FLAGS}.items(): | ||||||
|         if value: |         if isinstance(value, cv.Lambda): | ||||||
|  |             lambs[prop] = value | ||||||
|  |         elif value: | ||||||
|             flag_set.add(prop) |             flag_set.add(prop) | ||||||
|         else: |         else: | ||||||
|             flag_clr.add(prop) |             flag_clr.add(prop) | ||||||
| @@ -327,6 +339,13 @@ async def set_obj_properties(w: Widget, config): | |||||||
|     if flag_clr: |     if flag_clr: | ||||||
|         clrs = join_enums(flag_clr, "LV_OBJ_FLAG_") |         clrs = join_enums(flag_clr, "LV_OBJ_FLAG_") | ||||||
|         w.clear_flag(clrs) |         w.clear_flag(clrs) | ||||||
|  |     for key, value in lambs.items(): | ||||||
|  |         lamb = await cg.process_lambda(value, [], return_type=cg.bool_) | ||||||
|  |         flag = f"LV_OBJ_FLAG_{key.upper()}" | ||||||
|  |         with LvConditional(call_lambda(lamb)) as cond: | ||||||
|  |             w.add_flag(flag) | ||||||
|  |             cond.else_() | ||||||
|  |             w.clear_flag(flag) | ||||||
|  |  | ||||||
|     if states := config.get(CONF_STATE): |     if states := config.get(CONF_STATE): | ||||||
|         adds = set() |         adds = set() | ||||||
| @@ -348,7 +367,7 @@ async def set_obj_properties(w: Widget, config): | |||||||
|         for key, value in lambs.items(): |         for key, value in lambs.items(): | ||||||
|             lamb = await cg.process_lambda(value, [], return_type=cg.bool_) |             lamb = await cg.process_lambda(value, [], return_type=cg.bool_) | ||||||
|             state = f"LV_STATE_{key.upper()}" |             state = f"LV_STATE_{key.upper()}" | ||||||
|             with LvConditional(f"{lamb}()") as cond: |             with LvConditional(call_lambda(lamb)) as cond: | ||||||
|                 w.add_state(state) |                 w.add_state(state) | ||||||
|                 cond.else_() |                 cond.else_() | ||||||
|                 w.clear_state(state) |                 w.clear_state(state) | ||||||
|   | |||||||
| @@ -127,3 +127,11 @@ binary_sensor: | |||||||
|   - platform: lvgl |   - platform: lvgl | ||||||
|     name: LVGL checkbox |     name: LVGL checkbox | ||||||
|     widget: checkbox_id |     widget: checkbox_id | ||||||
|  |  | ||||||
|  | wifi: | ||||||
|  |   ssid: SSID | ||||||
|  |   password: PASSWORD123 | ||||||
|  |  | ||||||
|  | time: | ||||||
|  |   platform: sntp | ||||||
|  |   id: time_id | ||||||
|   | |||||||
| @@ -16,8 +16,6 @@ lvgl: | |||||||
|       border_width: 0 |       border_width: 0 | ||||||
|       radius: 0 |       radius: 0 | ||||||
|       pad_all: 0 |       pad_all: 0 | ||||||
|       pad_row: 0 |  | ||||||
|       pad_column: 0 |  | ||||||
|       border_color: 0x0077b3 |       border_color: 0x0077b3 | ||||||
|       text_color: 0xFFFFFF |       text_color: 0xFFFFFF | ||||||
|       width: 100% |       width: 100% | ||||||
| @@ -55,6 +53,13 @@ lvgl: | |||||||
|   pages: |   pages: | ||||||
|     - id: page1 |     - id: page1 | ||||||
|       skip: true |       skip: true | ||||||
|  |       layout: | ||||||
|  |         type: flex | ||||||
|  |         pad_row: 4 | ||||||
|  |         pad_column: 4px | ||||||
|  |         flex_align_main: center | ||||||
|  |         flex_align_cross: start | ||||||
|  |         flex_align_track: end | ||||||
|       widgets: |       widgets: | ||||||
|         - animimg: |         - animimg: | ||||||
|             height: 60 |             height: 60 | ||||||
| @@ -118,10 +123,8 @@ lvgl: | |||||||
|             outline_width: 10px |             outline_width: 10px | ||||||
|             pad_all: 10px |             pad_all: 10px | ||||||
|             pad_bottom: 10px |             pad_bottom: 10px | ||||||
|             pad_column: 10px |  | ||||||
|             pad_left: 10px |             pad_left: 10px | ||||||
|             pad_right: 10px |             pad_right: 10px | ||||||
|             pad_row: 10px |  | ||||||
|             pad_top: 10px |             pad_top: 10px | ||||||
|             shadow_color: light_blue |             shadow_color: light_blue | ||||||
|             shadow_ofs_x: 5 |             shadow_ofs_x: 5 | ||||||
| @@ -221,10 +224,47 @@ lvgl: | |||||||
|               - label: |               - label: | ||||||
|                   text: Button |                   text: Button | ||||||
|             on_click: |             on_click: | ||||||
|               lvgl.label.update: |               - lvgl.label.update: | ||||||
|                   id: hello_label |                   id: hello_label | ||||||
|                   bg_color: 0x123456 |                   bg_color: 0x123456 | ||||||
|                   text: clicked |                   text: clicked | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: !lambda return "hello world"; | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: !lambda |- | ||||||
|  |                         ESP_LOGD("label", "multi-line lambda"); | ||||||
|  |                         return "hello world"; | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: !lambda 'return str_sprintf("Hello space");' | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: | ||||||
|  |                     format: "sprintf format %s" | ||||||
|  |                     args: ['x ? "checked" : "unchecked"'] | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: | ||||||
|  |                     time_format: "%c" | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: | ||||||
|  |                     time_format: "%c" | ||||||
|  |                     time: time_id | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: | ||||||
|  |                     time_format: "%c" | ||||||
|  |                     time: !lambda return id(time_id).now(); | ||||||
|  |               - lvgl.label.update: | ||||||
|  |                   id: hello_label | ||||||
|  |                   text: | ||||||
|  |                     time_format: "%c" | ||||||
|  |                     time: !lambda |- | ||||||
|  |                         ESP_LOGD("label", "multi-line lambda"); | ||||||
|  |                         return id(time_id).now(); | ||||||
|             on_value: |             on_value: | ||||||
|               logger.log: |               logger.log: | ||||||
|                 format: "state now %d" |                 format: "state now %d" | ||||||
| @@ -396,6 +436,8 @@ lvgl: | |||||||
|                   grid_row_align: end |                   grid_row_align: end | ||||||
|                   grid_rows: [25px, fr(1), content] |                   grid_rows: [25px, fr(1), content] | ||||||
|                   grid_columns: [40, fr(1), fr(1)] |                   grid_columns: [40, fr(1), fr(1)] | ||||||
|  |                   pad_row: 6px | ||||||
|  |                   pad_column: 0 | ||||||
|                 widgets: |                 widgets: | ||||||
|                   - image: |                   - image: | ||||||
|                       grid_cell_row_pos: 0 |                       grid_cell_row_pos: 0 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user